根本原因是nginx错误地将静态资源请求转发给php处理,导致高开销、低吞吐、缓存失控;应通过location规则拦截静态后缀并由nginx直出,配合immutable缓存头与cdn域名正确配置。

PHP 环境下静态资源加载慢,根本原因不是 PHP 本身,而是你让 Nginx 把 CSS/JS/图片这些本该直出的文件,错误地交给了 PHP 处理——这相当于用跑车拉白菜:高开销、低吞吐、缓存难控。
为什么不能用 PHP 脚本 serve 静态资源
常见错误是写个 serve-static.php 或在 ThinkPHP/Laravel 的路由里用 readfile() 输出文件。这种做法每请求一次就启动 PHP-FPM 进程、解析脚本、读磁盘、拼响应头,实测 QPS 不到 1200,延迟 40ms+。而 Nginx 原生服务同样文件,QPS 可达 25000,延迟压到 1ms 级别。
- PHP 处理静态资源时,
ETag和Last-Modified很难精准生成(尤其文件在 symlink 或容器挂载路径下) -
Cache-Control头由 PHP 脚本手动输出,容易遗漏、覆盖或与 Nginx 冲突 - CDN 回源时若收到 PHP 返回的响应,可能因缺少
public或immutable指令,拒绝缓存或缓存时间极短
Nginx location 块必须拦截静态后缀
确保所有静态请求不进 PHP-FPM,由 Nginx 直接响应。关键不是“有没有配置”,而是 location 是否真正生效、是否被其他规则覆盖。
- 把以下配置放在
server块内、root指令同级位置,不要嵌套在location /或location ~ \.php$里面 - 匹配顺序很重要:
location ~* \.(js|css|png|jpg|gif|woff2|ttf|eot|svg|webp)$必须出现在 PHP 处理规则之前 - 务必加
try_files $uri =404;,避免请求/logo.png/xxx这类非法路径时意外转发给 PHP - 如果用的是 phpEnv(如小皮面板),检查其自动生成的配置是否已含类似块;若已有,确认它没被
location /的proxy_pass或fastcgi_pass覆盖
Cache-Control 要带 immutable,不是只设 max-age
仅写 expires 1y; 或 add_header Cache-Control "max-age=31536000"; 是不够的。现代浏览器和 CDN 对带哈希指纹的静态资源(如 app.a1b2c3.js)支持 immutable 指令,意味着“只要 URL 不变,内容绝不会变”,可跳过协商缓存验证,彻底省掉一次条件请求。
- 正确写法:
add_header Cache-Control "public, max-age=31536000, immutable"; - 不要用
private——静态资源是公开的,CDN 和代理需要缓存 - 避免
no-cache:它强制每次发If-None-Match请求,对带哈希的文件纯属浪费 - 如果项目没做文件名哈希(比如还用
main.css),则改用max-age=3600+must-revalidate,并确保后端正确返回ETag
CDN 域名和 publicPath 不匹配是隐形杀手
即使 Nginx 缓存头全对,前端 HTML 里写的还是 /static/js/app.js,那用户请求仍打向源站,CDN 完全不生效。这不是 Nginx 的问题,而是构建链路断了。
- Webpack/Vite 项目必须设置
output.publicPath(Webpack)或base(Vite)为 CDN 域名,例如"https://static.example.com/" - ThinkPHP 模板中引用静态资源,要用
{:static('js/app.js')}这类函数封装,并在函数内部拼接 CDN 前缀,而不是硬编码/static/ - 检查浏览器开发者工具 Network 标签页:所有 JS/CSS 请求的域名是否确实是你的 CDN 域名?响应头里是否有
X-Cache-Status: HIT?没有就说明没走 CDN
最常被忽略的一点:Nginx 的 add_header 默认不作用于 404 响应——如果某个 .webp 图片实际不存在,Nginx 返回 404 时不会带 Cache-Control,某些 CDN 会按默认策略缓存这个 404 达数小时。真要严谨,得加 always 参数或单独配 location = /404.html 块。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











