frankenphp默认不处理options请求,导致404或500错误;需在frankenphp.yaml中显式配置options路由并返回204响应及完整cors头,确保access-control-allow-headers包含authorization且origin精确匹配。

FrankenPHP 里 OPTIONS 请求被直接 404 或 500
FrankenPHP 默认不接管 OPTIONS 请求——它把这类请求当普通静态路由处理,而 Symfony 的路由没配 OPTIONS 方法时,就会 fallback 到 404 或触发未捕获异常(比如 NotFoundHttpException)。这不是 CORS 配置没生效,是请求根本没进 Symfony 的中间件链。
解决方法只有两个:要么让 FrankenPHP 提前拦截并响应 OPTIONS,要么确保 Symfony 路由显式支持 OPTIONS。推荐前者,更轻量、更可控。
- 在 FrankenPHP 的
frankenphp.yaml中添加options路由规则,匹配所有 API 路径,返回空响应 + CORS 头 - 示例配置:
routes: - path: "^/api/.*" methods: ["OPTIONS"] response: status: 204 headers: Access-Control-Allow-Origin: "https://your-frontend.com" Access-Control-Allow-Methods: "GET, POST, PUT, DELETE, OPTIONS" Access-Control-Allow-Headers: "Content-Type, Authorization, X-Requested-With" Access-Control-Allow-Credentials: "true" - 注意:如果前端用的是
http://localhost:3000,Access-Control-Allow-Origin必须写全,不能用*(否则Access-Control-Allow-Credentials: true会失效)
Symfony 的 NelmioCorsBundle 在 FrankenPHP 下不生效
NelmioCorsBundle 依赖 Symfony 的 HTTP 内核生命周期,在 FrankenPHP 的 SAPI 模式下,某些中间件顺序或事件钩子可能被跳过——尤其是当请求被 FrankenPHP 直接短路(如命中静态文件或预检路由)时,Bundle 根本没机会运行。
别指望靠 Bundle 自动兜底。必须确认三点:
- FrankenPHP 没把
OPTIONS请求转发给 PHP-FPM 或 Symfony;查frankenphp.log看请求是否进了index.php -
nelmio_cors的paths配置必须精确匹配实际请求路径(比如/api/users和/api/users/是不同路径) - 生产环境启用
cache:clear后,务必确认var/cache/prod/下的 CORS 缓存配置已更新(Bundle 会生成缓存文件,FrankenPHP 不会自动 reload)
前端带 Authorization 头却卡在预检
错误信息通常是:Request header field authorization is not allowed by Access-Control-Allow-Headers。这说明 FrankenPHP 或 Symfony 返回的 Access-Control-Allow-Headers 响应头里漏了 Authorization。
关键点在于:这个头必须出现在 OPTIONS 响应中,且大小写不敏感但拼写必须完全一致(authorization ≠ Authorization)。
- FrankenPHP 配置里,
Access-Control-Allow-Headers必须显式包含Authorization(不是auth或token) - 如果用 NelmioCorsBundle,检查
nelmio_cors.allow_headers是否设为['*']或明确列出'Authorization'(注意单引号和引号类型) - 某些 FrankenPHP 版本对 header 值里的空格敏感,建议写成
"Content-Type,Authorization,X-Requested-With",不要换行或多余空格
开发时 localhost 端口变化导致 Origin 不匹配
前端跑在 http://localhost:5173,但某天切到 :3000,CORS 就挂了——因为 FrankenPHP 的 Access-Control-Allow-Origin 写死成一个值,或 Symfony 的 allowed_origins 没覆盖新端口。
硬编码 origin 是最常见也最隐蔽的坑。临时方案是开发期用动态判断:
- 在 FrankenPHP 的
response.headers里避免写死,改用origin变量(如果支持),或退回到 PHP 层处理 - 若必须由 Symfony 控制,就在入口
public/index.php开头加几行判断:if (isset($_SERVER['HTTP_ORIGIN'])) { $origin = $_SERVER['HTTP_ORIGIN']; if (preg_match('/^https?:\/\/localhost(:[0-9]+)?$/i', $origin)) { header('Access-Control-Allow-Origin: ' . $origin); header('Access-Control-Allow-Credentials: true'); exit; } } - 注意:这段代码必须放在任何输出之前,且不能有任何空格或 BOM 字符,否则报
headers already sent
FrankenPHP 的预检失败,往往不是 CORS 配置本身错,而是请求压根没走到能读配置的地方——先盯住 OPTIONS 是否被正确拦截,再看 header 是否完整、是否匹配、是否被覆盖。漏掉任意一环,浏览器就只给你一个红字,不解释。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











