1. 跨域访问与CORS核心原理
当我们在浏览器中访问https://example.com的网页,这个网页中的JavaScript代码尝试向https://api.othersite.com发起请求时,就会遇到跨域问题。现代浏览器基于同源策略(Same-Origin Policy)的安全机制,默认会阻止这类跨域请求。
CORS(Cross-Origin Resource Sharing)机制的出现,就是为了在保证安全的前提下,解决合理的跨域访问需求。它的工作原理可以概括为:
- 简单请求:对于GET、HEAD和特定Content-Type的POST请求,浏览器直接发送请求,但会在请求头中添加
Origin字段表明来源 - 预检请求:对于可能修改数据的复杂请求(PUT/DELETE等),浏览器先发送OPTIONS预检请求
- 服务器响应:服务器通过响应头声明允许的源、方法和头信息
- 实际请求:预检通过后,浏览器发送实际请求
关键点:CORS是浏览器强制实施的机制,服务器只需正确设置响应头,不需要修改前端代码
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Nginx中的CORS配置详解
2.1 基础CORS配置
在Nginx中配置CORS,主要是通过添加特定的响应头来实现。以下是一个基础配置示例:
nginx复制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' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
# 预检请求缓存时间
add_header 'Access-Control-Max-Age' 1728000;
# 允许浏览器在跨域请求中暴露响应头
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
# 处理OPTIONS预检请求
if ($request_method = 'OPTIONS') {
return 204;
}
# 其他代理配置...
proxy_pass http://backend;
}
}
2.2 生产环境推荐配置
在实际生产环境中,我们通常需要更精细的控制:
nginx复制map $http_origin $cors_origin {
default "";
"~^https://(www\.)?example\.com$" $http_origin;
"~^https://app\.example\.org$" $http_origin;
}
server {
# ...其他配置
location / {
# 动态设置允许的来源
add_header 'Access-Control-Allow-Origin' $cors_origin;
# 允许携带凭据(如cookies)
add_header 'Access-Control-Allow-Credentials' 'true';
# 其他CORS头...
# 重要:当允许凭据时,不能使用通配符*
if ($cors_origin = "") {
add_header 'Access-Control-Allow-Origin' "";
}
}
}
3. 高级配置与性能优化
3.1 针对不同请求类型的配置
nginx复制location /api/ {
# 基础CORS配置
add_header 'Access-Control-Allow-Origin' $cors_origin;
# 处理预检请求
if ($request_method = 'OPTIONS') {
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' 'Authorization,Content-Type';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
# 处理实际API请求
proxy_pass http://api_backend;
}
3.2 缓存优化配置
nginx复制# 在http块中定义CORS变量
map $http_origin $cors_origin {
# ...同上
}
# 定义CORS头为一个变量,避免重复添加
map $request_uri $skip_cors {
default 0;
"~*\.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg)" 1;
}
server {
location / {
# 静态资源不添加CORS头
if ($skip_cors) {
break;
}
# 添加CORS头
add_header 'Access-Control-Allow-Origin' $cors_origin;
# ...其他CORS头
}
}
4. 常见问题与解决方案
4.1 典型错误排查表
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| 跨域请求被阻止 | 缺少Access-Control-Allow-Origin头 |
确保Nginx配置正确添加该头 |
| 预检请求失败 | 未正确处理OPTIONS方法 | 添加OPTIONS方法处理逻辑 |
| 带凭据请求失败 | 使用了通配符*且允许凭据 | 指定具体域名而非* |
| 某些头信息未传递 | 未在Access-Control-Allow-Headers中声明 |
添加需要的头到允许列表 |
| 缓存不生效 | Access-Control-Max-Age未设置或值太小 |
适当增大缓存时间 |
4.2 特殊场景处理
处理多个域名的情况:
nginx复制map $http_origin $cors_origin {
default "";
"~^https://(www\.)?example\.com$" $http_origin;
"~^https://(app|api)\.example\.org$" $http_origin;
"~^https://partner\.example\.net$" $http_origin;
}
处理Vary头:
nginx复制add_header 'Vary' 'Origin';
处理复杂内容类型:
nginx复制add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Custom-Header';
5. 安全最佳实践
- 不要盲目使用通配符:生产环境应明确指定允许的域名
- 限制允许的方法:只开放必要的HTTP方法
- 控制允许的头信息:避免开放不必要的请求头
- 使用HTTPS:跨域请求应始终使用加密连接
- 定期审查配置:随着业务发展更新允许的源列表
nginx复制# 安全增强配置示例
location /api/ {
# 严格限制来源
add_header 'Access-Control-Allow-Origin' 'https://www.example.com';
# 仅允许必要方法
add_header 'Access-Control-Allow-Methods' 'GET, POST';
# 仅允许必要头
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
# 不允许凭据
add_header 'Access-Control-Allow-Credentials' 'false';
# 短期缓存
add_header 'Access-Control-Max-Age' 600;
}
在实际部署中,我发现将CORS配置放在单独的配置文件中并通过include引入,可以更好地维护和管理。例如创建/etc/nginx/conf.d/cors.conf:
nginx复制# cors.conf
map $http_origin $cors_origin {
# ...域名映射规则
}
# 基础CORS头
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
然后在主配置中引入:
nginx复制http {
include /etc/nginx/conf.d/cors.conf;
# ...其他配置
}
这种模块化的配置方式特别适合大型项目,可以确保CORS策略的一致性,同时便于集中更新。当需要调整跨域策略时,只需修改这一个文件即可全局生效,避免了在多处重复配置可能导致的遗漏或错误。
