Hyperf静态资源“重复加载”实为HTTP缓存策略缺失:Swoole默认不返回Cache-Control/ETag,浏览器无法协商缓存,导致每次200而非304;需显式启用StaticResourceHandler并配置document_root、enable_static_handler或中间件,同时监听public目录热更新。

Hyperf 常驻内存运行时,静态资源(如 CSS/JS 文件)不会“重复加载”——真正出问题的,是开发阶段热更新失效、生产环境缓存策略错配,或路由配置不当导致 public 目录被协程服务器绕过。
为什么你看到“重复加载”?其实是 HTTP 层误判
Hyperf 默认用 Swoole HTTP Server 托管静态资源,但它的行为和 Nginx 完全不同:不自动加 Cache-Control、不识别 If-None-Match、不走 ETag 协商。浏览器每次刷新都发完整 GET 请求,DevTools Network 面板显示 200(不是 304),看起来像“重复加载”,实则是服务端没告诉浏览器“可以缓存”。
- 检查响应头:如果没看到
Cache-Control: public, max-age=31536000或类似字段,那就是没配缓存 - 确认是否启用了
StaticResourceHandler:它只在config/autoload/server.php中显式启用才生效,不是默认开的 - 别把
public/目录扔给 PHP-FPM 处理:Hyperf 进程常驻,但若你反向代理把/static/转给 FPM,就会多一次进程启动开销,造成真·重复加载
StaticResourceHandler 配置必须显式声明
Hyperf 不像 Laravel 那样自动托管 public,必须手动注册静态资源处理器,否则所有静态请求都会落到 Router,最终 404 或被业务逻辑拦截。
- 在
config/autoload/server.php的settings下加:'document_root' => BASE_PATH . '/public' - 同时确保
'enable_static_handler' => true(Swoole 原生支持,轻量高效) - 如果要用 Hyperf 自带的
StaticResourceHandler(支持更细粒度控制),需在config/autoload/middlewares.php中注册:Hyperf\HttpServer\Middleware\StaticResourceMiddleware::class - 注意路径映射:该中间件默认只处理
/static/**,不是根路径;想托管public/css/app.css,得访问/static/css/app.css,或改配置prefix
开发环境热更新失效,不是加载问题,是文件监听漏了
Hyperf 的 watcher 默认不监听 public/ 目录——改了 JS/CSS,php bin/hyperf.php start 不会自动 reload,你以为“重复加载”,其实是旧文件一直被 serve。
- 修改
config/autoload/watcher.php,把public加进watch_composer或新增watch_dirs:BASE_PATH . '/public' - 别依赖浏览器硬刷新(Ctrl+F5):它强制跳过缓存,掩盖了服务端本应返回 304 的事实
- 验证是否生效:改一个 JS 文件后,看终端是否打印
[INFO] File "public/js/app.js" changed, reloading...
生产环境缓存头没设对,浏览器根本不敢复用
即使 Swoole 托管了静态文件,若没配合适的 Cache-Control 和 ETag,CDN 和浏览器都会反复拉取。Hyperf 不自动计算文件 hash,也不自动注入版本号,全靠你手动控制。
- 推荐做法:构建时生成带 hash 的文件名(如
app.a1b2c3.js),然后用StaticResourceHandler的cache_control配置设为public, max-age=31536000 - 避免用
max-age=0或no-cache:这等于告诉浏览器“每次都来问一遍”,毫无意义 - 别在中间件里用
response()->withHeader()给静态资源加缓存头:StaticResourceHandler 已接管响应,中间件无效
真正要盯住的,从来不是“Hyperf 是否重复加载”,而是“你的缓存策略有没有让浏览器相信它不用重拉”。常驻内存是优势,不是 bug;问题永远出在 HTTP 协议层的约定没对齐。


















