FrankenPHP中配置一年强缓存的关键在于Caddyfile中为静态资源路径显式设置Cache-Control: public, max-age=31536000, immutable,并确保文件名带哈希、路径匹配优先级高于PHP路由,三者缺一不可。

FrankenPHP里配强缓存一年,关键在Caddyfile加Cache-Control
FrankenPHP本身不直接处理静态资源缓存逻辑,它把这事交给内嵌的Caddy引擎。所以配「一年缓存」不是改PHP配置,而是写对Caddyfile里的Cache-Control响应头。默认Caddy对静态文件(比如/static/、/assets/下)不会自动加长缓存,必须显式声明。
常见错误是只加max-age=31536000却漏掉immutable——这会导致浏览器在资源更新后仍可能复用旧缓存,尤其配合HTML内联引用时出问题。
- 确保静态资源路径明确,例如
/static/或/dist/,避免和PHP路由冲突 - 在Caddyfile对应路径块里写:
header Cache-Control "public, max-age=31536000, immutable" - 不要对
.php或/api/这类动态路径加这个头,否则页面会卡在旧版本 - 如果用了worker模式,确认Caddyfile里没被
php指令意外覆盖静态路径处理顺序
为什么必须配合文件名哈希,光设max-age会失效
设了max-age=31536000后,浏览器真的一年都不再发请求——哪怕你悄悄替换了app.js内容,用户还是拿到旧版。这就是「强缓存不校验内容」的本质。所以immutable存在的前提,是文件名本身带变化标识。
构建时生成带哈希的文件名,比如app.a1b2c3.js,上线后新HTML里引用的是app.d4e5f6.js,浏览器一看文件名不同,就自然跳过旧缓存,发起新请求。没这步,Cache-Control配得再狠也没用。
立即学习“PHP免费学习笔记(深入)”;
- Laravel Mix、Vite、Webpack都默认支持哈希输出,检查构建产物是否含哈希字符串
- 手动版本号(如
style.v2.css)也行,但必须保证每次内容变更都改版本号,不能只改注释 - HTML里引用必须同步更新,用模板变量或构建插件自动注入,别手写死
FrankenPHP下etag和last-modified要不要关
不需要关,也不建议关。FrankenPHP默认开启ETag(基于文件内容生成),但只要你对静态资源路径配了Cache-Control: immutable,浏览器根本不会发送If-None-Match请求——ETag压根没机会被用到。它只是个后备机制,不影响主流程。
真正要留意的是:别在同一个路径上混用协商缓存和强缓存。比如给/static/既加no-cache又加max-age,Caddy会按规则合并,结果不可控。
- 静态资源路径只配
Cache-Control,别额外加etag off或header -Etag - HTML页面路径(如
/或/*.html)才需要开ETag +no-cache,让每次刷新都校验 - 用
curl -I https://yoursite.com/static/app.js验证响应头,确认只有Cache-Control且无ETag字段被强制移除
容易被忽略的FrankenPHP特有坑:worker模式下静态文件路径匹配优先级
FrankenPHP的worker指令默认会接管所有PHP相关路径,但如果Caddyfile里静态路径规则写在php块之后,Caddy可能先匹配到PHP路由,把/static/app.js当成PHP脚本去执行——结果返回500或空白,而不是文件内容。
解决方法是把静态资源路径块写在php或frankenphp块之前,利用Caddy「从上到下匹配」的规则优先处理。
- 正确顺序示例:
handle /static/* { ... }→handle *.php { ... }→frankenphp { ... } - 用
handle_path比handle更安全,避免正则误匹配 - 本地测试时用
caddy validate检查配置语法,上线前务必caddy reload而非重启
实际生效的关键,永远是「文件名变」+「响应头对」+「路径不被PHP劫持」三者同时成立。少一个,缓存一年就是一句空话。



















