如何使用Go代理HTTPS请求?现有实现HTTP请求可正常工作
问题根本原因
你碰到的错误本质是两种代理请求的逻辑差异:
- HTTP代理请求:客户端直接发送带完整URL的GET/POST请求,
Request.URL自带http://scheme,完全符合ReverseProxy的预期 - HTTPS代理请求:客户端会先发送CONNECT请求,这个请求的
Request.URL只有host:port格式的地址,没有协议前缀,直接喂给ReverseProxy就会触发unsupported protocol scheme ""的报错。你之前直接改请求方法、硬拼前缀的操作无效,是因为CONNECT请求本身的作用是建立传输隧道,并不是要转发HTTP请求内容,不符合普通转发的参数规则。
可行解决方案
根据你的两层代理转发需求,推荐优先使用隧道代理方案,不需要解密HTTPS流量,完全符合协议规范:
方案1:隧道代理实现(适配两层代理场景)
核心逻辑是区分普通HTTP请求和CONNECT请求,两类请求分开处理:
- 非CONNECT的普通HTTP请求,继续走原来的
ReverseProxy转发逻辑 - CONNECT请求单独处理,建立客户端到上层代理的透传隧道,不需要走ReverseProxy
参考Go实现代码:
func (p *CustomProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { // 单独处理HTTPS的CONNECT请求 if r.Method == http.MethodConnect { // 连接你的上层代理服务器 upstreamConn, err := net.Dial("tcp", "你的上层代理地址:端口") if err != nil { http.Error(w, err.Error(), http.StatusServiceUnavailable) return } // 告知客户端隧道已建立 w.WriteHeader(http.StatusOK) // 劫持当前HTTP连接的底层TCP连接 hijacker, ok := w.(http.Hijacker) if !ok { http.Error(w, "协议不支持", http.StatusInternalServerError) _ = upstreamConn.Close() return } clientConn, _, err := hijacker.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusServiceUnavailable) _ = upstreamConn.Close() return } // 双向透传流量,后续TLS握手、HTTPS数据直接转发无需处理 go io.Copy(upstreamConn, clientConn) go io.Copy(clientConn, upstreamConn) return } // 普通HTTP请求走原有ReverseProxy逻辑 p.ReverseProxy.ServeHTTP(w, r) }
注意:不要尝试修改CONNECT请求的Method或者硬拼URL Scheme,这不符合HTTP代理的协议规范,必然会触发参数校验错误。
方案2:HTTPS内容解密转发(需要配置证书)
如果你的场景需要拿到HTTPS明文请求做内容修改、审计等操作,可以用这个方案:
- 生成自签名CA证书,客户端需要信任这个CA证书
- 收到CONNECT请求后返回200响应,然后用CA证书动态为请求域名签发服务端证书,和客户端完成TLS握手
- 解密后拿到的HTTP请求就会和普通HTTP请求一致,自带完整的
https://前缀和路径,直接用ReverseProxy转发即可。
内容的提问来源于stack exchange,提问作者Kevin Jiang
相关产品推荐
相关产品推荐

