
本文介绍如何通过模拟真实浏览器请求头、启用连接复用等手段,使 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 的判断依据。精准模拟,方能稳定通行。










