根本原因是Web服务器未将多域名请求正确指向对应站点的public目录,导致静态资源直出失败而非PHP路由问题。

ThinkPHP 多域名下 public 目录被绕过导致静态资源 404
根本原因不是路由或配置写错了,而是 Web 服务器(Nginx/Apache)没把多域名请求正确指向对应站点的 public 入口目录。ThinkPHP 的静态资源(css、js、images)默认全放在 public 下,但多域名部署时,如果所有域名都指向项目根目录而非各自 public,就会直接 404。
常见错误现象:https://a.example.com/static/css/app.css 返回 404,但 https://b.example.com/index.php/static/css/app.css 能访问 —— 这说明 PHP 路由通了,但静态资源走的是 Web 服务器直出,没进 PHP。
- Nginx 配置里每个
server块的root必须明确指向对应域名的public目录,不能共用一个项目根目录 - Apache 的
DocumentRoot同理,不同VirtualHost要分别设为/path/to/app-a/public、/path/to/app-b/public - 别指望靠 ThinkPHP 的
__STATIC__或asset()函数“自动修正路径”——它们只生成 URL,不解决 Web 服务器找不到文件的问题
ThinkPHP 的 url_domain_deploy 开启后静态资源路径没变
url_domain_deploy 是用来开启多域名路由识别的开关,它只影响控制器和路由匹配逻辑,完全不干预静态资源的 URL 生成或 Web 服务行为。开了它,asset('css/app.css') 还是输出 /static/css/app.css,不会自动变成 https://a.example.com/static/css/app.css。
- 要让
asset()输出带完整域名的链接,得手动配app.url配置项,且必须按域名动态设置(比如在入口文件或中间件里判断$_SERVER['HTTP_HOST']后写入Config::set('app.url', 'https://' . $host)) - 更稳妥的做法是前端构建时注入域名变量,或用 CDN 域名统一前缀,避免 PHP 层硬编码
- 别把
url_domain_deploy = true当成“多域名资源自动适配”的开关,它只是让Route::domain()生效
多个子域名共享同一套静态资源但路径冲突
比如 a.example.com 和 b.example.com 都想用同一份 public/static/,但又不想重复部署两套文件。这时候容易踩坑:直接用符号链接或共享 root,结果因 ThinkPHP 的 public_path() 返回的是物理路径,而 Web 服务器对软链权限、跨目录限制处理不一致,导致部分资源 404 或 403。
立即学习“PHP免费学习笔记(深入)”;
- 推荐做法:用 Nginx 的
alias指令单独映射静态资源路径,例如:location ^~ /static/ { alias /var/www/shared-static/; },这样两个域名都能走同一物理目录,且不经过 PHP - 禁用 ThinkPHP 自动拼接的
__STATIC__,改用绝对路径或 CDN 地址,避免依赖public_path()的返回值 - 注意
alias和root的语义差异:alias替换匹配路径,root是拼接路径;写错会导致 404 或目录遍历风险
本地开发用 php think run 无法复现多域名静态资源问题
内置服务器(php think run)默认只绑定一个 host,不支持多 server 配置,也不会读取 Nginx/Apache 的 root 或 alias 规则。它把所有请求都交给 PHP 路由处理,连 /static/xxx.js 都会进 index.php,所以本地永远 200,上线就 404。
- 本地调试多域名必须模拟真实 Web 服务器环境,推荐用 Docker + Nginx 镜像,或至少用
php -S配合路由脚本(但仍有局限) - 上线前务必在测试环境用真实 Nginx/Apache 部署验证,重点检查浏览器开发者工具 Network 标签页里静态资源的 Status 和 Initiator
- 别信 “本地能跑,线上应该也没问题”——这个假设在多域名静态资源场景下几乎总是错的
最常被忽略的一点:Web 服务器配置里 try_files(Nginx)或 FallbackResource(Apache)是否覆盖了所有静态资源扩展名。漏掉 .woff2 或 .svg,就可能让字体或图标 404,而且错误日志里未必明显提示。



















