php后端必须主动输出完整cors头:access-control-allow-origin、access-control-allow-credentials及正确处理options预检;json请求需用php://input解析,cookie跨域需samesite=none; secure(https下)且domain配置有效,调试应优先用curl验证真实链路。

PHP后端直接输出CORS头最稳妥
浏览器报 Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy,根本原因不是前端没配,而是PHP没明确告诉浏览器“允许谁来调我”。header() 必须在任何输出前调用,且不能依赖Nginx/Apache代理层补救——万一中间有缓存、CDN或调试时绕过代理,就失效。
- 必须写全三类基础头:
header('Access-Control-Allow-Origin: http://localhost:3000');(不能用*配带凭证的请求) -
header('Access-Control-Allow-Credentials: true');和前端fetch(..., { credentials: 'include' })要严格配对 - 预检请求(OPTIONS)必须被PHP真实响应,不能只靠Nginx
add_header挡掉;常见漏点:没检查$_SERVER['REQUEST_METHOD'] === 'OPTIONS'就直接exit
POST JSON跨域时Content-Type触发预检
前端用 fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' } }),PHP收不到数据?不是 $_POST 为空,是浏览器先发了OPTIONS,而你的PHP没处理它,或者处理完没终止流程,导致后续脚本继续执行出错。
- 预检通过后,真实POST请求体是原始JSON字符串,得用
$raw = file_get_contents('php://input');解析,$_POST始终为空 - 如果同时支持表单提交和JSON,别硬塞进一个入口;建议按
Content-Type分支处理:if (strpos($_SERVER['CONTENT_TYPE'], 'application/json') === 0) - Apache下
.htaccess的mod_rewrite规则可能吞掉OPTIONS请求,优先在PHP里拦截比依赖服务器配置更可控
Cookie跨域共享需前后端双向确认
登录态无法跨域传递?90%是因为 Set-Cookie 头被浏览器静默丢弃。PHP设cookie时,setcookie() 的 $domain 参数不能写 localhost(无效),也不能漏掉 $secure 和 $samesite。
- 开发环境用
setcookie('auth_token', $val, ['domain' => 'localhost', 'path' => '/', 'secure' => false, 'httponly' => true, 'samesite' => 'Lax']);生产必须配真实二级域名如example.com,且前端请求要带credentials: 'include' -
SameSite=Lax在跨域POST下会阻止cookie发送,改成SameSite=None; Secure才行,但要求HTTPS(本地开发可用Secure=false临时绕过) - PHP 7.3+ 的
session_set_cookie_params()要在session_start()前调用,否则无效
调试时用curl模拟真实跨域请求链路
浏览器F12看到的请求头是“美化后”的,看不出预检是否真被响应、cookie是否被拒绝。直接用命令行验证,能绕过所有前端框架和DevTools干扰。
立即学习“PHP免费学习笔记(深入)”;
- 模拟预检:
curl -v -X OPTIONS -H "Origin: http://localhost:3000" -H "Access-Control-Request-Method: POST" https://api.example.com/login,看响应里有没有Access-Control-Allow-Origin - 模拟带凭证的真实请求:
curl -v -X POST -H "Origin: http://localhost:3000" --cookie "auth_token=abc" --cookie-jar /tmp/ck https://api.example.com/profile - 注意:curl默认不发Referer,但某些安全策略会校验它;加
-H "Referer: http://localhost:3000/"可复现问题
跨域真正的复杂点不在PHP语法,而在每个环节都得对齐——前端请求方式、服务端响应头、Cookie策略、反向代理行为、甚至浏览器版本对SameSite的实现差异。少对一环,就卡在黑盒里查半天。











