90%是服务器配置与路由未对齐:path_info解析错误、script_name值不匹配、url_route_must未开启三者踩一即全挂;apache需启用mod_rewrite、allowoverride all并设rewritebase,nginx需正确配置fastcgi_split_path_info与script_name,tp6必须设url_route_on和url_route_must为true。

ThinkPHP 伪静态配不起来,90% 不是框架问题,而是服务器配置和路由定义没对齐——尤其是 PATH_INFO 解析、SCRIPT_NAME 值、以及 url_route_must 开关这三处,踩一个就全挂。
Apache 下 .htaccess 规则总 500 或路由失效
常见现象是访问 /index.php/article/1 正常,但去掉 index.php 就 500 或跳首页;或者 favicon.ico、CSS 文件全 404。
- 确认
mod_rewrite已启用:sudo a2enmod rewrite && sudo systemctl restart apache2 -
.htaccess必须放在项目根目录(和index.php同级),且 Apache 虚拟主机配置中允许覆盖:AllowOverride All(不是None) - 子目录部署(如
https://a.com/blog/)必须加RewriteBase /blog/,否则%{REQUEST_FILENAME}判断永远为真 - 推荐用这个最小可用规则(兼容 TP5/TP6):
<ifmodule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] </ifmodule>
Nginx 下控制器 404 的真实原因
访问 /index.php 能打开,但 /article/1 报 404,基本锁定是 Nginx 没传 PATH_INFO 或 SCRIPT_NAME 错了。
- 检查 PHP-FPM 处理块是否含这两行:
fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_param PATH_INFO $fastcgi_path_info;
-
fastcgi_param SCRIPT_NAME必须严格匹配入口文件路径:项目在/public/index.php,就得写/public/index.php;在根目录就写/index.php - 别直接抄“通用规则”,二级目录部署时
location /会把/favicon.ico也重写进index.php,加个排除更稳:location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s=$1 last; } } location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control "public, immutable"; }
TP6 路由定义后 URL 还带 index.php
不是重写规则没生效,而是框架没走重写路径——它默认仍按 PATH_INFO 模式解析,但你 Nginx 没传过去,或 TP6 配置关了路由强制模式。
- 确保
config/app.php中开启并锁死路由:'url_route_on' => true, 'url_route_must' => true,
- TP6 默认不自动识别
/article/1这种无index.php的请求,必须靠 Nginx 重写到s=参数,再由框架解析。所以route.php里写的Route::get('article/<id>', 'index/article')</id>才能命中 - 别在路由里用
[:ext]后缀(如.html),Google 不索引带点号的 URL;后缀统一用'url_html_suffix' => ''空字符串 - 测试时用
curl -I https://yoursite.com/article/1看响应头是否含X-Powered-By: ThinkPHP和状态码 200,别只刷浏览器
多语言路由下伪静态容易漏掉的点
比如想支持 /zh/article/1 和 /en/article/1,光在路由里写 Route::group('zh', [...]) 不够,URL 解析链路上有三处要同步改。
-
config/app.php中语言前缀必须启用:'app_multi_module' => true, 'lang_switch_on' => true, 'lang_auto_detect' => false,
- 路由分组得显式绑定前缀:
Route::group('zh', function () { Route::get('article/<id>', 'index/article'); })->pattern(['id' => '\d+']); Route::group('en', function () { Route::get('article/<id>', 'index/article'); })->pattern(['id' => '\d+']);</id></id> - Nginx/Apache 重写规则不能拦掉语言前缀——上面所有示例规则都默认放行
/zh/xxx,但如果你用了RewriteCond %{REQUEST_URI} !^/zh/这类判断,就会把多语言请求提前拒掉 - SEO 友好起见,每个语言路由都要配独立
<link rel="alternate" hreflang="...">,框架不自动输出,得在模板里手写
最麻烦的其实是 SCRIPT_NAME 和实际入口路径不一致导致的路由错位,比如 Nginx 写了 /index.php 但项目实际入口是 /public/index.php,这时候 $_SERVER['REQUEST_URI'] 会被 TP6 多截一段,/article/1 就变成 /index.php/article/1,然后死循环重定向。这种问题看不到错误日志,只能靠 var_dump($_SERVER) 对比查。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











