You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nginx反向代理下部分主机LetsEncrypt ACME挑战报400错误排查

Nginx Docker容器ACME挑战400错误排查

我用Nginx Docker容器路由所有HTTP/S请求,HTTPS运行正常,但近期部分主机名的LetsEncrypt ACME挑战失败,Certbot等客户端访问时收到错误:400 Bad Request Request Header Or Cookie Too Large。

  • 仅默认映射的站点出现该问题:访问站点根目录可正常重定向到HTTPS,访问/.well-known/acme-challenge/根目录返回预期404;
  • 非默认映射的主机ACME挑战可正常完成;
  • 已尝试常见修复(增大请求头配置等)无效,调试日志无有效信息,且请求包总大小不足400字节,判断该400错误并非问题本质,求排查思路。

Nginx完整配置(nginx -T输出)

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# configuration file /etc/nginx/nginx.conf:
user  nginx;
worker_processes  auto;

pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;

  log_format  main  '$proxy_protocol_addr [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent"';

  access_log  /var/log/nginx/access.log  main;
  error_log   /var/log/nginx/error.log;

  sendfile        on;
  #tcp_nopush     on;

  keepalive_timeout  65;

  #gzip  on;

  resolver 127.0.0.11 valid=30s;

  map $host $name {
    default doklaba.example.com:80;             <--- Failing Mapping
    ~*^pf.+\.example\.com $host:8080;           <--- Working
    ~*^pve.+\.example\.com $host:80;            <--- Working
  }

  server {
    listen 80 default_server;
    listen [::]:80 default_server;

    location ^~ /.well-known/acme-challenge/ {
      proxy_pass http://$name$request_uri;
    }

    location = /.well-known/acme-challenge/ {
      return 404;
    }

    location / {
      return 301 https://$host$request_uri;
    }
  }

  server {
    listen 444 ssl proxy_protocol;
    listen [::]:444 ssl proxy_protocol;
    server_name speed.example.com speed.ext.example.com;

    location / {
      proxy_pass http://Speedtest.:3000;
    }

    ssl_certificate /etc/letsencrypt/npm-59/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/npm-59/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
  }

  server {
    listen 444 ssl proxy_protocol;
    listen [::]:444 ssl proxy_protocol;
    server_name vpn.example.com;

    location / {
      proxy_pass https://vpn.example.com;
    }

    ssl_certificate /etc/letsencrypt/npm-92/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/npm-92/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
  }
}

stream {
  resolver 127.0.0.11 valid=30s;

  upstream https {
    server localhost:444;
  }

  upstream signal {
    server localhost:4433;
  }

  map $ssl_preread_server_name $upstream {
    signal.example.com signal;
    default https;
  }

  log_format basic '$remote_addr [$time_local] $ssl_preread_server_name '
                   '$protocol $status $bytes_sent $bytes_received '
                   '$session_time';
#  access_log /var/log/nginx/access.log basic;
#  error_log /var/log/nginx/error.log;
  access_log off;
  error_log /dev/null;

  server {
    listen 443;
    listen [::]:443;
    proxy_pass $upstream;
    proxy_protocol on;
    ssl_preread on;
  }

  server {
    listen 4433 ssl proxy_protocol;
    listen [::]:4433 ssl proxy_protocol;
    proxy_pass Signal.:4433;
    ssl_preread on;

    ssl_certificate /etc/letsencrypt/npm-101/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/npm-101/privkey.pem;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
  }
}

调试日志

2025/03/11 15:04:46 [notice] 1#1: using the "epoll" event method
2025/03/11 15:04:46 [notice] 1#1: nginx/1.27.4
2025/03/11 15:04:46 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
2025/03/11 15:04:46 [notice] 1#1: OS: Linux 5.15.0-133-generic
2025/03/11 15:04:46 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2025/03/11 15:04:46 [notice] 1#1: start worker processes
2025/03/11 15:04:46 [notice] 1#1: start worker process 22
2025/03/11 15:05:11 [notice] 1#1: signal 3 (SIGQUIT) received, shutting down
2025/03/11 15:05:11 [notice] 22#22: gracefully shutting down
2025/03/11 15:05:11 [notice] 22#22: exiting
2025/03/11 15:05:11 [notice] 22#22: exit
2025/03/11 15:05:11 [notice] 1#1: signal 17 (SIGCHLD) received from 22
2025/03/11 15:05:11 [notice] 1#1: worker process 22 exited with code 0
2025/03/11 15:05:11 [notice] 1#1: exit
2025/03/11 15:05:11 [notice] 1#1: using the "epoll" event method
2025/03/11 15:05:11 [notice] 1#1: nginx/1.27.4
2025/03/11 15:05:11 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
2025/03/11 15:05:11 [notice] 1#1: OS: Linux 5.15.0-133-generic
2025/03/11 15:05:11 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2025/03/11 15:05:11 [notice] 1#1: start worker processes
2025/03/11 15:05:11 [notice] 1#1: start worker process 21
2025/03/11 15:07:33 [notice] 1#1: signal 3 (SIGQUIT) received, shutting down
2025/03/11 15:07:33 [notice] 21#21: gracefully shutting down
2025/03/11 15:07:33 [notice] 21#21: exiting
2025/03/11 15:07:33 [notice] 21#21: exit
2025/03/11 15:07:33 [notice] 1#1: signal 17 (SIGCHLD) received from 21
2025/03/11 15:07:33 [notice] 1#1: worker process 21 exited with code 0
2025/03/11 15:07:33 [notice] 1#1: exit

客户端请求头配置

http {
  client_body_buffer_size     32k;
  client_header_buffer_size   8k;
  large_client_header_buffers 8 64k;
  ...
}

排查思路

  1. 验证默认映射后端服务的响应

    • 直接访问doklaba.example.com:80的ACME挑战路径,确认是否是后端本身返回400错误。若后端返回错误,问题不在Nginx,需检查后端的请求头限制、Cookie处理逻辑。
    • 在ACME挑战的location中添加proxy_set_header Host $host;,因为ACME挑战要求Host头匹配请求域名,默认映射的后端是固定域名,可能因Host不匹配返回伪装成400的错误。
  2. 检查Nginx变量传递与循环代理

    • 在access_log中添加$name字段,确认默认映射场景下变量是否正确解析为doklaba.example.com:80:
      log_format  main  '$proxy_protocol_addr [$time_local] "$request" $name '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent"';
      
    • 排查是否存在循环代理:若doklaba.example.com的80端口反向代理回当前Nginx容器,会触发请求循环,导致异常400错误。
  3. 排查DNS解析问题

    • 进入Nginx容器执行nslookup doklaba.example.com,确认Docker内置DNS(127.0.0.11)能否正常解析该域名,若解析失败或指向错误IP,会导致代理请求异常。
  4. 调整代理请求头传递策略

    • 在ACME挑战的location中清空不必要的请求头,避免后端服务拒绝:
      location ^~ /.well-known/acme-challenge/ {
        proxy_pass http://$name$request_uri;
        proxy_set_header Cookie "";
        proxy_set_header Referer "";
      }
      
  5. 启用debug级日志

    • 将error_log级别改为debug:error_log /var/log/nginx/error.log debug;,重新触发ACME挑战,查看代理请求时的连接错误、后端响应细节等日志。
  6. 绕过Nginx直接测试

    • 临时将目标域名的80端口直接指向后端服务,手动执行ACME挑战验证。若成功,说明问题出在Nginx的代理配置上。

内容的提问来源于stack exchange,提问作者MeCJay12

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:35:54