如何配置nginx解决前端跨域请求问题

我们以一个简单的例子模拟不同情况下产生的跨域问题以及解决方案。假设在http://127.0.0.1:8000的页面调用接口

fetch('http://127.0.0.1:8003/api/data')

常看到的错误“Access to fetch at ‘http://127.0.0.1:8003/api/data’ from origin ‘http://localhost:8000’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”。这说明前端尝试访问的 API 服务,没有正确配置跨域资源共享(CORS)头。按如下配置:


server {
    listen 8003;
    server_name yourdomain.com;

    location / {
        # 允许所有来源访问(生产环境应限制为特定域名)
        add_header 'Access-Control-Allow-Origin' '*';
        # 允许的请求方法
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';     
        # 允许的请求头
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
        
        # 预检请求(OPTIONS)缓存时间
        add_header 'Access-Control-Max-Age' 1728000;
    }
}

并重新加载配置nginx -s reload,问题解决。接下来,我们对请求参数做下修改,并重新发起请求:

var options = {
  method: 'GET', // 请求类型
  headers: {
    'Custom-Header': '123', // 自定义header参数
  }
}
fetch('http://127.0.0.1:8003/api/data',options)

Access to fetch at ‘http://127.0.0.1:8003/api/data’ from origin ‘http://localhost:8000’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
HTTP 请求根据是否触发 CORS 预检(Preflight)分为 简单请求(Simple Request) 和 复杂请求(Complex Request)。它们的核心区别在于浏览器是否会先发送 OPTIONS 预检请求。 因为我们在请求头里增加了的参数Custom-Header,这样请求就从一个简单请求变成了复杂请求。上述错误显示预请求失败,我们重新修改nginx配置:


