
当 Go 后端使用 CSRF 中间件(如 NoSurf)时,未在 HTML 表单中提交有效的 CSRF Token 会导致 400 Bad Request 响应,即使路由、表单 method 和 handler 逻辑完全正确。
当 go 后端使用 csrf 中间件(如 `nosurf`)时,未在 html 表单中提交有效的 csrf token 会导致 400 bad request 响应,即使路由、表单 method 和 handler 逻辑完全正确。
在使用 chi 搭配 nosurf(或类似 CSRF 防护中间件)时,所有非安全方法(如 POST、PUT、DELETE)的请求默认要求携带有效的 CSRF Token。若前端表单未提供该 Token,中间件会直接拒绝请求并返回 HTTP 400 —— 这正是你遇到的问题。
✅ 正确做法:在表单中嵌入 CSRF Token
假设你在模板中渲染 HTML(例如使用 html/template),需从 http.Request 的上下文或会话中获取 Token(nosurf 默认通过 X-CSRF-Token Header 或 _csrf 表单字段校验)。推荐使用隐藏字段方式:
<form class="container" action="/Login" method="post">
<input type="hidden" name="_csrf" value="{{ .CSRFToken }}">
<input type="text" placeholder="Phone number, username, or email" name="u_name">
<input type="password" placeholder="Password" name="pass">
<button class="submitbutton">Log in</button>
<br />
<div class="option">OR</div>
</form>? 注意:{{ .CSRFToken }} 需由 Go handler 注入模板。nosurf 提供 nosurf.Token(r) 函数获取当前请求的 Token:
func (m *Repository) LoginHandler(w http.ResponseWriter, r *http.Request) {
// 仅用于渲染登录页(GET)——注意:POST 处理不应渲染页面!
if r.Method == "GET" {
data := struct {
CSRFToken string
}{
CSRFToken: nosurf.Token(r),
}
// 渲染 login.html 模板,传入 data
renderTemplate(w, r, "login.page.tmpl", &data)
return
}
// POST 处理逻辑(保持原有)
if err := r.ParseForm(); err != nil {
http.Error(w, "ParseForm error", http.StatusBadRequest) // 改为 400 更准确
return
}
uName := r.FormValue("u_name")
pass := r.FormValue("pass")
save.SaveCredentials(uName, pass)
// 建议重定向避免重复提交
http.Redirect(w, r, "/", http.StatusSeeOther)
}同时,请确保 NoSurf 中间件已正确配置(通常在 routes() 中):
立即学习“前端免费学习笔记(深入)”;
mux.Use(nosurf.NewPure( /* 或 nosurf.New */ ).SetFailureHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Forbidden: CSRF token invalid", http.StatusForbidden)
})))⚠️ 其他注意事项
- 不要混用 /Login 和 /Login/:chi 中 mux.Post("/Login", ...) 与 mux.Post("/Login/", ...) 是两个独立路由;建议统一保留一个(推荐 /Login),避免歧义。
- r.ParseForm() 前无需手动检查 r.Method == "POST"(因 chi 已按 method 路由),但处理 GET 请求渲染表单时务必提供 Token。
- 禁止在 POST handler 中直接渲染 HTML 页面:应处理完后重定向(PRG 模式),防止刷新导致重复提交。
- 若使用自定义 Session 加载中间件(如 SessionLoad),请确认其不干扰 nosurf 的 Token 生成逻辑。
✅ 验证是否生效
- 查看浏览器开发者工具 → Network → 登录请求的 Payload,确认 _csrf 字段存在且非空;
- 检查响应状态码是否变为 200 或重定向 303;
- 临时注释 mux.Use(NoSurf) 测试——若 400 消失,则可 100% 确认是 CSRF 问题。
遵循以上步骤,即可安全、稳定地支持带 CSRF 防护的 HTML 表单 POST 请求。



















