Golang HTTP over TLS场景自定义User-Agent请求头未被服务端接收问题
问题根因
该问题与自签名TLS证书无关,完全是客户端代码逻辑错误导致:你在请求已经发送完成后才设置请求头,且操作的对象是服务端返回的响应而非待发送的请求,因此服务端只能收到Go客户端默认的Go-http-client/1.1标识。
你客户端代码的错误点非常明确:
r, err := http.NewRequest("GET", serverDomain, nil) req, err := client.Do(r) // 这行执行完时,请求已经完整发到服务端了,此时请求头还是默认值 if err != nil { log.Fatal(err) } // 这里的req是*http.Response类型的服务端响应对象,不是请求!修改它的Header完全不会影响已经发出去的请求 req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.7113.93 Safari/537.36") defer req.Body.Close()
核心错误有两个:
- 执行顺序颠倒:请求头必须在调用
client.Do()发送请求之前设置,发完再改没有任何意义 - 变量命名混淆:把
client.Do()返回的响应对象命名为req,和前面创建的请求对象r混淆,后续操作完全搞错了对象
修复方案
调整客户端代码执行顺序,在发送请求前给请求对象设置Header,同时修正变量命名避免混淆,修复后的核心客户端代码如下:
// 1. 先读取证书、配置证书池的逻辑不变 cert, err := os.ReadFile(certPemPath) if err != nil { log.Fatal(err) } certPool := x509.NewCertPool() if ok := certPool.AppendCertsFromPEM(cert); !ok { log.Fatalf("unable to parse cert from %s", certPemPath) } // 2. 初始化HTTP客户端的逻辑不变 client := &http.Client{ Timeout: 2 * time.Second, Transport: &http.Transport{ DisableKeepAlives: false, TLSClientConfig: &tls.Config{ RootCAs: certPool, }, }, } // 3. 创建请求对象 r, err := http.NewRequest("GET", serverDomain, nil) if err != nil { log.Fatal(err) } // !!!关键:发送请求前,给请求对象设置自定义User-Agent r.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0 Waterfox/91.4.2") // 4. 发送请求拿到响应,注意这里把返回值命名为resp明确是响应对象 resp, err := client.Do(r) if err != nil { log.Fatal(err) } defer resp.Body.Close() // 5. 读取响应的逻辑不变 html, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%v\n", resp.Status) fmt.Printf(string(html))
补充说明
- 你的服务端代码没有问题,浏览器访问能正常拿到UA就是直接证明
- 自签名证书只要客户端正确将证书加入信任池,就不会影响HTTP请求头的传输,不需要怀疑证书配置导致头丢失
内容的提问来源于stack exchange,提问作者user19323515
相关产品推荐
相关产品推荐

