FrankenPHP可直接替代Nginx+PHP-FPM,将Caddy与PHP嵌入同一二进制,HTTP请求由Caddy解析、TLS终止、路由分发,PHP在内存中直执行,免去FastCGI转发与进程通信开销。

FrankenPHP 可以直接替代 Nginx+PHP-FPM,不需要改代码、不引入新抽象层,只要换掉入口进程和配置方式就行。它不是“另一个 PHP 运行时”,而是把 Caddy 和 PHP 嵌进同一个二进制里,HTTP 请求进来后,Caddy 解析、TLS 终止、路由分发,PHP 部分直接在内存里执行——没有 FastCGI 转发、没有 socket 权限问题、没有两个进程间通信开销。
frankenphp php-server 启动即用,但要注意 document_root
最简启动命令是 frankenphp php-server,它会默认监听 :8000,并把当前目录当 Web 根。但 Laravel/Symfony 项目不能直接这么跑,因为它们的入口是 public/ 子目录,不是项目根。
- 必须显式指定
--document-root=public,否则会 403 或 404(public/index.php找不到) - 如果用 Docker,官方镜像
dunglas/frankenphp默认工作目录是/app,所以启动时要加--document-root=/app/public - 不加
--document-root时,frankenphp不会自动 fallback 到public/,这点和 Laravel Valet 或 PHP 内置服务器不同
Caddyfile 替代 nginx.conf,但语法更直白
FrankenPHP 用 Caddyfile 管理路由、HTTPS、静态文件等,不再需要维护 nginx.conf + www.conf + php.ini 三套配置。一个 Caddyfile 就能覆盖全部。
- 最简 Laravel 配置只需 4 行:
your-domain.com { root * /app/public php php_server } - HTTPS 自动开启:只要域名 DNS 解析正确,Caddy 会自动申请 Let’s Encrypt 证书,无需
certbot或手动配置ssl_certificate - 静态文件由 Caddy 直接服务,不走 PHP;PHP 脚本才触发
php_server指令,这点和 Nginx 的location ~ \.php$逻辑一致,但写法更紧凑 - 注意
php_server是 FrankenPHP 特有指令,不是 Caddy 原生的,旧版 Caddy 会报错
worker 模式开启常驻内存,但必须配合 laravel/octane
经典模式(php-server)只是 Nginx+PHP-FPM 的平替,每次请求仍会完整初始化框架;真正抹掉 bootstrap 开销的是 worker 模式,但它不是开个开关就能用。
立即学习“PHP免费学习笔记(深入)”;
- 必须安装
laravel/octane包,并将启动命令换成frankenphp worker --env=production --server=octane - Octane 启动后,Laravel 容器、配置、路由缓存全部常驻内存,后续请求只执行控制器逻辑
- 不能混用:开了 worker 模式就不能再用
php_server指令,Caddyfile 中要删掉或注释掉该行,否则请求会被双路由 - worker 模式下,
frankenphp进程本身不退出,日志输出也从 stdout 变成由 Octane 控制,调试时要看storage/logs/octane-*.log
扩展缺失是最常见的启动失败原因
FrankenPHP 二进制自带 PHP 运行时,但默认不带所有扩展。你本地 php -m 有的,FrankenPHP 未必有——它用的是自己编译的嵌入式 SAPI,不是系统 PHP。
- 检查内置扩展:运行
frankenphp php-cli -m,确认pdo_mysql、redis、opcache、mbstring等都在列表里 - Docker 用户优先用
install-php-extensions工具(官方镜像已预装),比如RUN install-php-extensions pdo_mysql redis opcache - 别依赖
php.ini文件:FrankenPHP 不读系统/etc/php/*/cli/php.ini,扩展必须在构建时显式启用,运行时无法dl()加载 - 常见报错如
Class 'PDO' not found或Call to undefined function curl_init(),基本都是扩展没装全
真正容易被忽略的是:FrankenPHP 的 worker 模式和传统 FPM 的 pm.* 参数不是同一套逻辑——它没有 pm.max_children,而是靠 Go runtime 管理 goroutine 并发,调优重点变成 --workers 数量和内存限制,而不是进程池大小。



















