Nginx SSL与Varnish搭配异常,报400错误求助
问题:Nginx+Varnish配置出现400 Bad Request(HTTP请求发送到HTTPS端口)
环境:Ubuntu 20.04.6服务器,带SSL的Nginx,Varnish 6.2.1
报错内容:
400 Bad Request The plain HTTP request was sent to HTTPS port nginx/1.18.0 (Ubuntu)
现有配置文件
Nginx站点配置(/etc/nginx/sites-available/file)
server { listen 91 ssl http2; server_name my_site_name; # varnish proxy location / { proxy_pass http://127.0.0.1:6081; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } ssl_certificate /etc/letsencrypt/live/my_site_name/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/my_site_name/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot }
Varnish配置(/etc/varnish/default.vcl)
vcl 4.0; backend default { .host = "my_https_site"; .port = "85"; } sub vcl_recv { if (client.ip != "127.0.0.1" && req.http.host ~ "my_host.by") { set req.http.x-redir = "https://my_https_site" + req.url; return(synth(850, "")); } } sub vcl_deliver { if (resp.status == 850) { set resp.http.Location = req.http.x-redir; set resp.status = 301; return (deliver); } }
Varnish启动配置(/etc/default/varnish)
DAEMON_OPTS="-a :6081 \ -T localhost:6082 \ -f /etc/varnish/default.vcl \ -S /etc/varnish/secret \ -s malloc,256m"
日志信息
Varnish后端日志(varnishlog -b)
* << BeReq >> 98394 - Begin bereq 98393 fetch - VCL_use boot - Timestamp Start: 1682520086.557505 0.000000 0.000000 - BereqMethod GET - BereqURL / - BereqProtocol HTTP/1.1 - BereqHeader Host: my_host - BereqHeader sec-ch-ua: "Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99" - BereqHeader sec-ch-ua-mobile: ?0 - BereqHeader sec-ch-ua-platform: "Linux" - BereqHeader upgrade-insecure-requests: 1 - BereqHeader user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 - BereqHeader accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 - BereqHeader sec-fetch-site: none - BereqHeader sec-fetch-mode: navigate - BereqHeader sec-fetch-user: ?1 - BereqHeader sec-fetch-dest: document - BereqHeader accept-language: en-US,en;q=0.9,ru;q=0.8 - BereqHeader X-Forwarded-For: 127.0.0.1 - BereqHeader Accept-Encoding: gzip - BereqHeader X-Varnish: 98394 - VCL_call BACKEND_FETCH - VCL_return fetch - BackendOpen 26 default 181.122.19.2 85 181.122.19.2 33736 - BackendStart 181.122.19.2 85 - Timestamp Bereq: 1682520086.557843 0.000338 0.000338 - Timestamp Beresp: 1682520086.558193 0.000688 0.000350 - BerespProtocol HTTP/1.1 - BerespStatus 400 - BerespReason Bad Request - BerespHeader Server: nginx/1.18.0 (Ubuntu) - BerespHeader Date: Wed, 26 Apr 2023 14:41:26 GMT - BerespHeader Content-Type: text/html - BerespHeader Content-Length: 666 - BerespHeader Connection: close - TTL RFC -1 10 0 1682520087 1682520087 1682520086 0 0 cacheable - VCL_call BACKEND_RESPONSE - TTL VCL 120 10 0 1682520087 cacheable - TTL VCL 120 10 0 1682520087 uncacheable - VCL_return deliver - Filters - Storage malloc Transient - Fetch_Body 3 length stream - BackendClose 26 default - Timestamp BerespBody: 1682520086.558474 0.000969 0.000281 - Length 666 - BereqAcct 657 0 657 161 666 827 - End
客户端请求日志
* << Request >> 98399 - Begin req 98398 rxreq - Timestamp Start: 1682521060.864589 0.000000 0.000000 - Timestamp Req: 1682521060.864589 0.000000 0.000000 - VCL_use boot - ReqStart 127.0.0.1 35458 a0 - ReqMethod GET - ReqURL /favicon.ico - ReqProtocol HTTP/1.1 - ReqHeader Connection: upgrade - ReqHeader Host: my_host - ReqHeader sec-ch-ua: "Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99" - ReqHeader sec-ch-ua-mobile: ?0 - ReqHeader user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 - ReqHeader sec-ch-ua-platform: "Linux" - ReqHeader accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8 - ReqHeader sec-fetch-site: same-origin - ReqHeader sec-fetch-mode: no-cors - ReqHeader sec-fetch-dest: image - ReqHeader referer: https://alva.by:91/ - ReqHeader accept-encoding: gzip, deflate, br - ReqHeader accept-language: en-US,en;q=0.9,ru;q=0.8 - ReqHeader X-Forwarded-For: 127.0.0.1 - VCL_call RECV - VCL_return hash - ReqUnset accept-encoding: gzip, deflate, br - ReqHeader Accept-Encoding: gzip - VCL_call HASH - VCL_return lookup - VCL_call MISS - VCL_return fetch - Link bereq 98400 fetch - Timestamp Fetch: 1682521060.865292 0.000702 0.000702 - RespProtocol HTTP/1.1 - RespStatus 400 - RespReason Bad Request - RespHeader Server: nginx/1.18.0 (Ubuntu) - RespHeader Date: Wed, 26 Apr 2023 14:57:40 GMT - RespHeader Content-Type: text/html - RespHeader Content-Length: 666 - RespHeader X-Varnish: 98399 - RespHeader Age: 0 - RespHeader Via: 1.1 varnish (Varnish/6.2) - VCL_call DELIVER - VCL_return deliver - Timestamp Process: 1682521060.865304 0.000715 0.000012 - Filters - RespHeader Connection: keep-alive - Timestamp Resp: 1682521060.865364 0.000775 0.000061 - ReqAcct 568 0 568 224 666 890 - End
解决方案
问题根源
从日志可见:Varnish以纯HTTP协议向后端my_https_site:85发送请求,但该端口的Nginx配置为HTTPS服务,因此返回400错误。
方案1:让Varnish用HTTPS请求后端
修改/etc/varnish/default.vcl,为后端启用SSL连接:
vcl 4.0; backend default { .host = "my_https_site"; .port = "85"; .ssl = true; # 开启SSL连接 .ssl_sni = "my_https_site"; # 指定SNI,适配多域名SSL证书 } # 保留原有重定向逻辑 sub vcl_recv { if (client.ip != "127.0.0.1" && req.http.host ~ "my_host.by") { set req.http.x-redir = "https://my_https_site" + req.url; return(synth(850, "")); } } sub vcl_deliver { if (resp.status == 850) { set resp.http.Location = req.http.x-redir; set resp.status = 301; return (deliver); } }
修改后重启Varnish:
systemctl restart varnish
方案2:调整后端Nginx,让端口85接收HTTP请求
如果后端无需在端口85启用SSL,修改对应Nginx站点配置,将listen 85 ssl http2;改为listen 85;,并移除该server块内的SSL相关配置(证书、ssl_dhparam等),然后重启Nginx:
systemctl restart nginx
额外检查项
- 用
curl -v http://my_https_site:85测试后端端口,若返回400则说明是HTTPS端口,需用方案1 - Varnish 6.0+原生支持HTTPS后端,6.2.1版本无需额外依赖
- 若后端是自签名SSL证书,需在Varnish后端配置中添加
.ssl_verify_depth和.ssl_ca指定证书路径,Let's Encrypt证书无需额外配置
内容的提问来源于stack exchange,提问作者Sulti
相关产品推荐
相关产品推荐

