0

Nginx CORS跨域配置详解:从原理到实战的完整指南

2026.05.23 | youres | 19次围观

什么是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是否允许携带Cookietrue
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点:

  1. add_header加always——保证所有状态码都有CORS头
  2. if块内重新写CORS头——Nginx不继承外层add_header
  3. Cookie场景指定域名——*和Credentials不能同时用
  4. 多域名用map——安全又灵活

把这4点搞清楚,Nginx的CORS配置基本不会出问题。

相关阅读

版权声明

本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论
882文章数 0评论数
作者其它文章