跨域请求怎么解决?

文章导读
跨域报错不等于请求没发出去。浏览器按同源策略比对页面 Origin 与目标地址的协议、域名、端口,不一致就拦下响应,服务端通常已经处理并返回了内容。排查起点在开发者工具的 Network 和 Console,而不是先动前端代码。改请求地址、加本地 proxy 只对开发服务器有效,线上要靠服务端响应头白名单或同域反向代理落地。
📋 目录
  1. 一 mooc非法跨域请求怎么解决
  2. 二 跨域请求先定位在哪一层
  3. 三 跨域请求:先看这四个位置
  4. 四 跨域请求:按可能性从高到低排除
  5. 五 跨域请求:改完怎么确认真的解决了
A A

跨域报错不等于请求没发出去。浏览器按同源策略比对页面 Origin 与目标地址的协议、域名、端口,不一致就拦下响应,服务端通常已经处理并返回了内容。排查起点在开发者工具的 Network 和 Console,而不是先动前端代码。改请求地址、加本地 proxy 只对开发服务器有效,线上要靠服务端响应头白名单或同域反向代理落地。

先打开开发者工具 Network 面板,确认失败的是真实请求还是 OPTIONS 预检,再看响应头有没有 Access-Control-Allow-Origin。没有该头,说明请求到了服务端而服务端没配 CORS,处理点在应用框架或网关;有头仍报错,多半是凭证模式与通配符冲突、Allow-Headers 没覆盖自定义头,或同一个响应头被写了两遍。开发服务器的 proxy 只对本地有效,线上要么服务端放开白名单 Origin,要么走同域反向代理。改完用带 Origin 头的 curl 复验一遍再收工。

mooc非法跨域请求怎么解决

