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; ... }
排查思路
验证默认映射后端服务的响应
- 直接访问
doklaba.example.com:80的ACME挑战路径,确认是否是后端本身返回400错误。若后端返回错误,问题不在Nginx,需检查后端的请求头限制、Cookie处理逻辑。 - 在ACME挑战的
location中添加proxy_set_header Host $host;,因为ACME挑战要求Host头匹配请求域名,默认映射的后端是固定域名,可能因Host不匹配返回伪装成400的错误。
- 直接访问
检查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错误。
- 在access_log中添加
排查DNS解析问题
- 进入Nginx容器执行
nslookup doklaba.example.com,确认Docker内置DNS(127.0.0.11)能否正常解析该域名,若解析失败或指向错误IP,会导致代理请求异常。
- 进入Nginx容器执行
调整代理请求头传递策略
- 在ACME挑战的
location中清空不必要的请求头,避免后端服务拒绝:location ^~ /.well-known/acme-challenge/ { proxy_pass http://$name$request_uri; proxy_set_header Cookie ""; proxy_set_header Referer ""; }
- 在ACME挑战的
启用debug级日志
- 将error_log级别改为debug:
error_log /var/log/nginx/error.log debug;,重新触发ACME挑战,查看代理请求时的连接错误、后端响应细节等日志。
- 将error_log级别改为debug:
绕过Nginx直接测试
- 临时将目标域名的80端口直接指向后端服务,手动执行ACME挑战验证。若成功,说明问题出在Nginx的代理配置上。
内容的提问来源于stack exchange,提问作者MeCJay12
相关产品推荐
相关产品推荐

