Nginx通过proxy_pass代理至Cloudflare仅在流量被代理时报错
Nginx转发Cloudflare代理端点触发403/421错误的解决办法
背景说明
我们有一批使用 vanity 域名的客户,原本都是把 vanity-url.com 通过CNAME解析到我们的 real-url.com。最近为了升级DDoS防护,引导他们把域名指向新的Cloudflare端点。子域名配置都没问题,但很多DNS服务商受RFC限制,不允许根域名设置CNAME记录。
为绕开这个限制,我搭了台Nginx服务器,用proxy_pass把流量转发到Cloudflare端点,但碰到了奇怪的问题:
- 当Cloudflare端点设为「DNS only」模式时,Nginx运行完全正常
- 一开启Cloudflare代理模式,先返回403 Forbidden,多试几次就变成421 Misdirected Request错误
我已经在Cloudflare里添加了对应的自定义主机名,证书和密钥也都配对了,按说SSL终止应该没问题,怀疑是Cloudflare的配置漏了什么,但摸不准。当前的Nginx配置如下:
worker_processes 1; events { worker_connections 1024; } http { server { server_name vanity-url.com; ssl_certificate vanity-url.com.crt; ssl_certificate_key vanity-url.com.key; listen 443 ssl; location / { proxy_pass https://cloudflare.real-url.com; proxy_set_header Host $host; proxy_ssl_server_name on; } } }
问题根源
421错误的本质是Cloudflare代理模式下会严格校验请求的Host头是否属于它托管的域名。现在你的Nginx把Host头设成了vanity-url.com,但Cloudflare的端点cloudflare.real-url.com默认只认自己的域名,或者已经绑定过的自定义主机名。另外,虽然开了proxy_ssl_server_name但没指定具体的SNI名称,Cloudflare可能没法正确识别请求目标,直接拒掉了。
解决方法
方法一:调整Nginx的代理配置
修改Nginx配置,重点处理Host头和SNI设置:
- 如果
vanity-url.com已经在Cloudflare里添加了自定义主机名,可保留proxy_set_header Host $host;如果没绑定,就把Host头改成Cloudflare端点的域名cloudflare.real-url.com - 加上
proxy_ssl_name明确指定SNI名称,确保Cloudflare能正确识别SSL请求目标
修改后的配置:
worker_processes 1; events { worker_connections 1024; } http { server { server_name vanity-url.com; ssl_certificate vanity-url.com.crt; ssl_certificate_key vanity-url.com.key; listen 443 ssl; location / { proxy_pass https://cloudflare.real-url.com; # 根据自定义主机名绑定情况二选一: # 已绑定vanity-url.com到Cloudflare端点时用下面这行 # proxy_set_header Host $host; # 未绑定时用端点域名 proxy_set_header Host cloudflare.real-url.com; proxy_ssl_server_name on; # 指定SNI为Cloudflare端点域名 proxy_ssl_name cloudflare.real-url.com; # 可选:把真实客户端IP传给Cloudflare,方便日志和防护 proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; } } }
方法二:核对Cloudflare自定义主机名配置
确认这几点:
vanity-url.com已经在Cloudflare控制台的「自定义主机名」页面添加完成- 自定义主机名的证书状态显示「有效」,并且和
cloudflare.real-url.com这个端点关联正确 - Cloudflare的「SSL/TLS」设置里,该域名的SSL模式选的是「灵活」或「严格」(如果Nginx已经做了SSL终止,选「灵活」即可)
方法三:直接用Cloudflare的根域名方案(更省心)
既然核心问题是根域名不能设CNAME,不如直接用Cloudflare的「CNAME扁平化」功能,让客户直接把根域名解析到Cloudflare:
- 让客户在自己的DNS服务商那里,把根域名的A记录指向Cloudflare的官方IP,或者如果服务商支持的话,直接用Cloudflare的CNAME扁平化功能
- 确保客户的域名已经加到Cloudflare并完成托管,这样Cloudflare会自动处理根域名的CNAME兼容问题,还能直接提供DDoS防护,不用再折腾中间的Nginx了
验证步骤
- 重启Nginx:
sudo nginx -s reload - 用curl测试:
curl -v https://vanity-url.com,查看返回的状态码和响应信息 - 去Cloudflare的「分析」或「日志」页面,确认请求是否被正常接收处理
内容的提问来源于stack exchange,提问作者Ryan Grush
相关产品推荐
相关产品推荐

