使用Nginx作为反向代理调用API时出现502 Bad Gateway错误
问题:Nginx反向代理SSL握手失败返回502,但Golang客户端直接调用正常
我尝试用Nginx做反向代理对接外部REST GET API,用来调试Golang服务和外部服务之间的延迟问题。Golang通过net/http库直接调用目标公开URL能正常获取响应,但走Nginx反向代理时却返回502 Bad Gateway。Nginx错误日志显示SSL握手失败(错误码SSL: error:0A000438),但我搞不懂为什么Golang客户端调用没问题。另外已经通过/ping-nginx接口验证Nginx本身运行正常,相关代码、测试响应及日志如下:
相关代码与输出
1. Golang服务代码(ping.go)
package main import ( "fmt" "log" "net/http" "strconv" ) func main() { http.HandleFunc("/ping-via-go-client", func(w http.ResponseWriter, r *http.Request) { url := "https://flights-explorer.makemytrip.com/ping" resp, err := http.Get(url) var statusCode string if err == nil { statusCode = strconv.Itoa(resp.StatusCode) } fmt.Fprintf(w, "pong-via-go-client with statusCode:"+statusCode) }) http.HandleFunc("/ping-via-nginx", func(w http.ResponseWriter, r *http.Request) { url := "http://localhost/ping" resp, err := http.Get(url) var statusCode string if err == nil { statusCode = strconv.Itoa(resp.StatusCode) } fmt.Fprintf(w, "pong-via-nginx with statusCode:"+statusCode) }) log.Fatal(http.ListenAndServe(":3003", nil)) }
2. curl测试响应
➜ ~ curl http://localhost:3003/ping-via-go-client pong-via-go-client with statusCode:200% ➜ ~ curl http://localhost:3003/ping-via-nginx pong-via-nginx with statusCode:502%
3. Nginx配置(nginx.conf)
#user nginx; daemon off; worker_processes auto; error_log /opt/logs/nginx-error.log warn; pid /var/run/nginx.pid; events { worker_connections 250; } http { log_format upstream_time '$time_local $status $remote_addr to:- $upstream_addr $request ' 'uct:$upstream_connect_time uht:$upstream_header_time urt:$upstream_response_time ' 'request_time:$request_time tid_header:$http_tid status:$upstream_cache_status ' 'slot:$http_slot slot_time:$http_slotstarttime ttl_req:$http_ttl ttl_resp:$upstream_http_x_accel_expires ' 'job_flag:$http_jobflag cookies:"$http_cookie" bytes_sent:$bytes_sent gzip_ratio:$gzip_ratio ' '"$http_referer" "$http_user_agent" $http_x_forwarded_for cur_time:$msec'; keepalive_timeout 85; upstream flights-explorer.makemytrip.com { server flights-explorer.makemytrip.com:443; keepalive 30; } server { listen 80; access_log /opt/logs/nginx-access.log upstream_time; location = /basic_status { stub_status; } location /ping-nginx { return 200 'pong-nginx\n'; add_header Content-Type text/plain; } location /ping { # proxy_buffering off; proxy_pass https://flights-explorer.makemytrip.com$request_uri; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_ssl_verify off; # Disable SSL certificate verification proxy_ssl_verify_depth 0; #proxy_ssl_session_reuse on; #proxy_socket_keepalive on; proxy_connect_timeout 10s; proxy_read_timeout 10s; } } }
4. Nginx错误日志(nginx-error.log)
2024/03/03 11:07:55 [error] 20078#0: *6 SSL_do_handshake() failed (SSL: error:0A000438:SSL routines::tlsv1 alert internal error:SSL alert number 80) while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.25:443/ping", host: "localhost" 2024/03/03 11:07:55 [warn] 20078#0: *6 upstream server temporarily disabled while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.25:443/ping", host: "localhost" 2024/03/03 11:07:55 [error] 20078#0: *6 SSL_do_handshake() failed (SSL: error:0A000438:SSL routines::tlsv1 alert internal error:SSL alert number 80) while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.67:443/ping", host: "localhost" 2024/03/03 11:07:55 [warn] 20078#0: *6 upstream server temporarily disabled while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.67:443/ping", host: "localhost" 2024/03/03 11:07:56 [error] 20078#0: *6 SSL_do_handshake() failed (SSL: error:0A000438:SSL routines::tlsv1 alert internal error:SSL alert number 80) while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.74:443/ping", host: "localhost" 2024/03/03 11:07:56 [warn] 20078#0: *6 upstream server temporarily disabled while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://23.63.110.74:443/ping", host: "localhost"
5. Nginx访问日志(nginx-access.log)
03/Mar/2024:11:07:56 +0530 502 127.0.0.1 to:- 23.63.110.25:443, 23.63.110.67:443, 23.63.110.74:443 GET /ping HTTP/1.1 uct:-, -, - uht:-, -, - urt:0.131, 0.116, 0.107 request_time:0.354 tid_header:- status:- slot:- slot_time:- ttl_req:- ttl_resp:- job_flag:- cookies:"-" bytes_sent:314 gzip_ratio:- "-" "Go-http-client/1.1" - cur_time:1709444276.083
问题分析与解决方法
核心原因
目标服务器flights-explorer.makemytrip.com对SSL握手的**SNI(服务器名称指示)**有严格要求:Golang的net/http客户端默认会自动发送SNI信息,而你的Nginx配置中未启用SNI传递,导致握手时没有告知目标服务器要访问的域名,触发了SSL内部错误(alert 80)。
验证方式
用openssl测试不带SNI的握手,会出现相同错误:
openssl s_client -connect flights-explorer.makemytrip.com:443 -servername ""
带SNI的请求则会成功:
openssl s_client -connect flights-explorer.makemytrip.com:443 -servername flights-explorer.makemytrip.com
修复方案
在Nginx的/ping location中添加proxy_ssl_server_name on;,确保SSL握手时传递正确的SNI:
location /ping { proxy_pass https://flights-explorer.makemytrip.com$request_uri; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_ssl_verify off; proxy_ssl_verify_depth 0; proxy_ssl_server_name on; # 启用SNI传递 proxy_connect_timeout 10s; proxy_read_timeout 10s; }
另外,你定义的upstream块当前未被使用,可改为通过upstream转发,配置更规范:
upstream flights_upstream { server flights-explorer.makemytrip.com:443; keepalive 30; } location /ping { proxy_pass https://flights_upstream$request_uri; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_ssl_verify off; proxy_ssl_verify_depth 0; proxy_ssl_server_name on; proxy_connect_timeout 10s; proxy_read_timeout 10s; }
内容的提问来源于stack exchange,提问作者Sharath BJ
相关产品推荐
相关产品推荐

