Access-Control-Allow-Origin头未生效是因为FrankenPHP基于Caddy,PHP的header()无法绕过Caddy响应重写,必须在Caddyfile中显式配置header指令并前置处理OPTIONS请求,且*与credentials=true互斥。

为什么 Access-Control-Allow-Origin 头没生效?
FrankenPHP 1.4 默认不自动添加 CORS 响应头,即使你在 PHP 脚本里用 header('Access-Control-Allow-Origin: *'),也可能被底层 Caddy 配置拦截或覆盖。根本原因是 FrankenPHP 以 Caddy 为 Web 服务器,所有 HTTP 响应都经 Caddy 中间件处理,PHP 的 header() 只影响应用层,无法绕过 Caddy 的响应重写规则。
Caddyfile 中必须显式配置 CORS 中间件
在 FrankenPHP 启动时加载的 Caddyfile(通常是项目根目录下的 Caddyfile 或通过 --caddyfile 指定)中,需为对应路由添加 header 指令。常见错误是只配了 PHP 路由而漏掉静态资源或 API 前缀路径。
正确写法示例:
localhost:8080 {
php_server
header /api/* {
Access-Control-Allow-Origin "*"
Access-Control-Allow-Methods "GET,POST,OPTIONS,PUT,DELETE"
Access-Control-Allow-Headers "Content-Type,Authorization,X-Requested-With"
Access-Control-Expose-Headers "X-Total-Count"
}
header / {
Access-Control-Allow-Origin "*"
}
}
-
/api/*路径块专用于接口,支持复杂请求(带认证、自定义头),必须包含OPTIONS方法和Access-Control-Allow-Headers - 若前端直接访问
/下的index.php,也需单独配根路径的header,否则预检请求(preflight)可能失败 - 不要用
header * { ... }全局匹配,Caddy 会报错:通配符不能用于header指令
启用 php_server 时 OPTIONS 请求必须透传
FrankenPHP 的 php_server 插件默认不处理 OPTIONS 请求,导致浏览器预检失败,返回 404 或 500。这不是 CORS 配置问题,而是请求根本没进 PHP 层。
解决方式:在 Caddyfile 中显式放行 OPTIONS,并确保它不被 php_server 拦截:
localhost:8080 {
# 先匹配并响应 OPTIONS,避免进入 php_server
@options method OPTIONS
respond @options 204 {
header Access-Control-Allow-Origin "*"
header Access-Control-Allow-Methods "GET,POST,OPTIONS,PUT,DELETE"
header Access-Control-Allow-Headers "Content-Type,Authorization"
}
<pre class="brush:php;toolbar:false;">php_server
header /api/* { ... }}
- 必须把
@options规则放在php_server之前,Caddy 规则按顺序匹配 - 响应码用
204(No Content),不是200,符合 CORS 预检规范 - 如果用了 Laravel/Symfony 等框架自带 CORS 中间件,仍需在 Caddy 层放行
OPTIONS,否则请求到不了 PHP
开发环境可临时禁用凭证(credentials)简化调试
当设置 Access-Control-Allow-Origin: "*" 时,Access-Control-Allow-Credentials 必须为 false,否则浏览器拒绝请求。这是硬性限制,不是 FrankenPHP 的 bug。
如果你需要携带 Cookie 或 Authorization header,必须把 Access-Control-Allow-Origin 改为具体域名,例如:
header /api/* {
Access-Control-Allow-Origin "https://myapp.com"
Access-Control-Allow-Credentials "true"
...
}
- 本地开发常用
http://localhost:3000,但注意协议、端口、斜杠结尾都要完全匹配 - Caddy 不支持动态 Origin(如读取
Origin请求头后回写),必须静态配置;多域名需用多个header块或外部插件 - Chrome 控制台若显示 “The value of the 'Access-Control-Allow-Origin' header must not be the wildcard '*' when the request's credentials mode is 'include'”,说明你同时设了
*和credentials: true,立刻检查这两处
FrankenPHP 的 CORS 实际是 Caddy 的 CORS,不是 PHP 的——所有配置点都在 Caddyfile,而不是 .htaccess 或 php.ini。最容易忽略的是 OPTIONS 请求未被显式处理,以及 Access-Control-Allow-Origin 与 credentials 的互斥关系。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











