Skip to main content

Request Sanitization

Regius includes a request sanitization middleware that neutralizes XSS at the request boundary by sanitizing query params, form-encoded values, and selected request headers with bluemonday before downstream handlers ever see them.

Features

  • Defense-in-depth: Enabled by default in scaffolded apps (REQUEST_SANITIZATION_ENABLED=true)
  • Two policies via env: strict (default — strips all HTML, returns safe text) and ugc (allows a safe HTML subset like <b>, <a> for comment-style fields)
  • Comprehensive coverage: Sanitizes URL query params, form-encoded POST values (including multipart text fields), and a configurable allowlist of request headers (default Referer, User-Agent)
  • JSON-safe: application/json request bodies are never parsed or consumed, so API handlers keep full access to r.Body
  • Path exemption: Routes matching REQUEST_SANITIZATION_EXEMPT (default /api/.*) bypass sanitization entirely, mirroring the CSRF (NoSurf) exemption
  • Standalone helpers: r.Sanitize(s) / r.Sanitizer() (app-configured policy) and package-level regius.Sanitize(s) (strict)
  • Non-destructive: Clean values are left byte-for-byte intact; only values containing HTML are rewritten
caution

Don't sanitize structural headers (Authorization, Cookie, X-CSRF-Token, Content-*, X-Forwarded-*, X-Request-ID) — doing so breaks routing, auth, and tracing. The default allowlist avoids these.

Usage

Request sanitization is applied globally when REQUEST_SANITIZATION_ENABLED=true. No additional code is required.

Targeted sanitization in a handler (e.g. before storing user input):

func (a *App) StoreComment(w http.ResponseWriter, r *http.Request) {
raw := r.FormValue("comment")
safe := a.Sanitize(raw) // uses the app's configured policy
// store safe...
}

Or build the middleware manually for a route group:

r.Group(func(mux chi.Router) {
mux.Use(a.RequestSanitizer(regius.RequestSanitizerConfig{
Enabled: true,
Policy: regius.SanitizePolicyUGC, // allow safe HTML subset
Headers: []string{"Referer"},
}))
// routes here...
})

Configuration Options

config := regius.RequestSanitizerConfig{
Enabled: true, // Master toggle
Policy: regius.SanitizePolicyStrict, // "strict" (default) | "ugc"
Query: regius.BoolPtr(true), // Sanitize URL query params (default true)
Form: regius.BoolPtr(true), // Sanitize form-encoded values (default true)
Headers: []string{"Referer", "User-Agent"}, // Header allowlist (default none)
Exempt: "/api/.*", // Regex of paths to skip (default "/api/.*")
Custom: nil, // Optional *bluemonday.Policy override
}

BoolPtr is a tiny helper to set *bool fields (nil defaults to true):

regius.BoolPtr(false) // explicitly disable a scope

Policies

PolicyDescription
SanitizePolicyStrictStrips all HTML, returns safe text (default)
SanitizePolicyUGCAllows a safe HTML subset (<b>, <a>, etc.) for comment-style fields

Environment Variables

REQUEST_SANITIZATION_ENABLED=true
REQUEST_SANITIZATION_POLICY=strict
REQUEST_SANITIZATION_QUERY=true
REQUEST_SANITIZATION_FORM=true
REQUEST_SANITIZATION_HEADERS=Referer,User-Agent
REQUEST_SANITIZATION_EXEMPT=/api/.*