FrankenPHP是将Caddy与PHP嵌入单二进制的现代应用服务器,开箱即用,无需Nginx、PHP-FPM或手动配置HTTPS;其php-server指令要求public/目录及index.php入口,Caddyfile中需明确定义root和php_server规则,Docker运行需注意端口权限与证书路径,CLI模式下扩展需预编译或显式指定php.ini。

FrankenPHP 不是另一个 PHP-FPM 替代品,它是把 Caddy 和 PHP 打包成一个可执行文件的现代应用服务器——装完就能跑,不用配 Nginx、不用管证书、不手动启 FPM 进程。
frankenphp php-server 启动失败:检查当前目录和 public 入口
直接运行 frankenphp php-server 失败,常见原因是当前目录下没有 public/ 子目录或其中缺少 index.php。FrankenPHP 默认把 public/ 当作 Web 根目录,且要求入口文件为 index.php(不是 index.html 或其他名字)。
- 确认项目结构是:
./public/index.php,而不是./index.php - 如果用的是 Laravel 或 Symfony,确保已把
public/目录正确复制或符号链接到位 - 若想换根目录,必须显式指定配置文件:
frankenphp run --config Caddyfile,不能靠命令行参数临时改 root
Caddyfile 配置里 php_server 指令不生效:路径与扩展名必须匹配
php_server 是 FrankenPHP 的核心指令,但它只对匹配特定后缀(默认 .php)且落在 root 路径内的请求生效。它不会自动“接管所有请求”,也不会 fallback 到静态文件逻辑之外的处理。
- 确保 Caddyfile 中有明确的
root * /path/to/public声明 -
php_server必须写在route或全局作用域中,且不能被try_files或file_server错误覆盖顺序 - 如果需要支持
.phtml或自定义扩展名,得加参数:php_server *.phtml - 注意:Laravel 的
index.php通常要配合重写规则,否则/posts/123会 404,需在 Caddyfile 里补上uri strip_prefix /index.php或用php_server { rewrite .* /index.php }
Docker 启动时 443 端口报错:UDP 映射和证书路径权限问题
运行 docker run -p 443:443 -p 443:443/udp 报错,多数是因为宿主机 443 已被占用,或容器内 Caddy 尝试写入证书目录失败。
立即学习“PHP免费学习笔记(深入)”;
- Linux/macOS 下,非 root 用户无法绑定 443,要么用
-p 8080:80 -p 8443:443临时绕过,要么启动容器时加--cap-add=NET_BIND_SERVICE - Caddy 默认把证书存在
/data,Docker 运行时没挂载-v ./caddy_data:/data,第一次启动就会因权限拒绝而退出 - 本地开发用
https://localhost时,Caddy 自签证书是正常的,浏览器提示“不安全”可手动接受,但不能跳过证书验证逻辑去强制关 HTTPS
frankenphp php-cli 执行脚本报错:扩展缺失或 INI 加载顺序不对
frankenphp php-cli /path/to/script.php 报 Class not found 或 extension not loaded,说明 CLI 模式下没加载项目所需的扩展(比如 pdo_mysql),或 php.ini 未生效。
- FrankenPHP 自带的 PHP 是静态编译的,扩展需在构建时启用;独立二进制默认不含
opcache、pdo_mysql等,得自己编译或改用 Docker 镜像 - CLI 模式不读
php.ini-production,除非显式指定:frankenphp php-cli -c /etc/php/php.ini /path/to/script.php - 更稳妥的做法是:用
frankenphp run启动 HTTP 服务,再用curl http://localhost/artisan(Laravel)或类似方式触发 CLI 逻辑,避免环境不一致
FrankenPHP 的“开箱即用”只对标准结构有效;一旦涉及自定义入口、多应用共存、或需要扩展原生 PHP 功能,就得深入 Caddyfile 规则或接受它对 PHP 生命周期的强约束——比如 worker 模式下,__destruct 不一定被调用,全局变量跨请求不隔离,这些不是 bug,是设计取舍。



















