http.Redirect默认用302(StatusFound)因语义上属临时跳转,适配登录后跳首页、表单防重复等常见场景;301强缓存且影响SEO,故不作默认,需显式传StatusMovedPermanently。

Go 里用 http.Redirect 就能实现重定向,但默认是 302,不是 301;想永久跳转必须显式传 http.StatusMovedPermanently,否则 SEO 和缓存都会出问题。
为什么 http.Redirect 默认不是 301?
因为语义上 302(http.StatusFound)才是“临时跳转”,符合大多数场景:登录后跳首页、表单提交后防刷新重复、A/B 测试分流。301 是强缓存行为,搜索引擎会直接替换索引,浏览器甚至可能永久记住跳转,本地改了代码也不生效——你看到的“没变化”,大概率是 Chrome 或 Safari 缓存了上次的 301 响应。
常见错误现象:http: superfluous response.WriteHeader call——在调 http.Redirect 前手动写了状态码或响应体;Location 头指向了相对路径如 /login,结果跳到 http://localhost/login(开发环境还行,上线就错)。
- 永远显式传第四个参数,别依赖默认值
- 目标 URL 必须是绝对地址,比如
"https://example.com/dashboard",不能只写"/dashboard" - 如果服务跑在反向代理后面(Nginx / Cloudflare),
r.URL.Host是内网地址,拼 URL 会崩;应从r.Header.Get("X-Forwarded-Proto")和r.Header.Get("X-Forwarded-Host")构建,或直接用相对路径让浏览器补全
307 和 308 怎么用?什么时候必须用?
当你需要保留原始请求方法时——比如 POST 请求跳转后还想发 POST,而不是被浏览器降级成 GET。302 和 301 都不保证方法不变,而 307(临时)、308(永久)才明确要求客户端保持 method 和 body 不变。
立即学习“go语言免费学习笔记(深入)”;
典型场景:微服务 API 路径迁移(POST /v1/users → POST /v2/users)、网关做灰度路由、表单提交链路中转。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 用
http.Redirect(w, r, "https://api.example.com/v2/users", http.StatusTemporaryRedirect)发 307 - 用
http.Redirect(w, r, "https://api.example.com/v2/users", http.StatusPermanentRedirect)发 308 - 注意:前端 JavaScript
fetch默认遵守 307/308,但传统表单提交仍可能被某些浏览器忽略,需实测
如何防止开放重定向漏洞?
如果跳转目标来自用户输入(比如 ?to=https://evil.com),不校验就直接 http.Redirect,等于把你的域名借给钓鱼站用。
最简白名单做法:只允许跳转到本域下的路径,或预设几个可信域名。
- 用
url.Parse(target)解析输入,检查u.Scheme == "" && u.Host == ""表示是相对路径,安全 - 若含协议和 host,比对是否在白名单里,比如
u.Host == "example.com" || u.Host == "app.example.com" - 拒绝
javascript:、data:、vbscript:等危险 scheme - 别用
strings.HasPrefix(target, "/")判断——攻击者可构造//evil.com绕过
调试时怎么确认重定向真发出去了?
别只看浏览器地址栏;curl 最可靠:
curl -v http://localhost:8080/old
看响应头里有没有 Location: https://example.com/new 和状态码是不是你设的(301 / 302 / 307)。如果返回 200 或压根没 Location,说明 http.Redirect 被提前 return 拦截了,或者前面已写过响应体。
另一个坑:用了反向代理但没配 proxy_redirect,Nginx 可能把你的 Location 头里的 https://localhost:8080 改成 http://yourdomain.com,协议不一致导致混合内容警告——这时候得靠 X-Forwarded-Proto 动态生成目标 URL,而不是硬编码。

















