FrankenPHP经典模式支持零代码改动上线,只需替换Nginx+PHP-FPM为单进程服务,监听:8080,自动托管静态资源;但必须验证$_SERVER['HTTPS']、'REQUEST_URI'、'SCRIPT_NAME'三项运行时行为,并显式启用扩展、严格设置.env权限(600)及storage目录写权限。

不需要改一行 PHP 代码就能上线,前提是别开 Worker 模式;真正要动手的全是环境配置和验证动作。
经典模式下直接替换 Nginx + PHP-FPM
FrankenPHP 的经典模式就是为零改动迁移设计的:它把 Nginx 和 PHP-FPM 合成一个进程,public/index.php 照常执行,$_SERVER 变量、路由逻辑、中间件生命周期全都不变。
- 删掉你原来的
nginx.conf和www.conf,它们彻底失效了 - 启动命令只需一句:
frankenphp server --document-root public/ - 默认监听
:8080,如需改端口加--addr :80(注意需要 root 权限) - 静态资源(CSS/JS/图片)自动由内置 Caddy 服务,不用额外配 location 规则
必须验证的三个运行时行为
即使代码没改,FrankenPHP 构造请求上下文的方式和 Nginx+PHP-FPM 不同,不验证会出静默错误——比如 HTTPS 判断失败导致重定向跳 http,或 URL 生成带错端口。
- 检查
$_SERVER['HTTPS']是否为"on"(不是空字符串或"1"),否则 Laravel 的url()会降级为 http - 确认
$_SERVER['REQUEST_URI']包含 query string(如/search?q=foo),某些路由匹配依赖这个 - 验证
$_SERVER['SCRIPT_NAME']是"/index.php"而非空字符串,否则 Laravel 的asset()可能漏前缀 - 快速验证法:在
public/index.php开头加var_dump(array_intersect_key($_SERVER, ['HTTPS', 'REQUEST_URI', 'SCRIPT_NAME'])); die();
扩展和权限问题比代码更致命
FrankenPHP 自带 PHP 运行时,不读系统 /etc/php/*/fpm/php.ini,所有扩展必须显式启用,且路径、符号表都得对得上。
立即学习“PHP免费学习笔记(深入)”;
- 运行
frankenphp php-cli -m查看已加载模块,重点确认pdo_mysql、redis、opcache、mbstring在列 -
redis扩展不能直接用系统版,得装php8.2-franken-redis(Laravel 11 要求 PHP 8.2+) -
.env文件权限必须是600,FrankenPHP 不做权限兜底,暴露即泄漏 -
storage/logs/和storage/framework/目录仍需确保运行用户(如www-data)有写权限
Worker 模式不是“开个开关”就完事
想用 Worker 模式提升性能?那就要面对真实 PHP 常驻进程的约束:全局状态不能跨请求残留,连接池不能手动 new,单例不能靠静态变量缓存。
- 必须安装
laravel/octane并用php artisan octane:start启动,直接跑frankenphp server不生效 - 禁用所有在服务提供者
boot()中做的副作用操作,例如exec('convert')或file_put_contents('/tmp/log', ...) - 数据库连接必须交给 Octane 管理,不能在控制器里
new PDO(...);Redis 同理,要用Redis::connection()而非new Redis() - 测试是否真复用:写个接口返回
spl_object_id(app('db.connection')),连续调用两次,ID 必须相同才算成功
最常被忽略的是 $_SERVER['HTTP_X_FORWARDED_PROTO'] ——Caddy 默认不设这个头,但 Laravel 的 TrustedProxy 中间件依赖它判断协议,漏配会导致所有 HTTPS 请求被当成 HTTP 处理。这个必须在 Caddyfile 或 FrankenPHP 启动参数里显式透传。



















