ThinkPHP 3.2 透過「空模組」與「空操作」機制,提供了優雅的 404 頁面處理方案。本文詳細說明如何建立 EmptyAction、配置 CommonAction 空操作、製作 404 模板、以及 Nginx/Apache 層面的配合配置。
/NotExistModule/Index) → 觸發 EmptyAction/Index/NotExistAction) → 觸發該模組的 _empty() 方法<?php
// Application/Lib/Action/EmptyAction.class.php
class EmptyAction extends CommonAction {
/**
* 空模組入口:訪問不存在的模組時觸發
*/
public function _empty() {
// 發送標準 404 狀態碼(關鍵:利於 SEO 與監控)
header('HTTP/1.0 404 Not Found');
header('Status: 404 Not Found');
// 禁用佈局(如有)
$this->layout(false);
// 顯示 404 模板
$this->display('Public:404');
}
}
<?php
// Application/Lib/Action/CommonAction.class.php
class CommonAction extends Action {
// ... 其他共用邏輯 ...
/**
* 空操作入口:訪問不存在的 Action 時觸發
* 透過 R() 重定向到空模組的 _empty 方法,統一處理邏輯
*/
public function _empty() {
// 方法一:內部重定向(推薦,保持 URL 不變)
R('Empty/_empty');
// 方法二:直接輸出(若不需複用邏輯)
// header('HTTP/1.0 404 Not Found');
// $this->display('Public:404');
}
}
<!-- Application/Tpl/Public/404.html -->
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>頁面不存在 - {$site_name|default='本站'}</title>
<meta name="robots" content="noindex, nofollow">
<style>
body { font-family: 'Microsoft JhengHei', sans-serif; background: #f8f9fa; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; }
.container { text-align: center; padding: 40px; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,.1); max-width: 400px; }
h1 { font-size: 96px; color: #dc3545; margin: 0 0 16px; line-height: 1; }
p { color: #6c757d; font-size: 18px; margin-bottom: 24px; }
a { display: inline-block; padding: 12px 28px; background: #007bff; color: white; border-radius: 4px; text-decoration: none; font-weight: 500; }
a:hover { background: #0056b3; }
.links { margin-top: 20px; display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
</style>
</head>
<body>
<div class="container">
<h1>404</h1>
<p>您訪問的頁面不存在或已被移除</p>
<a href="__ROOT__/">返回首頁</a>
<div class="links">
<a href="__ROOT__/sitemap">網站地圖</a>
<a href="__ROOT__/contact">聯繫我們</a>
<a href="javascript:history.back()">返回上一頁</a>
</div>
</div>
<script>
// 可選:記錄 404 URL 供分析
fetch('/api/log-404', { method: 'POST', body: JSON.stringify({ url: location.href, referrer: document.referrer }) }).catch(()=>{});
</script>
</body>
</html>
圖片、CSS、JS 等靜態檔案不存在時,不應進入 ThinkPHP 處理,由 Web Server 直接返回 404,節省資源:
server {
root /var/www/html;
index index.php index.html;
location / {
# 靜態檔案直接嘗試服務,不存在則 404(不進入 PHP)
try_files $uri $uri/ @thinkphp;
}
location @thinkphp {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
# 明確排除靜態資源
location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf|txt|woff2?)$ {
expires 30d;
access_log off;
try_files $uri =404; # 關鍵:不存在直接 404
}
}
<IfModule mod_rewrite.c>
RewriteEngine On
# 靜態檔案直接服務
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# 其他轉發到 index.php
RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>
# 靜態資源不存在直接 404
<FilesMatch "\.(jpg|jpeg|png|gif|ico|css|js|pdf|txt|woff2?)$">
ErrorDocument 404 "Not Found"
</FilesMatch>
<?php
// Application/Lib/Action/EmptyAction.class.php (補充)
public function _empty() {
header('HTTP/1.0 404 Not Found');
// 記錄 404 訪問(非同步寫入,不阻塞響應)
$logData = [
'url' => $_SERVER['REQUEST_URI'],
'referrer' => $_SERVER['HTTP_REFERER'] ?? '',
'ua' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'ip' => $this->getClientIp(),
'time' => date('Y-m-d H:i:s'),
];
// 寫入檔案或推送到 Redis 佇列
$logFile = RUNTIME_PATH . 'Logs/404_' . date('Ymd') . '.log';
@file_put_contents($logFile, json_encode($logData, JSON_UNESCAPED_UNICODE) . "\n", FILE_APPEND);
$this->display('Public:404');
}
private function getClientIp() {
foreach (['HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'] as $key) {
if (!empty($_SERVER[$key])) return $_SERVER[$key];
}
return 'unknown';
}
ThinkPHP 5.0+ 採用異常處理機制,配置更簡單:
<?php
// config/app.php (TP 5.x/6.x)
return [
// ...
'exception_tmpl' => APP_PATH . 'view/public/404.html', // 全域異常模板
'exception_handle' => '\\app\\common\\exception\\HttpException', // 自定義異常處理類
];
// app/common/exception/HttpException.php
namespace app\common\exception;
use think\exception\Handle;
use think\Response;
class HttpException extends Handle {
public function render(\Exception $e): Response {
if ($e instanceof \think\exception\HttpException && $e->getCode() == 404) {
return Response::create(view('public/404'), 'html', 404);
}
return parent::render($e);
}
}
/NonExistModule → 顯示 404 頁面、狀態碼 404/Index/NonExistAction → 顯示 404 頁面、狀態碼 404/static/missing.css → Nginx 直接 404、不進 PHPHTTP/1.0 404 Not Found 正確返回正確配置 404 頁面不僅能提升用戶體驗,更能避免搜尋引擎索引無效頁面,是專案上線前的必檢項目。