跨域资源共享(CORS)安全配置
CORS 控制浏览器跨域请求的能力。NGINX 作为反向代理可统一添加 CORS 响应头,实现集中管理的跨域策略。
基础 CORS 配置
简单跨域
nginx
server {
location /api/ {
add_header 'Access-Control-Allow-Origin' 'https://app.example.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Allow-Credentials' 'true';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://backend;
}
}
Access-Control-Allow-Credentials: true时Origin不能使用*,必须指定具体域名。
动态 Origin 白名单
多域名 CORS
nginx
map $http_origin $cors_origin {
default "";
"~^https://(www\.)?example\.com$" $http_origin;
"~^https://app\.example\.com$" $http_origin;
"~^https://admin\.example\.com$" $http_origin;
}
server {
location /api/ {
if ($cors_origin != "") {
add_header 'Access-Control-Allow-Origin' $cors_origin;
}
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With';
add_header 'Access-Control-Allow-Credentials' 'true';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://backend;
}
}
使用
map指令根据请求的Origin动态匹配,只允许白名单中的域名。
安全注意事项
避免通配符
nginx
# 危险 - 不要这样做
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' '*';
通配符允许任何站点跨域访问,可能导致 CSRF 攻击和数据泄露。
Preflight 请求处理
nginx
location /api/ {
# 预检请求直接返回,不转发到后端
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_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' 3600;
return 204;
}
# 正常请求转发到后端
if ($cors_origin != "") {
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
}
proxy_pass http://backend;
}
OPTIONS 预检请求由 NGINX 直接处理,减少后端压力。
Max-Age控制浏览器缓存预检结果的时间。
子域名通配
匹配同域所有子域名
nginx
map $http_origin $cors_origin {
default "";
"~^https://[a-z0-9-]+\.example\.com$" $http_origin;
}
正则匹配任意字母数字和连字符组成的子域名,但不会匹配
example.com本身。
不同路径不同策略
nginx
server {
# 公开 API - 宽松策略
location /api/public/ {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://backend;
}
# 私有 API - 严格策略
location /api/private/ {
if ($cors_origin != "") {
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
}
proxy_pass http://backend;
}
}
要点总结
- 使用
map指令实现动态 Origin 白名单,避免通配符 Credentials: true时Origin不能为*- OPTIONS 预检请求由 NGINX 直接返回 204,减少后端压力
Max-Age控制浏览器缓存预检结果,减少请求次数- 公开接口和私有接口使用不同 CORS 策略
- 避免设置过于宽松的
Allow-Headers,仅暴露必要的头
📝 发现内容有误?点击此处直接编辑