Golang 隧道代理:net/http 与 resty 四种 IP 控制场景

Go 语言标准库 net/http 和第三方库 resty 实现四种隧道代理场景,Transport.ProxyConnectHeader 原生支持 HTTPS Proxy-Tunnel。

亿牛云技术团队2026年5月29日1 分钟阅读

Go 的代理支持

Go 标准库 net/http 通过 Transport.ProxyConnectHeader 原生支持在 HTTPS CONNECT 阶段注入自定义头——这是 Go 相比 Python requests 的优势所在,无需自定义 Adapter。

环境准备

export PROXY_HOST=t.16yun.cn
export PROXY_PORT=31111
export PROXY_USERNAME=your-username
export PROXY_PASSWORD=your-password
export PROXY_TUNNEL=golang-demo-12345

方案一:net/http 标准库

场景 A:强制切换 IP

package main

import (
    "crypto/tls"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "time"
)

func getenv(key, def string) string {
    if v := os.Getenv(key); v != "" { return v }
    return def
}

func main() {
    host := getenv("PROXY_HOST", "t.16yun.cn")
    port := getenv("PROXY_PORT", "31111")
    user := getenv("PROXY_USERNAME", "user")
    pass := getenv("PROXY_PASSWORD", "password")

    proxyURL, _ := url.Parse(fmt.Sprintf("http://%s:%s", host, port))
    target := getenv("TARGET_URL", "https://httpbin.org/ip")

    for i := 1; i <= 3; i++ {
        tr := &http.Transport{
            Proxy:             http.ProxyURL(proxyURL),
            TLSClientConfig:   &tls.Config{InsecureSkipVerify: false},
            DisableKeepAlives: true,
        }
        client := &http.Client{Transport: tr, Timeout: 15 * time.Second}

        req, _ := http.NewRequest("GET", target, nil)
        req.Header.Set("Connection", "close")
        req.SetBasicAuth(user, pass)

        resp, _ := client.Do(req)
        body, _ := io.ReadAll(resp.Body)
        resp.Body.Close()

        var data map[string]interface{}
        json.Unmarshal(body, &data)
        fmt.Printf("请求 %d: %v\n", i, data["origin"])
    }
}

场景 B:Keep-Alive 保持相同 IP

// 复用 Transport 和 Client
tr := &http.Transport{
    Proxy:             http.ProxyURL(proxyURL),
    TLSClientConfig:   &tls.Config{InsecureSkipVerify: false},
    DisableKeepAlives: false,
    MaxIdleConns:      10,
    IdleConnTimeout:   30 * time.Second,
}
client := &http.Client{Transport: tr, Timeout: 15 * time.Second}

for i := 1; i <= 3; i++ {
    req, _ := http.NewRequest("GET", target, nil)
    req.Header.Set("Connection", "keep-alive")
    req.SetBasicAuth(user, pass)
    resp, _ := client.Do(req)
    body, _ := io.ReadAll(resp.Body)
    resp.Body.Close()
    var data map[string]interface{}
    json.Unmarshal(body, &data)
    fmt.Printf("请求 %d: %v\n", i, data["origin"])
}

场景 C-HTTPS:Proxy-Tunnel 固定 IP(HTTPS 目标)

这是 Go 的核心优势——通过 Transport.ProxyConnectHeader 在 CONNECT 阶段注入头:

tunnelVal := getenv("PROXY_TUNNEL", "golang-demo")

connectHeaders := http.Header{}
connectHeaders.Set("Proxy-Tunnel", tunnelVal)

tr := &http.Transport{
    Proxy:              http.ProxyURL(proxyURL),
    ProxyConnectHeader: connectHeaders, // 这在 CONNECT 阶段发送
    DisableKeepAlives:  false,
}
client := &http.Client{Transport: tr, Timeout: 15 * time.Second}

for i := 1; i <= 3; i++ {
    req, _ := http.NewRequest("GET", "https://httpbin.org/ip", nil)
    resp, _ := client.Do(req)
    body, _ := io.ReadAll(resp.Body)
    resp.Body.Close()
    var data map[string]interface{}
    json.Unmarshal(body, &data)
    fmt.Printf("请求 %d: %v\n", i, data["origin"])
}

ProxyConnectHeader 是 Go net/http 标准库提供的能力,所有基于 net/http 的库(resty、go-resty)都可以通过底层 Transport 使用。

方案二:resty 第三方库

resty 是基于 net/http 的 HTTP 客户端库,API 更简洁。代理场景同样通过底层 Transport 控制:

package main

import (
    "crypto/tls"
    "fmt"
    "net/http"
    "net/url"
    "os"
    "time"
    "github.com/go-resty/resty/v2"
)

func getenv(key, def string) string {
    if v := os.Getenv(key); v != "" { return v }
    return def
}

func main() {
    host := getenv("PROXY_HOST", "t.16yun.cn")
    port := getenv("PROXY_PORT", "31111")
    user := getenv("PROXY_USERNAME", "user")
    pass := getenv("PROXY_PASSWORD", "password")

    proxyURL, _ := url.Parse(fmt.Sprintf("http://%s:%s@%s:%s", user, pass, host, port))

    // 场景 A:强制切换
    for i := 1; i <= 3; i++ {
        client := resty.New()
        client.SetProxy(proxyURL.String())
        client.SetTransport(&http.Transport{
            DisableKeepAlives: true,
        })
        resp, _ := client.R().Get("https://httpbin.org/ip")
        fmt.Printf("A-%d: %s\n", i, resp.String())
    }

    // 场景 B:Keep-Alive
    client := resty.New()
    client.SetProxy(proxyURL.String())
    client.SetTransport(&http.Transport{
        DisableKeepAlives: false,
        MaxIdleConns:      10,
    })
    for i := 1; i <= 3; i++ {
        resp, _ := client.R().Get("https://httpbin.org/ip")
        fmt.Printf("B-%d: %s\n", i, resp.String())
    }

    // 场景 C-HTTPS:Proxy-Tunnel
    tunnelVal := getenv("PROXY_TUNNEL", "resty-demo")
    client2 := resty.New()
    client2.SetTransport(&http.Transport{
        Proxy: http.ProxyURL(proxyURL),
        ProxyConnectHeader: http.Header{
            "Proxy-Tunnel": []string{tunnelVal},
        },
        DisableKeepAlives: false,
    })
    for i := 1; i <= 3; i++ {
        resp, _ := client2.R().Get("https://httpbin.org/ip")
        fmt.Printf("C-%d: %s\n", i, resp.String())
    }
}

四种场景对比

场景net/http 实现resty 实现
ADisableKeepAlives: true + 每次新 Client每次新 resty.New()
B复用 Transport + 启 KeepAlive复用 resty 实例
C-HTTP请求头 Proxy-TunnelSetHeader("Proxy-Tunnel", ...)
C-HTTPSProxyConnectHeader 设置头底层 Transport 设置

错误排查

curl -x http://$PROXY_USERNAME:$PROXY_PASSWORD@$PROXY_HOST:$PROXY_PORT https://httpbin.org/ip

407 核对凭据,429 降低并发,504 重试。

需要企业代理方案?

我们可根据目标站点、并发规模与稳定性目标提供定制方案。