php接口跨域预检失败本质是options请求未被正确响应,原因包括web服务器拦截、php未提前处理、框架中间件位置错误或cors头配置不匹配。

PHP接口跨域预检请求失败,本质是浏览器发了 OPTIONS 请求,但服务端没正确响应——不是代码没写,而是没走到、没写对、或被中间层拦住了。
确认 OPTIONS 请求是否真正到达 PHP
很多情况下,OPTIONS 根本没进 PHP 脚本,直接被 Web 服务器拒绝(返回 405)或静默丢弃。先验证这点:
- 用 curl 模拟:curl -X OPTIONS -H "Origin: https://your-frontend.com" -I http://your-api.com/api/user
- 看响应状态码:如果是 405,说明 Nginx/Apache 拦截了 OPTIONS 方法;如果是 200/404/500 但没带 CORS 头,说明 PHP 层没处理或处理失败
- Nginx 需在 location 块中显式支持 OPTIONS:不能只靠 try_files 转发,要加 if 判断并 return 204
PHP 层必须主动响应 OPTIONS,不能只设 header
即使设置了 Access-Control-Allow-Origin,若脚本没对 OPTIONS 方法做 exit 或提前返回,后续逻辑可能出错或输出干扰内容,导致响应体不为空(浏览器要求预检响应体必须为空)。
- 在入口文件(如 index.php 或框架统一入口)最顶部加判断:
- if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { header('Access-Control-Allow-Origin: https://your-frontend.com'); header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With'); header('Access-Control-Allow-Credentials: true'); header('Access-Control-Max-Age: 86400'); header('Content-Length: 0'); http_response_code(204); exit; }
- 注意:Access-Control-Allow-Origin 不能为 * 且同时开启 credentials;若前端带 credentials: 'include',这里必须写具体域名,并校验 $_SERVER['HTTP_ORIGIN'] 白名单
检查 header 设置时机和输出污染
header() 失效的常见原因不是逻辑错,而是执行前已有输出。
- 文件开头有 UTF-8 BOM(尤其 Windows 编辑器保存时默认带),用 VS Code 或 Notepad++ 改为“UTF-8 无 BOM”
- require/include 的配置文件、日志工具、debug 函数(如 var_dump、print_r)提前输出了空格或内容
- 用 headers_sent($file, $line) 快速定位:if (headers_sent($file, $line)) { die("Header already sent in $file on line $line"); }
- 框架用户避免在控制器里手动 header()——ThinkPHP/Laravel 等已发过响应头,再调会警告且失效
匹配前端实际发送的 Headers
Access-Control-Allow-Headers 必须精确列出前端所有自定义 Header,大小写敏感,漏一个就失败。
- 前端 fetch 若带 Authorization: Bearer xxx,后端就必须包含 Authorization
- 若用 X-Auth-Token 或 x-requested-with,也要一一对应写入 allowHeaders
- 不能写成 *(除非没开 credentials),也不能用全小写或驼峰拼错
- ThinkPHP 6 中若用内置 Cors 中间件,需传参指定 allowHeaders 数组,不能依赖默认值
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











