Hyperf 不能直接在 FrankenPHP 中运行,因其基于 Swoole/Swow 的常驻进程协程模型与 FrankenPHP 的 PHP 线程内嵌模型根本冲突;正确方式是 FrankenPHP 作反向代理,Hyperf 独立监听内部端口。

Hyperf 项目不能直接扔进 FrankenPHP 里跑——两者运行模型根本不同:Hyperf 是基于 Swoole 或 Swow 的常驻内存协程服务器,FrankenPHP 是基于 Caddy 的 PHP 线程内嵌模型。硬塞会导致 php_server 指令无法接管路由、bin/hyperf.php start 启动冲突、甚至 $_SERVER['REQUEST_URI'] 被 Caddy 重写后失真。
为什么不能直接用 php_server 处理 Hyperf 入口
FrankenPHP 的 php_server 指令只适用于传统 PHP-FPM 风格的“每个请求启动一次脚本”的模式(如 index.php),而 Hyperf 的 bin/hyperf.php start 是长期运行的守护进程,自带事件循环、协程调度和 HTTP 服务监听。两者在生命周期、信号处理、全局状态管理上完全不兼容。
-
php_server会尝试把每个请求当作独立 CGI 执行public/index.php,但 Hyperf 的入口文件不返回响应,而是注册服务并阻塞等待连接 - Caddy 的
php_server不传递$_SERVER['SERVER_PROTOCOL']、$_SERVER['REQUEST_METHOD']等关键字段给 Hyperf 的HttpServer,导致路由解析失败 - FrankenPHP 的线程池机制与 Swoole 的多进程+协程模型存在资源竞争,实测会出现
Segmentation fault或coroutine already closed
正确做法:用 FrankenPHP 做反向代理,Hyperf 自行监听端口
让 FrankenPHP 发挥它最擅长的事:HTTPS 终结、HTTP/3 支持、静态资源分发、安全头注入;Hyperf 则专注业务逻辑,用 hyperf/http-server 在内部端口(如 127.0.0.1:9501)提供 HTTP 服务。两者通过反向代理桥接。
- 确保 Hyperf 已安装
hyperf/http-server并配置为监听127.0.0.1:9501(非0.0.0.0,避免暴露到外网) - 在 FrankenPHP 的
Caddyfile中禁用php_server,改用reverse_proxy指向本地 Hyperf 实例 - 示例片段:
{<br> frankenphp {<br> num_threads 2<br> }<br>}<br><br>yourdomain.com {<br> encode zstd br gzip<br> header {<br> Strict-Transport-Security "max-age=31536000; includeSubDomains"<br> }<br> reverse_proxy 127.0.0.1:9501<br>}注意:reverse_proxy 是 Caddy 原生命令,不需要额外模块;FrankenPHP 会自动继承该指令,无需改动二进制行为。
立即学习“PHP免费学习笔记(深入)”;
静态资源与 PHP 文件需分离处理
Hyperf 项目通常把前端构建产物放在 public/ 目录,但 FrankenPHP 默认不会识别这个路径——它只认 Caddy 的 root 和 file_server 规则。若不显式配置,所有 /static/js/app.js 请求都会被转发给 Hyperf,徒增无谓开销。
- 在
Caddyfile中为静态路径添加handle块,优先于reverse_proxy - 确保
root指向public/目录,且file_server启用hidden防止源码泄露 - 完整示例:
yourdomain.com {<br> root * /app/public<br> handle /static/* {<br> file_server<br> }<br> handle /favicon.ico {<br> file_server<br> }<br> reverse_proxy 127.0.0.1:9501<br>}这里 /app/public 是容器内路径,对应宿主机或构建时 COPY 的位置;handle 的匹配顺序很重要,必须放在 reverse_proxy 之前,否则全被代理走。
环境变量与配置注入的差异点
FrankenPHP 通过环境变量控制全局行为(如 FRANKENPHP_CONFIG),而 Hyperf 使用 .env + config/autoload/ 加载配置。二者不共享上下文,尤其要注意数据库、Redis、JWT 密钥等敏感项不能靠 FrankenPHP 的 env 块透传给 Hyperf 进程。
- FrankenPHP 的
env只影响它自身启动时读取的 PHP ini 参数(如memory_limit),不影响子进程 - Hyperf 必须通过 Docker
environment或挂载.env文件方式注入变量,不能依赖 Caddyfile 的env指令 - 若用 Docker Compose,推荐写法:
services:<br> hyperf:<br> image: my-hyperf-app<br> environment:<br> - DB_HOST=database<br> - REDIS_HOST=redis<br> frankenphp:<br> image: dunglas/frankenphp<br> environment:<br> - SERVER_NAME=yourdomain.com<br> volumes:<br> - ./Caddyfile:/etc/frankenphp/Caddyfile
最关键的一点:不要试图让 FrankenPHP “托管” Hyperf 的生命周期。它的角色是边缘网关,不是应用容器。任何想用 frankenphp run 启动 Hyperf 的尝试,最终都会卡在 Worker exited with code 1 或静默无响应——因为那根本不是它的设计目标。



















