什么是CORS跨域
浏览器出于安全考虑,实施同源策略(Same-Origin Policy),禁止网页向不同域名、端口或协议发送请求。CORS(Cross-Origin Resource Sharing,跨源资源共享)就是浏览器提供的一种机制,让服务器声明哪些外部来源可以访问自己的资源。
简单说:没有CORS配置,前端跨域请求会被浏览器拦截;配了CORS,服务器告诉浏览器"我允许这个来源",请求就能正常完成。
为什么用Nginx配CORS
很多人在后端代码里加CORS头,这样做有几个问题:
- 每种后端语言写法不同,维护成本高
- 多服务架构下每个服务都要配一遍
- 改代码需要重新部署,不如改Nginx配置重启快
在Nginx层统一配置CORS,一次配置覆盖所有后端服务,改了立即生效,运维也方便排查。
CORS核心响应头一览
| 响应头 | 作用 | 常见值 |
|---|---|---|
| Access-Control-Allow-Origin | 允许的来源 | * 或具体域名 |
| Access-Control-Allow-Methods | 允许的HTTP方法 | GET, POST, PUT, DELETE, OPTIONS |
| Access-Control-Allow-Headers | 允许的请求头 | Content-Type, Authorization等 |
| Access-Control-Allow-Credentials | 是否允许携带Cookie | true |
| Access-Control-Max-Age | 预检请求缓存时间 | 1728000(20天) |
| Access-Control-Expose-Headers | 暴露给前端的响应头 | Content-Length等 |
基础配置:允许所有来源
最简单的CORS配置,适合公开API或测试环境:
server {
listen 80;
server_name api.example.com;
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Content-Type, Authorization';
# 处理预检请求
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Content-Type, Authorization';
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
proxy_pass http://backend;
}
}
注意:if块里的add_header不会继承外层location的add_header,所以if块内必须重新写一遍CORS头。这是很多人踩的第一个坑。
进阶配置:指定域名 + 允许Cookie
生产环境中,Access-Control-Allow-Origin: *和Access-Control-Allow-Credentials: true不能同时使用——浏览器会拒绝。需要指定具体域名:
server {
listen 80;
server_name api.example.com;
location / {
# 指定允许的域名
add_header Access-Control-Allow-Origin 'https://www.example.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://www.example.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';
add_header Content-Length 0;
return 204;
}
proxy_pass http://backend;
}
}
always参数:最容易忽略的关键点
Nginx的add_header指令默认只在响应码为200、201、204、206、301、302、303、304、307、308时生效。如果后端返回了400、500等错误码,CORS头就不会被添加,浏览器就会报跨域错误。
解决方案很简单——加always参数:
add_header Access-Control-Allow-Origin * always;
加了always之后,无论响应码是什么,CORS头都会被添加。这是生产环境必须加的参数,不加上线后一定出问题。
多域名动态配置
如果需要允许多个域名访问,不能用逗号分隔(CORS规范不支持),需要用map指令动态判断:
map $http_origin $cors_origin {
default "";
"https://www.example.com" "https://www.example.com";
"https://app.example.com" "https://app.example.com";
"https://admin.example.com" "https://admin.example.com";
}
server {
listen 80;
server_name api.example.com;
location / {
if ($cors_origin != "") {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header Access-Control-Allow-Headers 'Content-Type, Authorization' always;
add_header Access-Control-Allow-Credentials true always;
}
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header Access-Control-Allow-Headers 'Content-Type, Authorization' 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';
add_header Content-Length 0;
return 204;
}
proxy_pass http://backend;
}
}
map指令根据请求的Origin头自动匹配允许的域名,未匹配到的请求不会添加CORS头,安全性更好。
常见问题排错
1. 配置了还是报跨域
检查清单:
- 配置是否在正确的location块内
- if块内是否重新写了CORS头(不继承外层)
- 是否加了always参数
- 是否有多个add_header重复定义(后者覆盖前者)
- 浏览器DevTools的Network面板看响应头是否真的有CORS头
2. OPTIONS请求返回405
说明后端不支持OPTIONS方法,Nginx没有拦截预检请求。确保if块里return 204在OPTIONS请求时生效。
3. 多次add_header被覆盖
Nginx的add_header在同一层级中不会追加,而是覆盖。如果上层server块和下层location块都定义了add_header,location块的生效,server块的被忽略。解决办法:在需要的地方完整写一遍所有CORS头。
4. 带Cookie跨域失败
记住三条铁律:
- Origin不能是*,必须写具体域名
- Allow-Credentials必须是true
- 前端请求必须设置
withCredentials: true
配置完成后如何验证
最简单的方式用curl测试:
# 测试简单请求
curl -H "Origin: https://www.example.com" \
-I https://api.example.com/api/test
# 测试预检请求
curl -X OPTIONS \
-H "Origin: https://www.example.com" \
-H "Access-Control-Request-Method: POST" \
-I https://api.example.com/api/test
检查响应头中是否包含Access-Control-Allow-Origin等CORS头即可。
总结
Nginx配置CORS跨域,核心就4点:
- add_header加always——保证所有状态码都有CORS头
- if块内重新写CORS头——Nginx不继承外层add_header
- Cookie场景指定域名——*和Credentials不能同时用
- 多域名用map——安全又灵活
把这4点搞清楚,Nginx的CORS配置基本不会出问题。
相关阅读
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论