PHP 怎么配 Caddy?一篇搞定现代 PHP 服务器的极简配置与性能调优
目录导读
- 为什么抛弃 Nginx/Apache,选择 Caddy?
- Caddy 核心优势:自动 HTTPS 与零配置反向代理
- PHP-FPM 与 Caddy 的“联姻”:
php_fastcgi指令详解 - 实战配置:从单站点到多站点(含 Laravel/WordPress)
- 性能与安全调优:缓存、超时、进程池与隐藏版本号
- 常见坑与高频问答(FAQ)
为什么抛弃 Nginx/Apache,选择 Caddy?
在 PHP 开发者的圈子里,Nginx 和 Apache 长期占据统治地位,但 Caddy 2 的出现,正在悄悄改变游戏规则。它最大的杀手锏是“自动 HTTPS”——无需手动申请证书、配置续期,Caddy 会免费为你的域名签发并自动续期 Let's Encrypt 证书。 这意味着你彻底告别了 certbot renew 的定时任务。

Caddy 的配置文件(Caddyfile)语法极为简洁,通常只有 Nginx 配置的 1/5 长度,如果你受够了 Nginx 里 location 块的层层嵌套,Caddy 的指令式语法会让你感到“神清气爽”。
Caddy 核心优势:自动 HTTPS 与零配置反向代理
Caddy 的核心是 caddy 命令 + Caddyfile,启动后,它会自动监听 80/443 端口,当你指定一个域名时,它自动完成以下动作:
- 申请证书
- 配置 TLS 握手
- 将 HTTP 请求 301 跳转到 HTTPS
- 重写请求头(如
X-Forwarded-Proto)
对于 PHP 开发,我们并不需要让 Caddy 直接处理 PHP 文件(它不支持 PHP 解释器),而是让它作为一个反向代理,将 *.php 的请求转发给 PHP-FPM 服务。
PHP-FPM 与 Caddy 的“联姻”:php_fastcgi 指令详解
Caddy 2 提供了一个极其便捷的指令:php_fastcgi,这一条指令,相当于 Nginx 中以下几段配置的合集:
fastcgi_passfastcgi_indexinclude fastcgi_params- 针对 PHP 文件存在的 try_files 检测
基础语法示例:
example.com {
root * /var/www/html
php_fastcgi 127.0.0.1:9000
}
这一行 php_fastcgi 做了什么? 它内部自动实现了:
- 如果请求 URI 以
.php则转发给0.0.1:9000(默认 PHP-FPM 监听地址)。 - 如果请求的文件不存在,自动尝试查找
index.php(这是try_files的逻辑)。 - 自动设置
SCRIPT_FILENAME环境变量。
实战配置:从单站点到多站点(含 Laravel/WordPress)
单站点(ThinkPHP / CodeIgniter 传统框架)
:8080 {
root * /srv/myapp/public
php_fastcgi unix//run/php/php8.2-fpm.sock
file_server
}
多站点(Laravel 强制入口 index.php)
Laravel 要求所有请求都经过 index.php,我们需要用 try_files 重写:
laravel.example.com {
root * /var/www/laravel/public
php_fastcgi unix//run/php/php8.2-fpm.sock
try_files {path} /index.php?{query}
file_server
}
注意:这里 php_fastcgi 后面的 try_files 指令,实际上会覆盖 php_fastcgi 内置的默认 try_files 逻辑,因为 Caddy 的指令执行顺序是“靠后覆盖靠前”。
WordPress(固定链接伪静态)
wordpress.example.com {
root * /var/www/wp
php_fastcgi unix//run/php/php8.2-fpm.sock
try_files {path} /index.php?$query_string
# 静态资源缓存(可选)
@static {
file
path *.css *.js *.png *.jpg *.gif *.svg *.ico
}
header @static Cache-Control "public, max-age=2592000"
file_server
}
性能与安全调优:缓存、超时、进程池与隐藏版本号
关键调优点(必看):
-
提升 PHP-FPM 并发: 修改
www.conf中的pm.max_children,pm.max_children = 50,同时调整 Caddy 的反向代理传输大小:request_body { max_size 20MB } -
超时控制: 防止慢 PHP 脚本拖垮服务器。
reverse_proxy 127.0.0.1:9000 { transport fastcgi { read_timeout 120s write_timeout 120s } }(如果你用
php_fastcgi简写指令,无法直接设置超时,你需要将其展开为route内的reverse_proxy才能指定超时。) -
利用
route指令实现精确匹配:php_fastcgi是“全局”的,如果你需要对特定路径(如/api/)单独处理,建议用route块:route { php_fastcgi 127.0.0.1:9000 # 其他路由规则 } -
隐藏 PHP 错误日志目录: 禁止外部访问
.env文件等敏感信息:@denied { path /.env /.git/* } respond @denied 404
常见坑与高频问答(FAQ)
问:我用了 php_fastcgi,但打开 PHP 文件变成了下载?
答: 这通常是因为 PHP-FPM 未监听该地址,或未启动,执行 systemctl status php8.2-fpm 检查,如果使用 socket 通信,确保 unix// 前缀后面跟对路径(unix//run/php/php8.2-fpm.sock 注意是两个斜杠)。
问:为什么 Laravel 路由一直返回 404?
答: 核心在于 try_files 顺序,请务必将 php_fastcgi 写在 try_files 之前,Caddy 的指令执行是自上而下,如果先遇到 try_files,它会直接尝试寻找文件,找不到就返回 404,而不会转发给 PHP,正确顺序是:root -> try_files(放在后面) -> php_fastcgi(放前面)或者用 route 包裹。
问:Caddy 是否支持 PHP 8.3 / 8.4? 答: 支持,Caddy 只是代理,它不关心 PHP 版本,只要 PHP-FPM 服务正常工作即可,你甚至可以在同一台机器上配置多个 FPM 实例,通过不同端口区分。
问:如何彻底伪装 PHP 版本号?
答: 在 Caddyfile 中添加:
header {
-Server
X-Powered-By "Unknown"
}
但更根本的是修改 php.ini 中的 expose_php = Off。
Caddy 的优雅之处在于“约定优于配置”,对于 PHP 开发者而言,迁移到 Caddy 的成本极低,只需保留 PHP-FPM 配置不变,把 Nginx 配置替换成短短的 10 行 Caddyfile,即可获得自动 HTTPS、HTTP/3 支持和简洁的日志格式,如果你还在用宝塔面板手动部署 Nginx,不妨在本地 Docker 环境试一下 Caddy + PHP-FPM,你会发现前所未有的部署快感。