Nginx正向代理对接启用mTLS的AWS API Gateway返回400如何解决
Nginx正向代理对接mTLS版AWS API Gateway返回400问题修复
问题现象
- Nginx 1.18.0部署在EC2实例上作为正向代理,通过实例EIP满足合作方白名单要求,此前对接其他mTLS业务正常
- 本次对接合作方AWS API Gateway时,直接在EC2上用curl、openssl携带客户端证书访问接口正常,经Nginx代理访问始终返回HTTP 400
- 调整多版配置参数无效,因mTLS流量加密,tcpdump抓包无法获取有效报错信息
原问题配置
server { listen 11013; server_name localhost; access_log /var/log/nginx/forward_partner_x_nginx_proxy_access.log; error_log /var/log/nginx/forward_partner_x_nginx_proxy_error.log warn; proxy_ssl_certificate /root/partner_x_ssl/uat_chain.crt; proxy_ssl_certificate_key /root/partner_x_ssl/uat.key; resolver 8.8.8.8; set $partner_x_upstream https://api.partner_x.app; location /test { access_log off; return 200; } location / { proxy_set_header Host $proxy_host; proxy_pass $partner_x_upstream; #proxy_pass_request_headers on; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_ssl_protocols TLSv1.2; proxy_ssl_server_name on; proxy_buffering off; proxy_ssl_name xx.xx.xx.xx; #add_header Content-Type application/json; } }
核心配置错误点
proxy_ssl_name配置值错误:当前配置传递的是IP地址,AWS API Gateway的mTLS校验强依赖SNI字段匹配自定义域名,SNI传IP时,API Gateway无法路由到对应配置了mTLS的自定义域名实例,请求会落到未配置对应mTLS规则的兜底节点,直接返回400。- Host头传递错误:使用变量形式定义
proxy_pass上游地址时,$proxy_host取值逻辑存在兼容问题,无法正确传递API Gateway要求的自定义域名Host头,APIG收到Host头不匹配的请求会直接返回400。 - 客户端证书链顺序风险:Nginx不会自动调整客户端证书顺序,要求
proxy_ssl_certificate指定的文件中,证书顺序必须为「客户端实体证书 → 中间CA证书」,不能包含根证书。如果证书顺序颠倒、混入根证书,APIG校验客户端证书链失败也会返回400。curl能正常访问是因为curl会自动修正证书链顺序,和Nginx逻辑不同。
修复后可用配置
server { listen 11013; server_name localhost; access_log /var/log/nginx/forward_partner_x_nginx_proxy_access.log; # 排查阶段可将日志级别改为debug,定位完成后改回warn error_log /var/log/nginx/forward_partner_x_nginx_proxy_error.log warn; # 注意:该文件内证书顺序必须为 客户端实体证书 -> 中间CA证书,不要放入根证书 proxy_ssl_certificate /root/partner_x_ssl/uat_chain.crt; proxy_ssl_certificate_key /root/partner_x_ssl/uat.key; # 增加DNS解析缓存,避免解析抖动 resolver 8.8.8.8 valid=300s; set $partner_x_upstream https://api.partner_x.app; location /test { access_log off; return 200; } location / { # 显式传递APIG自定义域名作为Host头 proxy_set_header Host api.partner_x.app; proxy_pass $partner_x_upstream; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 增加TLSv1.3兼容,适配AWS APIG最新TLS配置 proxy_ssl_protocols TLSv1.2 TLSv1.3; proxy_ssl_server_name on; # 修正SNI字段值,和APIG自定义域名完全一致 proxy_ssl_name api.partner_x.app; proxy_buffering off; # 如需开启服务端证书校验,可放开以下两行配置,注意CA证书路径要和系统实际路径一致 # proxy_ssl_verify on; # proxy_ssl_trusted_certificate /etc/pki/tls/certs/ca-bundle.crt; } }
验证步骤
- 先校验客户端证书链顺序:执行命令
openssl crl2pkcs7 -nocrl -certfile /root/partner_x_ssl/uat_chain.crt | openssl pkcs7 -print_certs -noout,确认输出的证书顺序第一个是你的客户端实体证书,后续为签发的中间CA证书,无多余根证书。如果顺序不对,手动调整文件内证书顺序即可。 - 校验Nginx配置并重载:执行
nginx -t确认配置无语法错误后,执行nginx -s reload重载配置。 - 本地代理测试:在EC2实例上执行
curl -v http://127.0.0.1:11013/实际业务接口路径,查看返回结果是否正常。 - 如果仍有报错,将error_log级别调整为debug,重载后复现问题,在错误日志中搜索
SSL_do_handshake相关字段,即可看到TLS握手阶段的具体报错,无需抓加密流量包。
内容的提问来源于stack exchange,提问作者black sensei
相关产品推荐
相关产品推荐

