经纬志
首页 / 实战手册 / 服务端与发布

跨域预检被拒,GET 能过 POST 不行

先看结论

查询瓦片正常,一提交分析任务就报跨域;网络面板里多出一条 OPTIONS 请求,返回 405。

最后验证 2026-11-03 · 多技术栈 · Nginx 1.24 · 更新 2026-10-29 · 引用本文

现象

浏览器控制台:

Access to fetch at 'https://gis.example.cn/geoserver/rest/process' from origin
'https://app.example.cn' 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.

网络面板中 OPTIONS /geoserver/rest/process 返回 405 Method Not Allowed, Allow 响应头里只有 GET, HEAD, POST。

原因

带自定义头(如 Content-Type: application/json、Authorization) 或非简单方法的请求,浏览器会先发 OPTIONS 预检。 预检请求不带凭证,也不会命中后端的业务路由,很多框架直接返回 405。

另一个常见错误是同时写 Access-Control-Allow-Origin: * 与 Access-Control-Allow-Credentials: true, 浏览器会判定为非法组合并拒绝。

排查过程

  1. 网络面板筛选 OPTIONS,确认状态码与响应头。
  2. 看请求是否带 withCredentials 或自定义头,决定是否真的需要预检。
  3. 用 curl 手动发一次预检,排除浏览器缓存干扰。
curl -i -X OPTIONS "https://gis.example.cn/geoserver/rest/process" \
  -H "Origin: https://app.example.cn" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: content-type,authorization"

解决方案

在 Nginx 层统一处理预检,后端不必关心 OPTIONS。注意 always 让 4xx 响应也带上跨域头:

location /geoserver/ {
    if ($request_method = OPTIONS) {
        add_header Access-Control-Allow-Origin "https://app.example.cn" 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 86400 always;
        add_header Content-Length 0;
        return 204;
    }

    add_header Access-Control-Allow-Origin "https://app.example.cn" always;
    add_header Access-Control-Allow-Credentials "true" always;
    add_header Access-Control-Expose-Headers "Content-Length, Content-Range" always;

    proxy_pass http://127.0.0.1:8080/geoserver/;
}

需要允许任意来源时,就不能带凭证。二者只能选一个:

# 允许任意来源,但不允许携带 Cookie
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Credentials "false" always;

前端确实不需要凭证时,去掉 credentials: 'include', 请求会从“预检 + 实际请求”两次变成一次:

fetch('https://gis.example.cn/geoserver/rest/process', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(payload)
});

验证

预检请求返回 204,响应头包含 Access-Control-Allow-Origin 与 Access-Control-Allow-Methods。 浏览器控制台不再出现 CORS 报错,POST 请求正常返回结果。 带 Cookie 的请求与不带 Cookie 的请求分别测试,均通过。

先看预检:GET 能过、POST 不能过,几乎都是预检没通过。 CORS 报错文案里的 “preflight request” 是判断依据,不必去查后端业务逻辑。

引用本文

经纬志(2026)。《跨域预检被拒,GET 能过 POST 不行》。https://www.jingweizhi.com/cookbook/wms-cors-preflight.html(访问日期 2026-11-03)。