Nginx可通过error_page 405 = @cors_error统一处理跨域预检失败,配合专用location主动响应合法OPTIONS请求并返回204,命名location@cors_error返回JSON错误响应并设置CORS头,避免与正常流程冲突。

当浏览器发起跨域请求时,会先发送一个 OPTIONS 预检请求。如果 Nginx 拒绝该请求(比如因缺少 CORS 头或路径不匹配),默认返回 405 或 404,而前端收到的是原始 HTML 错误页,不利于统一处理。你可以用 error_page 捕获预检失败的响应,并重写为标准 JSON 提示,关键在于精准匹配预检请求并避免干扰正常流程。
只对 OPTIONS 请求启用 error_page 转发
Nginx 的 error_page 是全局或 location 级别的,但你不能直接按 method 绑定。可行做法是在匹配 OPTIONS 的 location 中主动触发错误,再用 error_page 捕获:
- 在 server 或 location 块中,用
if ($request_method = 'OPTIONS') { ... }判断,但注意if在 location 内受限,推荐用专用 location - 定义一个专用于预检的 location,例如
location /api/ { ... },并在其中显式处理 OPTIONS - 更稳妥的方式:用
error_page 405 = @cors_preflight;,配合limit_except或if触发 405,再跳转到自定义 handler
用 named location 返回 JSON 并设置 CORS 头
定义一个命名 location(如 @cors_error),在里面设置 JSON 响应体、状态码和必要头信息:
- 用
return 400 '{"error":"CORS preflight failed"}';直接返回 JSON - 必须加上
add_header Content-Type 'application/json; charset=utf-8'; - 补全 CORS 相关头:
add_header Access-Control-Allow-Origin '*';、add_header Access-Control-Allow-Methods 'GET,POST,OPTIONS';、add_header Access-Control-Allow-Headers 'Content-Type,X-Requested-With'; - 注意:
add_header在 named location 中有效,但不能覆盖已发送的头;若前面已有响应头,需确保 location 是唯一出口
避免与正常 OPTIONS 处理冲突
如果你已在 location 中用 add_header 正常响应预检(如直接 return 204),就不要让 error_page 干预它。关键是区分“合法预检被拒”和“非法预检”:
- 对明确支持的 API 路径,用
location ~ ^/api/.*$ { if ($request_method = 'OPTIONS') { add_header ...; return 204; } }主动处理 - 对不支持 OPTIONS 的路径(如静态文件目录),允许它落入 405,再由
error_page 405 = @cors_error;统一兜底 - 慎用
error_page 404处理预检——404 表示路径不存在,而预检失败更多是 method 不被允许,语义不符
完整配置片段示例
以下是一个轻量、可复用的配置结构:
server {
listen 80;
server_name example.com;
<pre class="brush:php;toolbar:false;"># 兜底:所有 OPTIONS 请求若未被显式处理,触发 405
error_page 405 = @cors_error;
location / {
# 正常业务逻辑...
try_files $uri @backend;
}
# 显式支持的 API 接口,主动响应预检
location ~ ^/api/ {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin '*';
add_header Access-Control-Allow-Methods 'GET,POST,PUT,DELETE,OPTIONS';
add_header Access-Control-Allow-Headers 'Content-Type,Authorization,X-Requested-With';
add_header Access-Control-Max-Age 86400;
add_header Access-Control-Allow-Credentials 'true';
return 204;
}
proxy_pass http://backend;
# 其他 proxy 设置...
}
# 预检失败统一响应
location @cors_error {
internal;
add_header Content-Type 'application/json; charset=utf-8';
add_header Access-Control-Allow-Origin '*';
add_header Access-Control-Allow-Methods 'GET,POST,OPTIONS';
add_header Access-Control-Allow-Headers 'Content-Type,X-Requested-With';
return 400 '{"error":"Invalid CORS preflight request"}';
}}