http {
    include       mime.types;
    default_type  application/octet-stream;

    map $http_origin $cors_origin {
        default "*";  # 默认值(如果 $http_origin 不存在)
        "~^https?://(localhost|example\.com|sub\.example\.com)(:[0-9]+)?$" $http_origin;
        "~^http?://(localhost|example\.com|sub\.example\.com)(:[0-9]+)?$" $http_origin;
    }

    server {
        listen       8003;
        server_name  localhost;
   
        location /api/data {

          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';     
          add_header 'Access-Control-Allow-Credentials' 'true';
          add_header 'Access-Control-Max-Age' 1728000; # 预检缓存20天
          if ($request_method = 'OPTIONS') {
              add_header 'Access-Control-Allow-Origin' $cors_origin;
              add_header 'Access-Control-Allow-Credentials' 'true';
              add_header 'Access-Control-Max-Age' 1728000;
              add_header 'Content-Type' 'text/plain; charset=utf-8';
              add_header 'Content-Length' 0;
              return 204;
          }
          # 允许所有来源访问(生产环境应限制为特定域名)
          add_header 'Access-Control-Allow-Origin' '*';

          alias /Users/wk/data.json;
          default_type application/json;
          add_header Cache-Control "no-cache";
      }
    }

再发请求刚才的问题解决了,但又出现了新问题:
Access to fetch at ‘http://127.0.0.1:8003/api/data’ from origin ‘http://localhost:8000’ has been blocked by CORS policy: Request header field custom-header is not allowed by Access-Control-Allow-Headers in preflight response.”。
说明Access-Control-Allow-Headers不允许header里包含custom-header参数,需要在Access-Control-Allow-Headers里增加custom-header参数。注意Access-Control-Allow-Headers一定要放到if ($request_method = ‘OPTIONS’)语句内,否则不生效。因为CORS 配置的特殊要求,顺序尤为重要,预检请求(OPTIONS) 的响应头必须完整。


    server {
        listen       8003;
        server_name  localhost;
   
        location /api/data {

          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';     
          add_header 'Access-Control-Allow-Credentials' 'true';
         
          add_header 'Access-Control-Max-Age' 1728000; # 预检缓存20天
          if ($request_method = 'OPTIONS') {
              add_header 'Access-Control-Allow-Origin' $cors_origin;
               add_header 'Access-Control-Allow-Headers' 'Content-Type, Custom-Header,Authorization';
              add_header 'Access-Control-Allow-Credentials' 'true';
              add_header 'Access-Control-Max-Age' 1728000;
              add_header 'Content-Type' 'text/plain; charset=utf-8';
              add_header 'Content-Length' 0;
              return 204;
          }
          # 允许所有来源访问(生产环境应限制为特定域名)
          add_header 'Access-Control-Allow-Origin' '*';

          alias /Users/wk/data.json;
          default_type application/json;
          add_header Cache-Control "no-cache";
      }
    }

上述问题得以解决。我们再改下请求参数,增加 credentials: “include”。:


var options = {
  method: 'GET', // 请求类型
  credentials: "include",
  headers: {
    'Custom-Header': '123', // 自定义header参数
    'Content-Type': 'application/json' // 通常需要设置的内容类型
  }
}
fetch('http://127.0.0.1:8003/api/data',options)

credentials: “include” 的作用是:控制发送凭据,在跨域请求中携带当前域的 cookies、HTTP 认证头(如 Authorization)等敏感信息。但它有特殊要求,服务器需显式允许响应头包含 Access-Control-Allow-Credentials: true,Access-Control-Allow-Origin 不能为通配符 ,必须明确指定请求来源域名。否则就会错误类似的错误:
Access to fetch at ‘http://127.0.0.1:8003/api/data’ from origin ‘http://127.0.0.1:8000’ has been blocked by CORS policy: The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard '
’ when the request’s credentials mode is ‘include’。
解决方法:

http {
    include       mime.types;
    default_type  application/octet-stream;

    map $http_origin $cors_origin {
        default "*";  # 默认值(如果 $http_origin 不存在)
        "~^https?://(localhost|example\.com|sub\.example\.com)(:[0-9]+)?$" $http_origin;
        "~^http?://(localhost|example\.com|sub\.example\.com)(:[0-9]+)?$" $http_origin;
    }
    server {
        listen       8003;
        server_name  localhost;
   
        location /api/data {

          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';     
          add_header 'Access-Control-Allow-Origin' $cors_origin;
          add_header 'Access-Control-Max-Age' 1728000; # 预检缓存20天
          if ($request_method = 'OPTIONS') {
              add_header 'Access-Control-Allow-Origin' $cors_origin;
              add_header 'Access-Control-Allow-Headers' 'custom-header, content-type, authorization';
              add_header 'Access-Control-Allow-Credentials' 'true';
              add_header 'Access-Control-Max-Age' 1728000;
              add_header 'Content-Type' 'text/plain; charset=utf-8';
              add_header 'Content-Length' 0;
              return 204;
          }

          add_header 'Access-Control-Allow-Credentials' 'true';
          alias /Users/wk/data.json;
          default_type application/json;
          add_header Cache-Control "no-cache";
      }
    }

总结配置nginx支持跨域请求需要注意的几个要点

  1. 基础 CORS 头配置,必须返回的头部:
add_header 'Access-Control-Allow-Origin' '$http_origin';  # 或指定具体域名
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';  # 按需扩展
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';  # 包含客户端实际使用的头
Allow-Origin 不能同时使用 * 和 Allow-Credentials: true。
Allow-Methods 和 Allow-Headers 需明确列出(不能为 *)。
  1. 预检请求(OPTIONS)处理
if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' '$http_origin';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, OPTIONS';  # 与方法列表一致
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Custom-Header';
    add_header 'Access-Control-Max-Age' 86400;  # 预检缓存时间(秒)
    return 204;  # 空响应
}
  1. 带凭据的请求(Credentials)
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Origin' $http_origin;  # 动态获取域名

Access-Control-Allow-Origin明确域名,不能用 *。Cookie 需设置 SameSite=None; Secur(HTTPS 环境)。

  1. 动态来源管理,使用 map 匹配合法来源:
map $http_origin $cors_origin {
    default "";
    "~^https://(www\.)?example\.com$" $http_origin;
    "~^http://localhost(:\d+)?$" $http_origin;
}
server {
    add_header 'Access-Control-Allow-Origin' $cors_origin;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值