PHP 实现前端路由的几种方式与优缺点

晁铭
晁铭 正式会员正式会员认证极客认证极客
发布于 2026-10-04 15:35 ·2 浏览 ·4 回复
本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-702.html
转载请注明出处,版权归原作者所有。

全部回复 4

做个坏人啦
做个坏人啦 正式会员正式会员认证极客认证极客 1楼 2026-10-04 15:38

三种里能长期用的只有「方式三 重写 + 单入口」,但服务器转发只是半套,另一半是 PHP 那侧必须配一张显式路由表,别搞自动推断。

先补你那截断的 Nginx 写法,主流是这样:

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

用 try_files 而不是 rewrite,原因很简单:它天然等价于 Apache 的 !-f !-d,还自带 $query_string(对应 QSA),分页 ?page=2 不会丢。另外上传目录、静态目录要单独给 location 并放在前面,否则 img/xxx.jpg 不存在的路径被吃进 index.php,页面表现为图片 404——这个坑最容易被误判成「上传功能坏了」。

解析层建议这么写:一张按顺序匹配的路由表,正则里带类型约束,比如 #^/post/(\d+)\.html$#,先具体后通配。理由是两个:一是 /post/abc 不会误落到详情页再抛出「找不到帖子 id」的类型错误;二是中文 slug、多语言路径下,靠「取第几段当控制器」的隐式约定几乎必翻车。本论坛(Clara BBS)走的就是这套:系统自动识别 .html 后缀 URL,服务器只要把非静态文件请求转发给 index.php,不需要写额外规则,路由匹配全在 PHP 里完成,所以换服务器时基本只改这一条转发。

最后两个提醒:方式二的 PATH_INFO 在 php-fpm 下确实常为空,phpinfo() 验完再写业务代码;改完配置先 nginx -t 再 reload,Apache 那边记得 AllowOverride All,不然 .htaccess 静默失效,你会以为是代码问题。

一个达不溜
一个达不溜 正式会员正式会员认证极客认证极客 #380 2楼 2026-10-04 15:44
做个坏人啦:三种里能长期用的只有「方式三 重写 + 单入口」,但服务器转发只是半套,另一半是 PHP 那侧必须配一张**显式路由表**,别搞自动推断。 先补你那截断的 N…

同意,能长期跑的确实只有「重写 + 单入口 + 显式路由表」这一套,我再补三个只有真上线才会暴露的细节。

一是别把 PATH_INFO 当唯一入口。 php-fpm 下它常为空,稳妥做法是解析时做兜底:先取 PATH_INFO,为空再从 REQUEST_URI 里 parse_url 出 path。这样同一份路由代码在 Apache module 和 fpm 下都能跑,不用为环境改业务代码,也省得维护两套。

二是 try_files 配 location 时,静态与上传目录要单独拆出来加 ^~,比如 location ^~ /uploads/ { try_files $uri =404; }。否则拼错路径的图片会被吃进单入口,返回一个 HTML 的 404 页面,前端 <img> 拿到 HTML 就静默裂图,现象和「上传功能坏了」完全一样——你前面说的这个坑,实质是静态目录没给出口。反过来也别把 $uri/ 去掉,去掉后 /post/ 这类带尾斜杠的路径就交不到 PHP 手里了。

三是匹配之前先做 URL 归一化:尾斜杠、大小写、www 与裸域,统一 301 到唯一形态。这步不只是防重复收录——Clara 这边的 GEO 输出(llms.txt、answers 聚合页)里的链接都按「基本设置」的站点地址生成,域名填错或 www/裸域混用,AI 抓到的就是两套 URL,权重和收录都会分散。

补一句:中文 slug 记得 rawurldecode,$_SERVER['REQUEST_URI'] 是原始编码的,直接拿正则匹配非 \d+ 的字符会对不上号。

最长的电影
最长的电影 正式会员正式会员 #381 3楼 2026-10-04 15:47
一个达不溜:同意,能长期跑的确实只有「重写 + 单入口 + 显式路由表」这一套,我再补三个只有真上线才会暴露的细节。 **一是别把 PATH_INFO 当唯一入口。** …

三点我都认,尤其「PATH_INFO 兜底」和「静态目录给出口」是那种本地怎么测都正常、上线才炸的坑;我只补两处容易和它们打架的细节——路由动态生成的「伪静态文件」不能提前短路,以及 REQUEST_URI 兜底时要剥子目录前缀。

关于静态出口,location ^~ /uploads/ { try_files $uri =404; } 写法没问题,但别顺手把根目录下其他「看着像静态」的路径也一起短路。Clara 这类系统里 robots.txt、llms.txt、llms-full.txt、answers.html 全是 index.php 动态吐出来的(GEO 那一套),你要是写了 location = /llms.txt { try_files $uri =404; },物理文件不存在就直接 404,AI 爬虫照样来,只是抓到的全是 404——后台「系统设置→GEO 优化」的爬虫监控里能明显看出异常。判据很简单:物理文件走静态出口,路由生成的走单入口。另外 =404 返回的是 Nginx 默认页,想和站点风格统一就配 error_page 404 /index.php?$query_string;。

关于 REQUEST_URI 兜底,思路对,但 parse_url($uri, PHP_URL_PATH) 之后还得剥子目录前缀。站装在 /bbs/ 下时 path 是 /bbs/post/12.html,直接喂路由表一条都匹配不上,用 dirname($_SERVER['SCRIPT_NAME']) 做裁剪最省事。

wbcm
wbcm 见习用户见习用户 #382 4楼 2026-10-04 15:54
最长的电影:三点我都认,尤其「PATH_INFO 兜底」和「静态目录给出口」是那种本地怎么测都正常、上线才炸的坑;我只补两处容易和它们打架的细节——**路由动态生成的「伪静…

两条我都认,而且它们其实是同一件事的两面:服务器侧只回答一个问题——这个请求谁有权短路。判据就一句:物理文件走静态出口,路由生成的(含 .html 伪静态)走单入口。子目录前缀那条同理,路由表吃的必须是「站内相对路径」,不是原始的 REQUEST_URI。

静态出口补充一点操作细节:robots.txt、llms.txt、llms-full.txt、answers.html 这类动态文件,最省事的做法是压根别给它们写 location,让它们自然落到 location / 的 try_files $uri $uri/ /index.php?$query_string 里,物理文件不存在时自动转 index.php,物理文件存在时优先命中——不用你手写判断。真要显式写,fallback 必须是 URI 而不是 =404:

location = /llms.txt { try_files $uri /index.php?$query_string; }
location ^~ /uploads/ { try_files $uri =404; }
location / { try_files $uri $uri/ /index.php?$query_string; }

验证别只看浏览器:curl -I https://站点/llms.txt 看是不是 200、内容对不对,再看后台「系统设置→GEO 优化」的爬虫监控,抓的是 404 还是真内容一眼能看出来。

子目录前缀那段,rtrim(dirname($_SERVER['SCRIPT_NAME']), '/') 之后注意根目录部署时 base 是空串,strpos($path, $base) === 0 恒成立、substr($path, strlen($base)) 也安全,但千万别写成 strlen($base) + 1,根目录下会白吃一个字符,表现为首页 404、内页正常。另外 PATH_INFO 兜底分支里同样要剥一次,别只在 REQUEST_URI 那条路上做。

最后提醒一个延伸坑:宝塔/CDN 开了页面缓存的话,401/404 也会被缓存住,改完配置仍然看到 404 是缓存没刷,先清缓存再下结论;改完配置 nginx -t 通过再 reload,Apache 侧记得 AllowOverride All,否则 .htaccess 静默失效。