vercel上thinkphp伪静态总404,是因为其serverless环境不支持apache/nginx重写机制,且默认截断request_uri导致path_info为空;必须改用url_model=3(兼容模式)并配合vercel.json重写规则透传s参数。

不能直接用 Apache 或 Nginx 的 rewrite 规则,Vercel 不支持 .htaccess 也不执行 server 配置块。它靠 vercel.json 的重写规则驱动,且 ThinkPHP 的 PATHINFO 模式依赖 $_SERVER['PATH_INFO'] 或 $_SERVER['REQUEST_URI'],而 Vercel 默认不透传原始路径给 PHP 运行时(尤其是使用 Build Output API 的边缘函数场景)。
为什么 Vercel 上 ThinkPHP 伪静态总 404?
Vercel 的 PHP 支持基于 Serverless Functions(如 @vercel/php),它把请求映射为标准 CGI 环境,但会截断或标准化 REQUEST_URI —— 比如把 /user/profile/123 变成 /index.php,丢掉路径参数,导致 ThinkPHP 路由系统收不到 s=/user/profile/123 或 /user/profile/123 这类原始路径。
- Apache/Nginx 的
RewriteRule ^(.*)$ index.php/$1在 Vercel 完全无效,因为没 mod_rewrite - 即使你写了
if (!-e $request_filename) { ... },Vercel 根本不解析 nginx.conf - ThinkPHP 默认的
URL_MODEL=2(PATHINFO)在 Vercel 下大概率失效,$_SERVER['PATH_INFO']为空或不可靠
必须改用 URL_MODEL=3(兼容模式) + vercel.json 重写
ThinkPHP 5.1+ 支持 URL_MODEL=3,即「兼容模式」:将路由参数拼在 query string 里,形如 /index.php?s=/user/profile/123。这个结构能被 Vercel 正确保留并透传给 PHP。
- 在
config/app.php中设:'url_model' => 3(不是 2) - 确保入口文件是
public/index.php,且已启用路由:'url_route_on' => true - 所有链接生成必须走 ThinkPHP 的
url()函数,避免手写硬编码路径
vercel.json 里怎么写重写规则?
目标是把所有非静态资源请求(如 /user/profile/123)转给 public/index.php,并带上 s= 参数。注意:Vercel 的 rewrite 不支持正则捕获组变量(如 $1),得用 destination + query 拆解。
{
"rewrites": [
{
"source": "/(.*)",
"destination": "/public/index.php",
"query": {
"s": "/$1"
}
}
]
}
-
source: "/(.*)"匹配任意路径(包括根/),但会优先匹配真实存在的静态文件(如/css/app.css),所以要确保public/目录下有对应资源 -
destination必须指向实际 PHP 入口(/public/index.php),不能写index.php或相对路径 -
query.s是关键,ThinkPHP 会从$_GET['s']解析路由,而不是依赖 PATH_INFO - 不要加
has条件判断文件是否存在 —— Vercel 的文件存在性检查在边缘层,PHP 层无法感知,且会干扰重写逻辑
常见踩坑点:public 目录结构 & build 输出
Vercel 构建时默认把整个项目当 root,但 ThinkPHP 要求 Web 入口在 public/ 下。不处理会导致 index.php 找不到框架核心或配置。
- 在
vercel.json里加"builds"显式指定入口:"builds": [ { "src": "public/index.php", "use": "@vercel/php" } ] - 确保
public/下有完整的 ThinkPHP 引导逻辑,vendor/和thinkphp/应通过 Composer 安装在项目根,再由public/index.php正确加载 - 别把
.env放进 public 目录;Vercel 环境变量应通过面板设置,而非读取本地文件 - 如果用了子目录部署(如
/blog/xxx),vercel.json的source需带前缀:"source": "/blog/(.*)",且 ThinkPHP 配置中补上'url_domain_root' => '/blog'
最易被忽略的是:Vercel 的 PHP runtime 不自动设置 PATH_INFO,哪怕你强行用 URL_MODEL=2 并在 vercel.json 里尝试模拟,也极可能因环境差异失败 —— 直接切到 URL_MODEL=3 是唯一稳定路径。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











