如何让 Go 的 HTTP 请求绕过网站的反爬与防火墙检测

5次阅读

如何让 Go 的 HTTP 请求绕过网站的反爬与防火墙检测

本文介绍如何通过模拟真实浏览器请求头、启用连接复用等手段,使 go 编写的链接健康检查程序不被 f5 等 web 应用防火墙(waf)拦截或拒绝,从而稳定获取目标网页状态码。

现代 Web 应用防火墙(如 F5 ASM、Cloudflare、AWS WAF)常基于请求特征进行机器人识别——不仅检查 User-Agent,还会分析请求头完整性、顺序、http/1.1 行为(如 Connection: keep-alive)、Accept 与 Accept-Language 等字段是否存在及取值合理性。go 标准库 net/http 默认发送极简请求,缺少关键浏览器头部,极易被标记为自动化工具并拦截。

要让 Go 请求“看起来像浏览器”,需主动补全以下核心请求头(以 chrome 最新桌面版为例):

req.Header.Set("User-Agent", "Mozilla/5.0 (windows NT 10.0; Win64; x64) appleWebKit/537.36 (Khtml, like Gecko) Chrome/124.0.0.0 safari/537.36") req.Header.Set("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") req.Header.Set("Accept-Language", "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7") req.Header.Set("Accept-Encoding", "gzip, deflate") req.Header.Set("Connection", "keep-alive") req.Header.Set("Upgrade-Insecure-Requests", "1") req.Header.Set("Sec-Fetch-Dest", "document") req.Header.Set("Sec-Fetch-Mode", "navigate") req.Header.Set("Sec-Fetch-Site", "none") req.Header.Set("Sec-Fetch-User", "?1")

✅ 重要提示:Sec-* 头部(如 Sec-Fetch-*)虽非 HTTP 标准,但已被主流浏览器广泛采用,F5 等 WAF 常将其作为关键指纹。务必添加(即使 Go 不校验其语义)。

此外,还需优化客户端配置以增强真实性:

  • 启用连接复用:设置 Transport 并复用 TCP 连接;
  • 禁用自动重定向(避免暴露重试行为);
  • 设置合理超时(避免因慢响应触发速率限制);
  • 可选:使用 http.Transport 配置 TLS 指纹(如通过 github.com/zmap/zcrypto/tls 或 github.com/saucelabs/utls 实现 JA3 模拟,进阶场景)。

完整改进版函数示例如下:

func fetchURL(urlStr string, timeout time.Duration) (int, error) {     client := &http.Client{         Timeout: timeout,         Transport: &http.Transport{             // 复用连接,模拟浏览器持久化连接行为             MaxIdleConns:        100,             MaxIdleConnsPerHost: 100,             IdleConnTimeout:     30 * time.Second,         },     }      req, err := http.NewRequest("GET", urlStr, nil)     if err != nil {         return 0, fmt.Errorf("failed to create request: %w", err)     }      // 模拟真实浏览器头部(请根据实际测试环境微调)     req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36")     req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")     req.Header.Set("Accept-Language", "en-US,en;q=0.9")     req.Header.Set("Accept-Encoding", "gzip, deflate")     req.Header.Set("Connection", "keep-alive")     req.Header.Set("Upgrade-Insecure-Requests", "1")     req.Header.Set("Sec-Fetch-Dest", "document")     req.Header.Set("Sec-Fetch-Mode", "navigate")     req.Header.Set("Sec-Fetch-Site", "none")     req.Header.Set("Sec-Fetch-User", "?1")      resp, err := client.Do(req)     if err != nil {         return 0, fmt.Errorf("request failed: %w", err)     }     defer resp.Body.Close() // 注意:defer 在函数返回前执行,确保资源释放      return resp.StatusCode, nil }

⚠️ 注意事项

  • 不要硬编码固定 User-Agent 并长期使用——部分 WAF 会建立 UA 黑名单。建议轮换 2~3 个主流 UA 字符串(Chrome/firefox/Safari),配合随机延迟(如 time.Sleep(100*time.Millisecond + time.Duration(rand.Intn(200))*time.Millisecond))。
  • 若仍被拦截,可抓包对比浏览器真实请求(chrome devtools → Network → copy as curl),逐项比对缺失头字段。
  • 对于高防护站点(如银行、政府网站),可能需引入无头浏览器(Puppeteer/Playwright)或代理池,但会显著增加运维复杂度。

总之,“像浏览器” ≠ 仅改 UA,而是系统性地匹配 HTTP 协议层的行为指纹。从请求头完整性、连接策略到 TLS 握手特征,每一步都可能是 WAF 的判断依据。精准模拟,方能稳定通行。

text=ZqhQzanResources