「mooc非法跨域请求」在跨域链路里对应的是浏览器拦截这一步:教学平台页面(例如 https://mooc.example.edu)调用另一域名或端口的接口(例如 https://api.example.edu),接口响应缺少 CORS 头,或者预检 OPTIONS 被拒。确认位置有三处,都能直接核对:

  • Console 报错原文。区分两类:出现 No 'Access-Control-Allow-Origin' header is present on the requested resource,说明服务端没返回该头;出现 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',说明前端带了 Cookie 而服务端用了通配符。
  • Network 面板。点开失败请求,看列表里有没有 Method 为 OPTIONS 的同名请求。有 OPTIONS 且状态码为 401/403/404/405,说明预检被网关或鉴权中间件拦下,真实请求根本没发出。
  • 服务端日志。grep -i 'OPTIONS' /var/log/nginx/access.log | tail -n 50,看 OPTIONS 是否落到后端,以及返回的状态码。

如果 mooc 平台由学校或第三方托管、后端不在你手上,改服务端配置这条路可能走不通,需要平台方为你的 Origin 开白名单,或者在自己的服务里做一层同域转发。

跨域请求怎么解决?

跨域请求先定位在哪一层

按四层从外到内确认,别跳过:

  • 浏览器层:页面 Origin 是什么、请求是否带 credentials、有没有自定义头。位置在 Network → 请求 → Headers → Request Headers 的 Origin、Cookie、Access-Control-Request-Headers。
  • 网关/反向代理层:nginx 或网关是否拦下 OPTIONS,是否覆盖或丢弃上游返回的 CORS 头。位置是 access.log 的状态码和站点配置里的 add_header、proxy_pass 段。
  • 应用框架层:CORS 中间件是否注册、是否排在鉴权中间件之后、白名单是否包含页面 Origin。位置是框架启动日志和 CORS 注册代码。
  • 会话层:带 Cookie 时 Allow-Credentials 是否为 true,Allow-Origin 是否为具体域名,Cookie 的 SameSite、Domain 是否匹配。

判断依据很直接:curl 请求能拿到 CORS 头、浏览器拿不到,问题在网关或缓存;curl 也拿不到,问题在应用配置;两处日志都没有 OPTIONS 记录,说明预检在浏览器侧就没通过,或请求地址写错了域。

跨域请求:先看这四个位置

  • 日志:nginx 访问日志 tail -n 200 /var/log/nginx/access.log | grep -iE 'OPTIONS|api';应用日志按实际路径,例如 tail -n 200 ./logs/app.log | grep -i cors。看 OPTIONS 有没有进来、进来后返回了什么。
  • 命令输出:绕开浏览器直接问服务端要响应头。下面这段把 Origin 换成实际页面地址,URL 换成实际接口,Request-Method 换成前端真实使用的方法。
curl -i -s -X OPTIONS 'https://api.example.edu/api/v1/course/list' \
  -H 'Origin: https://mooc.example.edu' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: content-type,authorization'

看输出里是否出现 Access-Control-Allow-Origin、Allow-Methods、Allow-Headers、Allow-Credentials,状态码是 204 还是被挡回的 401/403/405。

跨域请求怎么解决?
  • 配置项:nginx 侧用 nginx -T | grep -n 'Access-Control'(旧版本无 -T 时逐个查看 include 的站点配置文件);应用侧找 CORS 注册处,确认 allowedOrigins、allowCredentials、allowedHeaders、maxAge 四项。
  • 面板字段:开发者工具 Network → 请求 → Headers → Response Headers,再看 Timing 里是否标了预检耗时。同一个 CORS 头在 Response Headers 出现两行,说明网关和应用各写了一层,浏览器会按非法值处理。

网关侧一段可直接套用的骨架,替换 /api/ 前缀与后端地址:

location /api/ {
    if ($request_method = OPTIONS) {
        add_header Access-Control-Allow-Origin      $http_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     "Content-Type, Authorization, X-Requested-With" always;
        add_header Access-Control-Max-Age           600 always;
        return 204;
    }
    add_header Access-Control-Allow-Origin      $http_origin always;
    add_header Access-Control-Allow-Credentials "true" always;
    proxy_pass http://app_backend;
}

这里最容易被忽略、也的确比较容易出问题的一点:always 参数必须带上。不带时,4xx/5xx 响应不会附加 CORS 头,前端只看到跨域报错,真实错误码被藏起来,排查方向会被带偏。另外直接回显 $http_origin 等于对任意来源放行,只适合内网或临时排查,对外服务建议用 map 做白名单,未命中的 Origin 不返回 Allow-Origin。

跨域请求怎么解决?

跨域请求:按可能性从高到低排除

  1. 服务端完全没配 CORS(最常见)。确认动作:跑上面那段 curl,响应头里没有 Access-Control-Allow-Origin。处理:在应用层注册 CORS 或在网关加响应头,二选一,不要两处同时加。
  2. 预检被网关或鉴权拦下。确认动作:access.log 里 OPTIONS 的状态码是 401/403/404/405。处理:鉴权中间件放行 OPTIONS,或把 return 204 的规则放到鉴权之前。
  3. 带 Cookie 却用了通配符。确认动作:Console 报错中出现 wildcard '*' 与 credentials mode 'include'。处理:Allow-Origin 回显具体 Origin 或走白名单,同时 Allow-Credentials 置 true。
  4. 自定义头或方法没覆盖。确认动作:Request Headers 的 Access-Control-Request-Headers 里有 authorization、x-token 一类字段,而响应的 Allow-Headers 里没有。处理:把字段名补进 Allow-Headers,逐项对齐。
  5. 响应头重复。确认动作:Response Headers 里 Allow-Origin 出现两行。处理:只保留一层,网关用 proxy_hide_header 去掉上游那份,或应用侧关掉。
  6. 前端实际请求了别的域。确认动作:Network 里 Request URL 的 host 与页面 host 不同,且本地是靠 devServer proxy 跑通的。处理:线上补同域反向代理 location,或把接口域名纳入白名单。

跨域请求:改完怎么确认真的解决了

三步验证,缺一步都容易误判为已解决:

  1. 命令行复验。重跑前面的 curl,OPTIONS 应返回 204 且带齐四个头;再把 -X OPTIONS 换成 -X POST 并加上 -H 'Content-Type: application/json' -d '{}',确认真实请求的响应里同样有 Access-Control-Allow-Origin。
  2. 浏览器复验。Network 面板勾选 Disable cache 后硬刷新,看 OPTIONS 与真实请求都变成 204/200,Console 无 CORS 报错;再用无痕窗口走一遍,排除旧响应头被 Access-Control-Max-Age 缓存住的情况。
  3. 观察。走完一轮完整业务流程(例如从登录到提交作业)都没有报错,再考虑收工;发布时确认网关与应用两侧的 CORS 配置都随版本一起变更了。

回滚不能省:改动前先备份,cp /etc/nginx/conf.d/api.conf /etc/nginx/conf.d/api.conf.bak;需要回滚时执行 cp /etc/nginx/conf.d/api.conf.bak /etc/nginx/conf.d/api.conf && nginx -t && nginx -s reload。应用侧改动通常是几个配置项,回滚就是把值恢复成改动前的内容并重启进程,重启前先用 curl 确认旧行为已恢复。临时排查时放开的白名单记得收回,回显任意 Origin 的配置不要长期留在线上。