Nginx中配置CORS需在location块内用add_header配合always参数设置完整响应头,并显式处理OPTIONS预检请求,返回204状态码。

直接在 Nginx 的 location 块中用 add_header 注入标准 CORS 响应头,是最简明有效的基础配置方式。关键不是加几个头,而是加对位置、覆盖所有响应状态、并显式处理 OPTIONS 预检请求。
必须配全的基础响应头
只写 Access-Control-Allow-Origin 很容易失败。浏览器要求一组头同时存在且逻辑自洽:
-
Access-Control-Allow-Origin:设为具体域名(如
https://your-app.com)更安全;若需传 Cookie,严禁用* -
Access-Control-Allow-Methods:列出真实会用的方法,例如
GET, POST, PUT, DELETE, OPTIONS -
Access-Control-Allow-Headers:前端实际发送的请求头名,如
Content-Type, Authorization, X-Requested-With,大小写需严格一致 -
Access-Control-Allow-Credentials:设为
true才能携带 Cookie 或认证凭证;此时Origin必须是明确域名,不能是* -
Access-Control-Max-Age:建议设为
1728000(20 天),减少重复预检请求
必须显式处理 OPTIONS 预检请求
浏览器在发复杂请求前,会先发一个 OPTIONS 请求试探权限。Nginx 默认不拦截,容易转发给后端导致 404 或 502,从而跨域失败:
- 在对应
location块内添加if ($request_method = 'OPTIONS')判断 - 在 if 块中重复写全所有 CORS 头(
add_header不继承作用域) - 结尾必须用
return 204—— 空响应体、符合规范,避免Content-Length冲突
确保头真正生效的两个细节
很多配置看似写了,但浏览器收不到,常因以下两点被忽略:
-
加
always参数:add_header ... always;确保 204、4xx、5xx 等非 2xx 响应也能带 CORS 头 -
放在
proxy_pass所在的 location 块内:不要写在server或根location /下,否则静态资源、健康检查接口也会被污染
一个可直接复用的基础配置示例
假设 API 路径为 /api/,后端服务运行在 http://backend:8000:
location /api/ {
proxy_pass http://backend:8000/;
<pre class="brush:php;toolbar:false;"># 基础 CORS 头(always 确保错误响应也生效)
add_header 'Access-Control-Allow-Origin' 'https://your-frontend.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 1728000 always;
# 预检请求处理
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://your-frontend.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 1728000 always;
add_header 'Content-Type' 'text/plain; charset=utf-8' always;
add_header 'Content-Length' 0 always;
return 204;
}}











