net/http.redirect 并非重定向的最终方案,真正起作用的是手动设置 location 头并返回 3xx 状态码;若调用前已写入响应体(如 w.write),后续状态码将被静默忽略而返回 200,且其不校验 url 合法性,易引发开放重定向漏洞。

net/http.Redirect 不是返回重定向响应的“最终方案”,它只是封装了常见写法;真正起作用的是手动设置 Location 头 + 返回 3xx 状态码。直接调用它省事,但容易在状态码、路径拼接、相对 URL 场景中出错。
为什么 net/http.Redirect 有时会返回 200 而不是 302?
根本原因是:它内部调用 http.Error 并写入响应体(比如 "Found" 文本),但如果你之前已经写过响应体(例如调用了 w.Write 或 fmt.Fprint(w, ...)),Go 的 HTTP handler 就会静默忽略后续的 Header().Set 和状态码修改——此时 Redirect 仍会尝试设 Location,但状态码卡在默认的 200,浏览器收不到重定向指令。
- 确保在调用
net/http.Redirect前,没有向http.ResponseWriter写入任何内容 - 检查中间件是否提前写了响应(如日志中间件误调
w.Write) - 调试时用
curl -v查看实际响应头和状态码,别只信浏览器跳转结果
net/http.Redirect 的第三个参数:选错状态码会怎样?
第三个参数是 code int,常见值有 http.StatusFound(302)、http.StatusMovedPermanently(301)、http.StatusTemporaryRedirect(307)。选错会影响客户端行为:
- 301/308 会被浏览器缓存,后续请求可能绕过你的服务直跳目标地址——开发时改逻辑却没跳转,很可能是被本地缓存住了
- 302/303 会把 POST 变成 GET(丢失请求体),不适合需要保留方法和 body 的重定向(如 OAuth 回调后跳回原页面)
- 307/308 才保证方法和 body 不变,但老版本客户端(如 IE)支持差,需权衡兼容性
示例:强制保持 POST 方法重定向
http.Redirect(w, r, "/new-endpoint", http.StatusTemporaryRedirect)
相对路径传给 net/http.Redirect 会怎么解析?
它不自动补全 Host 或 Scheme,只按 RFC 7231 规则处理:如果 url 是相对路径(如 "./dashboard" 或 "login"),Go 会直接塞进 Location 头,由浏览器根据当前请求的 URL 解析——这意味着它依赖用户访问时的完整路径上下文,极易因入口 URL 差异导致跳转错乱。
- 绝对 URL 最安全:
"https://example.com/dashboard" - 若必须用相对路径,优先用根路径开头:
"/dashboard"(避免"dashboard"这种无斜杠前缀) - 不要依赖
r.Referer()拼接跳转地址——它不可靠,可能为空或被篡改
最常被忽略的一点:重定向目标 URL 的合法性校验必须由你负责。net/http.Redirect 对传入的 url 字符串不做任何过滤或转义,如果它来自用户输入(如 r.URL.Query().Get("next")),直接传入会导致开放重定向漏洞。别只想着“跳过去就行”,漏掉校验等于给攻击者留了后门。











