前后端跨域(Nginx 跨域详解和配置)

前后端跨域(Nginx 跨域详解和配置)
Nginx 跨域详解和配置

一、什么是跨域?

跨域(Cross-Origin)是指浏览器同源策略(Same-Origin Policy)限制下的行为。当网页尝试从不同源(协议、域名、端口任一不同)请求资源时,就会发生跨域问题。

跨域 = 浏览器的安全规则:不允许一个网站,随便请求另一个网站的数据。

同源的定义:

  • 协议相同(http/https)
  • 域名相同(example.com)
  • 端口相同(80/443)

跨域示例:

  • http://a.com → http://b.com (域名不同)
  • http://a.com:8080 → http://a.com:9090 (端口不同)
  • http://a.com → https://a.com (协议不同)

比如:

  • 前端页面在 http://localhost:8080
  • 后端接口在 http://localhost:9090

浏览器直接拦截请求,报:

No 'Access-Control-Allow-Origin' header is present on the requested resource

这就是 跨域问题

二、Nginx 跨域配置

1. 基础跨域配置

location /api/ {    # 允许所有域名访问(生产环境建议指定具体域名)    add_header 'Access-Control-Allow-Origin''*' always;        # 允许的请求方法    add_header 'Access-Control-Allow-Methods''GET, POST, PUT, DELETE, OPTIONS' always;        # 允许的请求头    add_header 'Access-Control-Allow-Headers''DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;        # 允许携带凭证(Cookie)    add_header 'Access-Control-Allow-Credentials''true' always;        # 预检请求缓存时间(单位:秒)    add_header 'Access-Control-Max-Age'1728000 always;        # 处理 OPTIONS 预检请求    if ($request_method='OPTIONS') {        return 204;    }        proxy_pass http://backend_server;}

2. 动态域名配置(推荐)

前后端跨域(Nginx 跨域详解和配置)

# 允许特定域名跨域location /api/ {    # 获取请求源    set$origin$http_origin;        # 允许的域名列表    if ($origin ~* (https?://(www\.)?(domain1\.com|domain2\.com|localhost))) {        add_header 'Access-Control-Allow-Origin''$origin' always;        add_header 'Access-Control-Allow-Credentials''true' always;        add_header 'Access-Control-Allow-Methods''GET, POST, PUT, DELETE, OPTIONS' always;        add_header 'Access-Control-Allow-Headers''DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;    }        if ($request_method='OPTIONS') {        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_server;}

3. 使用 map 模块(更优雅)

# 在 http 块中定义map $http_origin$cors_origin {    default "";    "~^https?://(www\.)?(domain1\.com|domain2\.com)$" $http_origin;    "~^http://localhost(:[0-9]+)?$" $http_origin;}# 在 server/location 块中使用location /api/ {    add_header 'Access-Control-Allow-Origin'$cors_origin always;    add_header 'Access-Control-Allow-Credentials''true' always;    add_header 'Access-Control-Allow-Methods''GET, POST, PUT, DELETE, OPTIONS' always;    add_header 'Access-Control-Allow-Headers''Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With' always;        if ($request_method='OPTIONS') {        return 204;    }        proxy_pass http://backend_server;}

三、常见场景配置

1. 静态资源跨域

location /static/ {    add_header 'Access-Control-Allow-Origin''*' always;    alias /var/www/static/;}

2. 全局跨域配置

server {    listen 80;    server_name example.com;        # 全局添加跨域头    add_header 'Access-Control-Allow-Origin''*' always;    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;        location / {        root /var/www/html;        try_files $uri$uri/ /index.html;    }}

3. 前后端分离项目配置

server {    listen 80;    server_name frontend.com;        # 前端静态资源    location / {        root /var/www/frontend;        try_files $uri$uri/ /index.html;    }        # 后端 API 代理    location /api/ {        add_header 'Access-Control-Allow-Origin''http://frontend.com' always;        add_header 'Access-Control-Allow-Credentials''true' always;        add_header 'Access-Control-Allow-Methods''GET, POST, PUT, DELETE, OPTIONS' always;        add_header 'Access-Control-Allow-Headers''Authorization,Content-Type' always;                if ($request_method='OPTIONS') {            return 204;        }                proxy_pass http://backend_server:8080/;        proxy_set_header Host $host;        proxy_set_header X-Real-IP $remote_addr;        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;    }}

四、重要参数说明

参数

说明

Access-Control-Allow-Origin

指定允许的源,* 表示全部,但不能与 Credentials 同时使用

Access-Control-Allow-Methods

允许的 HTTP 方法

Access-Control-Allow-Headers

允许的请求头字段

Access-Control-Allow-Credentials

是否允许携带 Cookie

Access-Control-Max-Age

预检请求的缓存时间

Access-Control-Expose-Headers

允许客户端访问的响应头

五、注意事项

1.always 参数:确保即使响应状态码为 4xx/5xx 时也添加跨域头

2.\* 与凭证问题:当 Access-Control-Allow-Credentials: true 时,Allow-Origin 不能为 *,必须指定具体域名

3.OPTIONS 预检请求

  • 非简单请求(如 PUT、DELETE 或带自定义头)会先发送 OPTIONS 预检
  • 必须正确响应 OPTIONS 请求,返回 204 状态码

4.性能优化

  • 使用 Access-Control-Max-Age 缓存预检结果
  • 避免在 if 块中使用 proxy_pass(if 是限制性上下文)

5.安全建议

  • 生产环境避免使用 *,应指定具体的白名单域名
  • 使用 HTTPS 避免中间人攻击
  • 谨慎使用 Access-Control-Allow-Credentials

六、调试方法

# 查看响应头curl-I-H"Origin: http://example.com" http://your-domain.com/api/test# 测试 OPTIONS 请求curl-X OPTIONS -H"Origin: http://example.com"-H"Access-Control-Request-Method: POST" http://your-domain.com/api/test

浏览器控制台错误信息:

  • No 'Access-Control-Allow-Origin' header → 缺少跨域头
  • Credentials flag is 'true' → 凭证配置问题
  • Credentials flag is 'true' → 凭证配置问题

七、完整示例

http {    # 定义允许的源    map $http_origin$allowed_origin {        ~^https?://(www\.)?(trusted-domain\.com|app\.example\.com)$ $http_origin;        ~^http://localhost(:[0-9]+)?$ $http_origin;        default "";    }        server {        listen 80;        server_name api.example.com;                location /api/ {            # 添加跨域头            add_header 'Access-Control-Allow-Origin'$allowed_origin always;            add_header 'Access-Control-Allow-Credentials''true' always;            add_header 'Access-Control-Allow-Methods''GET, POST, PUT, DELETE, OPTIONS, PATCH' always;            add_header 'Access-Control-Allow-Headers''Authorization, Content-Type, X-Requested-With, Accept' always;            add_header 'Access-Control-Expose-Headers''Content-Length, X-Total-Count' always;            add_header 'Access-Control-Max-Age'3600 always;                        # 处理预检请求            if ($request_method='OPTIONS') {                add_header 'Content-Length'0;                add_header 'Content-Type''text/plain charset=UTF-8';                return 204;            }                        # 代理到后端服务            proxy_pass http://backend_upstream;            proxy_set_header Host $host;            proxy_set_header X-Real-IP $remote_addr;            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;            proxy_set_header X-Forwarded-Proto $scheme;        }    }}

一句话总结

  • 跨域:浏览器的安全限制,不同域名 / 端口不能互相请求。
  • Nginx 跨域:用 Nginx 配置,绕开或解除这个限制,让前后端能正常通信。

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有

最新文章

热门文章

本栏目文章