gorilla/mux · warning
Forbidden
Error message
Forbidden
What it means
Not an error thrown by gorilla/mux. README.md:571 is the README copy of the same authentication-middleware example shown in doc.go (error 24): when X-Session-Token isn't found in tokenUsers, the example writes http.Error(w, "Forbidden", http.StatusForbidden). Documented separately because it is the more commonly copied source (README > godoc).
Source
Thrown at README.md:571
amw.tokenUsers["00000000"] = "user0"
amw.tokenUsers["aaaaaaaa"] = "userA"
amw.tokenUsers["05f717e5"] = "randomUser"
amw.tokenUsers["deadbeef"] = "user0"
}
// Middleware function, which will be called for each request
func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("X-Session-Token")
if user, found := amw.tokenUsers[token]; found {
// We found the token in our map
log.Printf("Authenticated user %s\n", user)
// Pass down the request to the next middleware (or final handler)
next.ServeHTTP(w, r)
} else {
// Write an error and stop the handler chain
http.Error(w, "Forbidden", http.StatusForbidden)
}
})
}
```
```go
r := mux.NewRouter()
r.HandleFunc("/", handler)
amw := authenticationMiddleware{tokenUsers: make(map[string]string)}
amw.Populate()
r.Use(amw.Middleware)
```
Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. Middlewares _should_ write to `ResponseWriter` if they _are_ going to terminate the request, and they _should not_ write to `ResponseWriter` if they _are not_ going to terminate it.
### Handling CORS RequestsView on GitHub (pinned to db9d1d0073)
Solutions
- Send X-Session-Token set to a token that Populate() registered.
- Verify Populate() runs before r.Use(amw.Middleware) and that tokenUsers is populated from your real user store, not the example's hardcoded literals.
- Swap the in-memory map for a real authn backend (signed cookie / JWT / database lookup) before exposing the service.
- Use 401 for missing credentials and reserve 403 for authenticated-but-unauthorized, so clients can react correctly.
Example fix
// before (README.md:571)
http.Error(w, "Forbidden", http.StatusForbidden)
// after
if token == "" {
w.Header().Set("WWW-Authenticate", "Bearer realm=""mux""")
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
user, ok := userStore.BySessionToken(token)
if !ok {
http.Error(w, "forbidden", http.StatusForbidden)
return
} Defensive patterns
Strategy: validation
Validate before calling
// Validate session token at the boundary; reject empty/malformed before lookup.
func validSessionToken(t string) bool {
t = strings.TrimSpace(t)
if t == "" { return false }
// shape check appropriate to your tokens, e.g. opaque >= 16 hex bytes
if len(t) < 16 { return false }
for _, c := range t {
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
return false
}
}
return true
}
// in the middleware
token := r.Header.Get("X-Session-Token")
if !validSessionToken(token) {
w.Header().Set("WWW-Authenticate", "Bearer realm=\"mux\"")
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
user, ok := store.BySessionToken(token) Try / catch
// Treat the example's 403 as a fallback; classify failures explicitly.
func authmw(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user, status := authenticate(r.Header.Get("X-Session-Token"))
if status != 0 {
// status is 401 for missing/invalid, 403 for known-but-unauthorized
if status == 401 {
w.Header().Set("WWW-Authenticate", "Bearer")
}
http.Error(w, http.StatusText(status), status)
return
}
next.ServeHTTP(w, r.WithContext(withUser(r.Context(), user)))
})
} Prevention
- Treat the README example as a sketch: replace the hardcoded map with a real session/JWT store before shipping.
- Reserve 401 for 'no usable credential' and 403 for 'authenticated but not allowed' so clients can prompt correctly.
- Validate token shape and length before any lookup to avoid timing oracles on the map/store.
- Write tests asserting both pass-through and rejection paths so a future copy-paste of the example still behaves.
When it happens
Trigger: A request reaches a handler guarded by the README's authenticationMiddleware without an X-Session-Token header, with an unknown token, or before Populate() has seeded tokenUsers; the middleware short-circuits the chain with 403 "Forbidden".
Common situations: Front-end not yet sending the auth header; token list out of sync with the client; Populate() skipped during wiring; header name mismatch (e.g. Authorization Bearer vs X-Session-Token); the example pasted as-is into production code paths.
Related errors
AI-assisted analysis of gorilla/mux@db9d1d0073 (2026-08-04).
Data as JSON: /data/errors/a9e1611435cdbba1.json.
Report an issue: GitHub.