应在nginx 80端口server块中用if+set变量提前判断并排除特定路径(如/.well-known),再统一执行301跳转,避免acme验证失败;推荐使用map指令定义跳转开关以提升可维护性。

要在 Nginx 的 HTTPS 重定向中忽略特定目录(比如 .well-known),关键不是“让目录不跳转”,而是**在跳转逻辑执行前,先识别并排除这些路径**。否则 ACME 验证、健康检查等 HTTP 请求会被 301 强制跳到 HTTPS,导致失败。
推荐做法:用 if + set 变量控制跳转开关
这是最稳妥、兼容性好、不易出错的方式。把是否跳转变成一个可动态设置的开关:
- 在监听 80 端口的
server块开头,先设默认跳转标志:set $should_redirect "1"; - 用
if检查请求 URI 是否匹配豁免路径,命中则关掉开关:if ($request_uri ~ ^/.well-known/) { set $should_redirect "0"; } - 再确认当前确实是 80 端口(避免 HTTPS server 块误触发):
if ($server_port = 80) { set $should_redirect "${should_redirect}1"; } - 最后统一判断:
if ($should_redirect = "11") { return 301 https://$host$request_uri; }
这个 “11” 是个技巧:确保只有真正需要跳转的请求才满足条件,规避了 Nginx 中 if 嵌套不可靠的问题。
多目录排除写法
要同时放过多个路径(如 .well-known、/healthz、/debug),只需扩展正则:
if ($request_uri ~ ^/(\.well-known|healthz|debug)/ ) { set $should_redirect "0"; }- 注意点号
.必须转义为\.,否则会匹配任意字符 -
^/保证从 URI 开头匹配,防止误伤类似/api/.well-known这样的子路径
常见错误要避开
有些看似合理的写法实际隐患很大:
- 别在
location / { }里写rewrite:容易和 PHP、静态文件等其他 location 冲突 - 别只配
alias或root给.well-known:这只是解决文件能访问,但跳转逻辑仍会先执行 - 别依赖
$scheme判断:HTTP 请求下它永远是http,无法用于“排除”逻辑 - 测试时别只看浏览器:301 被强缓存,建议用
curl -I http://domain/.well-known/...直接验证响应头
替代方案:用 map 定义跳转开关(适合复杂规则)
如果豁免规则较多或需复用,可在 http 块顶部定义:
map $request_uri $skip_https_redirect { default 1; ~^/\.well-known/ 0; ~^/healthz 0; }- 然后在 80 端口 server 中:
if ($skip_https_redirect = 1) { return 301 https://$host$request_uri; } - 这种方式更清晰、易维护,也支持正则和前缀匹配
无论选哪种,核心原则一致:跳转决策必须发生在所有 location 匹配之前,且以请求路径为依据,而非响应内容或后端行为。











