必须用 $request_method 因其是 nginx 内置只读变量,值恒为大写 http 方法名,在请求解析初期即确定,稳定无歧义、不受重写或请求体影响,是唯一能 100% 确认浏览器预检请求的依据;其他变量如 $request_uri 或 $http_access_control_request_method 均存在匹配不可靠或伪造风险。

直接用 if ($request_method = 'OPTIONS') 判断是最常用也最有效的识别方式,它在请求进入代理链前就完成匹配,不依赖后端、不消耗业务资源。
为什么必须用 $request_method 而不是其他变量
$request_method 是 Nginx 内置的只读变量,值为大写的 HTTP 方法名(如 GET、POST、OPTIONS),在请求解析阶段就已确定,稳定且无歧义。它不随 URI 重写或内部跳转改变,也不受请求体内容影响——这正是预检请求识别所需的关键特性。
- 不能用
$request_uri或$args匹配,因为 OPTIONS 请求通常无查询参数、URI 简单,且浏览器不保证路径唯一性 - 避免用
$http_access_control_request_method单独判断,它只存在于预检请求中,但无法区分是否真是预检(比如客户端伪造) -
$request_method是唯一能 100% 确认“这是浏览器发来的预检请求”的依据
if 指令必须放在 location 块内才生效
if 指令在 Nginx 中有作用域限制:只能出现在 server 或 location 块中,且不能嵌套。处理跨域时,应将它写在具体 API 路径的 location 块里,例如 location /api/ { ... }。
- 放在
server块顶层会导致所有路径(包括静态资源)都拦截 OPTIONS,可能干扰前端构建产物或监控探针 - 若使用
location ~ \.php$这类正则匹配,需确认 OPTIONS 也能命中;更稳妥的是用前缀匹配 + 显式if - Nginx 1.19+ 支持
location = /health精确匹配,但预检请求路径与主请求一致,所以仍需在通用路径块中处理
搭配 return 204 才算真正终止处理
仅写 if ($request_method = 'OPTIONS') { add_header ... } 不够——头信息加了,但请求还会继续走 proxy_pass,后端仍会收到一次 OPTIONS,白白增加延迟和负载。
- 必须跟
return 204;,让 Nginx 立即返回空响应并退出当前请求处理流程 - 状态码选 204(No Content)而非 200:语义准确,部分浏览器对 200 + 空体的 OPTIONS 响应缓存行为不一致
-
return 204之后的所有指令(包括proxy_pass、rewrite)都会被跳过,零转发、零日志、零后端调用
add_header 必须带 always 参数才能作用于 204 响应
默认情况下,add_header 只对 2xx 和 3xx 的成功响应生效。而 204 属于 2xx,看似没问题——但 Nginx 的实现机制中,return 触发的响应属于“特殊构造响应”,普通 add_header 可能不生效。
- 显式加上
always参数:如add_header Access-Control-Allow-Origin "*" always; - 否则即使写了 header,在 curl -I 或浏览器 Network 面板里也看不到,导致预检失败
- 所有 CORS 相关头(Allow-Origin、Allow-Methods、Allow-Headers、Max-Age、Credentials)都要加
always











