frankenphp 1.6 的多站点本质是 caddyfile 路由配置,所有站点共用一个进程,通过 domain 分组的 route + root + php + file_server 规则实现隔离,不支持 per-site php.ini,https 需显式配置证书路径或确保 80 端口开放以触发 http-01 自动签发。

FrankenPHP 1.6 的多站点本质是 Caddyfile 路由配置
FrankenPHP 1.6 不提供类似 Apache VirtualHost 或 Nginx server 块的独立虚拟主机语法,它把“多站点”完全交由内置的 Caddy Web Server 管理。也就是说,你不需要启动多个 FrankenPHP 进程,也不用为每个站点单独配 PHP-FPM 池——所有站点共用一个进程,靠 Caddyfile 中的 handle 和 route 规则分流请求。
关键点在于:FrankenPHP 启动时只读取一个 Caddyfile,所有域名、路径、TLS、PHP 处理逻辑都写在里面。配置错误导致的“只跑通一个站”或“404 所有 PHP 路径”,几乎全是 Caddyfile 路由顺序或 PHP 直通规则没对齐造成的。
-
handle块必须显式匹配*.php或具体路径,否则 PHP 文件会被当作静态文件返回源码(常见于未加php指令) - 多个域名共存时,
route必须按域名分组,且每个分组内要包含完整的 PHP 处理链(php+file_server回退) - 不要在 Caddyfile 里写
fastcgi_pass——FrankenPHP 内部已封装,强行加会报错unknown directive 'fastcgi_pass'
如何为不同域名绑定独立文档根目录
FrankenPHP 1.6 不支持像传统 FPM 那样为每个 pool 设置 chdir 或 php_admin_value[doc_root]。它的 PHP 执行上下文默认以 Caddy 当前 root 为准,所以必须靠 Caddyfile 的 route + root 组合来隔离。
示例:两个域名 site-a.com 和 site-b.com 分别指向 /var/www/a 和 /var/www/b:
site-a.com {
root * /var/www/a
php
file_server
}
<p>site-b.com {
root * /var/www/b
php
file_server
}</p>
注意:php 指令必须紧跟 root 后面,否则 PHP 脚本找不到 index.php;file_server 是必需的回退项,否则非 PHP 请求(如 CSS/JS)会 404。
- 如果某个站点需要 Laravel 的重写规则(隐藏
index.php),在对应route内加try_files {path} /index.php?{query} - 不建议用
import引入外部片段来管理多站点——FrankenPHP 1.6 的 Caddyfile 解析器对嵌套 import 支持不稳定,容易静默忽略 - 修改 Caddyfile 后必须重启 FrankenPHP(
systemctl restart frankenphp或 kill 后重运行),热重载目前不可靠
HTTPS 自动化与证书共用问题
FrankenPHP 1.6 内置 Caddy 的 HTTPS 自动化能力,但多站点下证书申请行为容易被误解:Caddy 默认对每个 domain 单独申请证书,不是共享一张证书。如果你用泛域名证书(如 *.example.com),必须显式指定,否则 Caddy 会为 a.example.com 和 b.example.com 各申请一张。
正确做法是在 Caddyfile 顶部声明 TLS 选项:
{
email admin@example.com
https_port 443
}
<p>a.example.com, b.example.com {
tls /etc/caddy/example.com.crt /etc/caddy/example.com.key
root * /var/www/a
php
file_server
}</p>
- 省略
tls行会导致首次访问时触发 Let's Encrypt 挑战,若服务器无公网 IP 或防火墙阻断 80/443,会卡在证书获取阶段,日志显示obtain: [a.example.com] failed - FrankenPHP 1.6 不支持 ACME DNS 挑战(如 Cloudflare API),仅支持 HTTP-01,所以确保 80 端口可被外网访问是前提
- 证书路径必须用绝对路径,相对路径(如
./certs/)会静默失败
PHP 配置无法 per-site 生效?这是设计使然
FrankenPHP 1.6 不支持 per-virtual-host 的 php.ini 覆盖,也没有 php_admin_flag 这类指令。整个进程共用同一份 PHP 配置(即编译时加载的 php.ini 和运行时通过 -c 指定的配置)。
这意味着:你不能让 site-a.com 的 memory_limit=256M,而 site-b.com 是 512M。如果真有这种隔离需求,只能拆成两个 FrankenPHP 实例,各自带独立 php.ini 和 Caddyfile,监听不同端口再由反向代理分发——但这已脱离“单二进制简化部署”的初衷。
- 框架级配置(如 Laravel 的
.env)仍可 per-site 独立,不受影响 - 可通过
php_value在 Caddyfile 的php指令后追加运行时设置,但仅限部分可变参数(如date.timezone),且对所有站点生效 - 最常被忽略的一点:FrankenPHP 的
php指令默认启用opcache.enable=1,但不会自动清理旧脚本缓存。多站点共用 opcache 时,一个站点改了composer.json并composer install,另一个站点可能因 opcode 缓存未更新而报错
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











