本文目录导读:

- 核心:设置正确的缓存头(Expires / Cache-Control)
- 关键策略:版本化/指纹化文件名(Cache Busting)
- 进阶:合并与压缩(减少请求数 & 体积)
- 结构化策略:按需加载与延迟加载
- 服务器 & 基础设施
- PHP项目特有的坑与最佳实践
- 一个简单的实施路线图
针对PHP项目的静态文件(CSS、JS、图片、字体等)缓存优化,核心目标是 减少不必要的HTTP请求 和 利用浏览器缓存,以下是分步骤的优化策略,从简单到深入:
核心:设置正确的缓存头(Expires / Cache-Control)
这是最基础也是最有效的一步,告诉浏览器“这个文件在xx时间内不用再来问我了”。
-
Apache (在
.htaccess或httpd.conf中):<IfModule mod_expires.c> ExpiresActive On # CSS、JS、图片缓存1年 ExpiresByType text/css "access plus 1 year" ExpiresByType application/javascript "access plus 1 year" ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/png "access plus 1 year" ExpiresByType image/gif "access plus 1 year" ExpiresByType image/svg+xml "access plus 1 year" # 字体文件也缓存1年 ExpiresByType font/woff2 "access plus 1 year" ExpiresByType application/font-woff "access plus 1 year" # HTML(不要缓存太长时间,因为内容可能变) ExpiresByType text/html "access plus 0 seconds" </IfModule> -
Nginx (在
server或location块中):location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; # immutable 告诉浏览器:即使刷新页面,也别去验证这个文件,直接用缓存 } location / { # HTML:不缓存 expires -1; } -
PHP 代码中(作为后备或补充): 如果你的PHP文件直接输出静态内容(或作为代理),可以加头:
header('Cache-Control: public, max-age=31536000, immutable'); header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT');
关键策略:版本化/指纹化文件名(Cache Busting)
设置了长缓存后,当你更新了 style.css 文件,浏览器不会去下载,因为它还在用旧的缓存。必须通过修改URL来强制更新。
-
常见做法:
style.css?v=1.0.2(查询字符串方式)- 缺点: 某些代理/CDN可能忽略 后面的参数,或者缓存不了带参数的URL。
-
推荐做法: 文件名哈希化
style.a1b2c3d4.css-
优点: 文件内容变了,文件名就变了,浏览器完全当做一个新文件去请求。
-
实现方案(3种):
a. 构建工具(最推荐,适合中大型项目)
- Webpack / Vite / Laravel Mix: 自动根据文件内容计算哈希,输出类似
app.123456.css的文件。 - 在PHP模板中自动引入:
// Laravel Mix 示例 <link rel="stylesheet" href="{{ mix('css/app.css') }}">
b. 手动或简单脚本
- 的MD5值作为版本号(PHP动态生成,略影响性能,但集成简单)。
<?php $cssFile = '/path/to/style.css'; $version = md5_file($cssFile); // 或 substr(md5_file(...), 0, 8) ?> <link rel="stylesheet" href="/css/style.css?v=<?= $version ?>">
c. 后端框架集成
- Laravel:
elixir()或mix()函数。 - Symfony:
encodeFilename()或assets.version配置。 - WordPress:
wp_enqueue_style()+版本号参数,每次更新主题或插件时手动更新版本号。
- Webpack / Vite / Laravel Mix: 自动根据文件内容计算哈希,输出类似
-
进阶:合并与压缩(减少请求数 & 体积)
-
合并 CSS/JS:将多个小文件合并成一个(
all.css),减少HTTP连接数。- ⚠️ 注意:HTTP/2 多路复用后,合并的优势变小了,但小于10个文件时,合并对HTTP/1.1用户仍有意义,推荐保留一定数量的合并(比如3-5个核心文件),不要极端合并成一个巨大文件。
-
压缩:
- Gzip/Brotli:在服务器启用(Nginx:
gzip on;,Apache:mod_deflate)。强烈建议开启,体积可减少70%。 - Minify:删除CSS/JS中的空格、注释、缩短变量名。
- 工具:
terser(JS),cssnano/purifycss(CSS)。 - 注意:不要在线上动态Minify,一定要在构建阶段预先生成
.min.css/.min.js文件。
- 工具:
- Gzip/Brotli:在服务器启用(Nginx:
结构化策略:按需加载与延迟加载
- 仅加载需要的CSS/JS:不要在全局加载整个Bootstrap CSS,如果某个页面只用到了模态框,只加载
modal.css。 - CSS 关键渲染路径:首屏的CSS内联在HTML
<head>中(<style>...</style>),非关键的CSS异步加载,这样可以避免CSS阻塞渲染。 - JS 异步/延迟:对非关键的JS,加上
defer或async属性,避免阻塞HTML解析。<script src="app.js" defer></script>
服务器 & 基础设施
- CDN:将静态文件托管到CDN(如Cloudflare、AWS CloudFront、阿里云CDN),CDN节点离用户近,大大缩短加载时间。
- HTTP/2:允许浏览器在一个连接上并行请求多个静态文件,配合多文件拆分(而非合并)效果更好。
- 开启 Keep-Alive:允许HTTP连接复用,减少建立连接的消耗。
PHP项目特有的坑与最佳实践
- 不要用PHP处理静态文件请求:永远不要让PHP脚本读取
.css文件并返回,应该由Nginx/Apache直接处理静态文件,效率高得多。- ❌ 错误:
<link href="/serve-css.php?file=style.css"> - ✅ 正确:
<link href="/css/style.css">
- ❌ 错误:
- 框架视图缓存:如果框架(如Laravel Blade、Twig)在编译模板时,尽量使用生产环境的视图缓存,不要每次请求都编译。
- 头像/用户上传的图片:对这些文件设置合理的缓存(比如1天或1周),并使用哈希化文件名,推荐使用 专有域名+无Cookie的静态资源域名(
static.example.com),避免不必要的Cookie传输。
一个简单的实施路线图
- 立刻做:在Web服务器(Nginx/Apache)开启对所有静态文件的
Expires和Cache-Control头(缓存1年)。 - 必须做:实现文件名版本化(哈希或
?v=...),推荐用构建工具(Webpack/Gulp),小项目用md5_file()。 - 推荐做:开启Gzip压缩;使用CDN;将非关键CSS/JS异步加载。
- 高级:HTTP/2;关键CSS内联;图片WebP格式输出。
核心原则: 永远不要让你的用户(或浏览器)怀疑“这个文件是不是最新的”,只要文件内容变了,它的URL(文件名或版本号)就必须跟着变。