limit_except 的核心作用是白名单式允许指定HTTP方法,仅放行括号内列出的方法,其余返回405;必须置于location块内,不继承父配置,自动包含HEAD但需显式添加OPTIONS以支持跨域,仅校验请求方法字符串,不处理请求头或body,比if指令更安全可靠。

limit_except 的核心作用是“只放行列出的方法,其余一律拒绝”,它不是黑名单式禁止,而是白名单式允许——写在括号里的方法才被接受,没写的直接返回 405 Not Allowed。这个逻辑简单但非常关键,用错就等于把门敞开了。
必须放在 location 块内,不能单独存在
limit_except 不支持出现在 http 或 server 顶层,只能嵌套在某个 location 中。它依附于路径范围生效,脱离路径上下文就无效。
- ✅ 正确:location /api/users { limit_except GET POST { deny all; } }
- ❌ 错误:server { limit_except GET { deny all; } } —— Nginx 启动会报错
- ⚠️ 注意:它不继承父块的 limit_except 配置,每个 location 需独立设置
自动包含 HEAD,但 OPTIONS 必须显式添加
GET 自动带出 HEAD(因为 HEAD 是 GET 的无 body 版本),所以写 limit_except GET 就等同于允许 GET + HEAD。但跨域请求中的 OPTIONS 预检不会被自动包含,前端发 PUT/DELETE 时若没放行 OPTIONS,预检失败,主请求根本到不了后端。
- 需要支持跨域写操作?必须把 OPTIONS 加进列表:limit_except GET POST PUT DELETE OPTIONS
- 也可以单独处理 OPTIONS:if ($request_method = 'OPTIONS') { add_header Access-Control-Allow-Origin "*"; return 204; },但统一放进 limit_except 更简洁、更少歧义
只校验请求行 method 字符串,不碰请求头和 body
limit_except 是纯方法级拦截,发生在请求解析早期,不读取也不校验请求头、参数、Cookie 或请求体内容。它不管你是带着 token 还是空 body,只要 method 不在白名单里,立刻拦下。
- POST 请求含 body?不影响判断,只看是不是 POST
- 想结合用户权限控制?得搭配 auth_basic 或 JWT 验证模块,limit_except 不负责身份层面的事
- 想限制上传大小或超时?那是 client_max_body_size 或 client_header_timeout 的事,和 limit_except 无关
比 if + $request_method 更安全可靠
有人习惯用 if ($request_method != 'GET') { deny all; } 实现类似效果,但这有隐患:
- if 在 location 中属于“伪指令”,行为不稳定,尤其和 deny、rewrite 等混用时容易触发意外跳转或配置失效
- limit_except 是专为该场景设计的原生命令,语义明确、执行路径清晰、无副作用
- 它完全兼容 location 内其他指令(比如 proxy_pass、add_header、auth_basic),不会打断配置链


















