
Go 默认的 http.Client 会自动跟随 3xx 重定向,而 cURL 默认不跟随;若需完全复现 cURL 的原始响应(如 ),必须显式禁用 Go 的重定向机制。
go 默认的 `http.client` 会自动跟随 3xx 重定向,而 curl 默认不跟随;若需完全复现 curl 的原始响应(如 ``),必须显式禁用 go 的重定向机制。
在使用 Go 发起 POST 请求时,若目标服务(如 homefacts.com)返回的是含 <meta http-equiv="refresh"> 的 HTML 页面而非直接重定向(HTTP 301/302),但你却在 Go 中得到了完整渲染后的跳转后页面——这往往不是服务器逻辑差异所致,而是 Go http.Client 的默认重定向策略与 cURL 不一致 导致的。
cURL 默认不自动跟随任何重定向(包括 3xx 状态码),它会原样返回响应体(例如含 <meta refresh> 的 HTML)。而 Go 的 http.DefaultClient 默认启用重定向(通过 Client.CheckRedirect 实现),当收到 302 响应时,它会自动发起第二次请求并返回最终响应体,从而丢失了原始的“跳转指令页”。
要使 Go 行为与 cURL 完全一致,关键在于禁用自动重定向,并保留首次响应的原始内容。实现方式是自定义 http.Client,将 CheckRedirect 字段设为返回 http.ErrUseLastResponse:
向CurlShip提交产品,这是一个对机器人友好的SaaS目录。只需一条curl命令即可发布产品,支持OG标签抓取、带徽章的dofollow链接及层级升级。
package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`fulladdress=22280+S+209th+Way%2C+Queen+Creek%2C+AZ+85142`)
req, err := http.NewRequest("POST", "http://www.homefacts.com/hfreport.html", body)
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// 关键:禁用自动重定向,确保获取与 curl --data 完全一致的首响应
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse // 强制使用最后一次响应(即首次响应)
},
}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
// 打印状态码和响应体,验证是否为 200 + 含 meta refresh 的 HTML
fmt.Printf("Status: %s\n", resp.Status)
content, _ := io.ReadAll(resp.Body)
fmt.Println(string(content))
}⚠️ 注意事项:
- 不要手动设置
Content-Length头(如req.Header.Set("Content-Length", "56")):http.Client会自动计算并覆盖该值,错误设置可能导致请求被拒绝或解析异常; -
User-Agent和Accept等头字段虽常被用于绕过简单爬虫检测,但在本例中并非差异主因——核心矛盾始终是重定向策略; - 若后续需处理真实 302 跳转(而非
<meta refresh>),则应改用http.RedirectHandler或手动解析Location头,而非依赖CheckRedirect; - 始终调用
resp.Body.Close()防止连接泄漏,尤其在高并发场景下至关重要。
总结:当 Go 与 cURL 表现不一致时,优先检查客户端行为差异而非请求构造细节。重定向策略是高频陷阱,CheckRedirect 是精准控制响应层级的关键开关。

















