CSRF Protection
Cross-Site Request Forgery (CSRF) attacks trick authenticated users into submitting malicious requests. Nimbus provides middleware.CSRF() to protect state-changing routes by requiring a valid token with each request.
What is CSRF?
A CSRF attack occurs when a malicious site submits a form or request to your application while the user is logged in. Because the browser automatically includes cookies, the server cannot distinguish the forged request from a legitimate one. CSRF tokens solve this by requiring a secret value that only your pages know.
Setting up CSRF middleware
Create a CSRFStore and register the middleware. The built-in MemoryCSRFStore works for single-server deployments:
store := middleware.NewMemoryCSRFStore()
app.Router.Use(middleware.CSRF(store))
How it works
The middleware skips validation for safe methods (GET, HEAD, OPTIONS). For all other methods (POST, PUT, PATCH, DELETE), it checks for a valid token in:
- The
X-CSRF-Tokenrequest header - The
csrf_tokenform field
If no valid token is found, the middleware responds with 403 Forbidden.
Generating tokens
Use store.Create() to generate and register a new token, or middleware.GenerateCSRFToken() for a standalone token. Pass the token to your templates or API responses:
func FormPage(c *http.Context) error {
token := store.Create()
return c.View("form", map[string]any{
"csrfToken": token,
})
}
Including CSRF token in forms
Add a hidden input field named csrf_token to your HTML forms. The value should be the token generated on the server:
<form method="POST" action="/posts">
<input type="hidden" name="csrf_token" value="TOKEN_VALUE_HERE">
<input type="text" name="title">
<button type="submit">Create</button>
</form>
CSRF for AJAX requests
For JavaScript requests, include the token in the X-CSRF-Token header:
fetch("/api/posts", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": csrfToken
},
body: JSON.stringify({ title: "My Post" })
})
Excluding routes from CSRF
Apply CSRF middleware to specific route groups rather than globally. Routes outside the group are not checked:
// CSRF only on web routes
web := app.Router.Group("")
web.Use(middleware.CSRF(store))
web.Post("/posts", CreatePost)
web.Post("/settings", UpdateSettings)
// API routes use token auth instead — no CSRF needed
api := app.Router.Group("/api")
api.Use(auth.RequireAuth(tokenGuard, ""))
api.Post("/posts", APICreatePost)
Custom CSRF store
Implement the middleware.CSRFStore interface to use Redis, a database, or another backend for token storage:
type CSRFStore interface {
Valid(ctx context.Context, token string) bool
}