When developing applications with Chi, engineers must assume that routers provide minimal built-in access controls and rely entirely on correctly structured middleware pipelines to enforce security policies. Developers must explicitly manage authentication, request normalization, trust boundaries, and resource limits because unvalidated inputs and misordered middleware can easily bypass perimeter protections. Security-sensitive surfaces include route grouping, header-based routing, client IP resolution, and wildcard parameter extraction, all of which must fail closed when encountering unexpected or malicious inputs.
Essential implementation rules
Isolate Access Control and Authorization via Router Groups
Apply access control and authorization middleware at the router group or subrouter level using chi.NewRouter(), r.Route(), or r.Group() before defining protected endpoints. Restrict sensitive administrative or debugging endpoints like middleware.Profiler() inside protected groups enforcing strict authorization.
Adhere Strictly to Chi API and Compressor Contracts
Pass strictly valid MIME types or trailing wildcard suffixes like text/* into compressor configuration constructors to avoid runtime panics. Register custom HTTP methods via chi.RegisterMethod sequentially during application initialization prior to routing, and write pass-through middleware using next.ServeHTTP(w, r) rather than deprecated constructor patterns.
Validate Credentials Before Modifying Request Context
Derive authentication state strictly from verified headers such as Authorization rather than unvalidated URL query parameters. Use custom unexported types for context keys to prevent collisions and pass updated request contexts downstream using r.WithContext after cryptographic validation.
Configure Explicit Client IP Trust Boundaries
Select only HTTP header names unconditionally overwritten by your edge proxy when using middleware.ClientIPFromHeader. Replace legacy real-ip logic with explicit trust configurations like middleware.ClientIPFromXFF containing trusted CIDR ranges to prevent IP spoofing.
Enforce Default-Deny Fallback Handlers in Header Routing
Explicitly configure a default handler via RouteDefault when routing requests using middleware.RouteHeaders() to prevent unmatched requests from silently bypassing security policies or failing open.
Canonicalize Paths and Sanitize Wildcard Parameters
Apply middleware.CleanPath early on the top-level router to canonicalize request paths and remove duplicate slashes before route matching occurs. Extract wildcard parameters using chi.URLParam(r, "*") and sanitize them thoroughly prior to file system access or downstream forwarding.
Restrict Endpoints with Explicit HTTP Methods
Define endpoints using HTTP method-specific routing functions such as r.Get and r.Post rather than generic catch-all handlers to automatically enforce HTTP status 405 Method Not Allowed semantics for unhandled methods.
Limit Request Size and Concurrency for Resource Protection
Apply middleware.RequestSize to restrict allowable request body sizes and prevent unconstrained payload reading. Protect expensive or sensitive routes against denial-of-service by applying middleware.Throttle or middleware.ThrottleWithOpts.
Exclude Secrets and Tokens from Query Parameters and Logs
Transmit sensitive tokens and personal identifiers via HTTP headers rather than URL query parameters to prevent request logging middleware such as middleware.Logger from capturing plaintext credentials in standard output or log sinks.
Enforce Correct Global Middleware Ordering
Register global security middleware like authentication, rate-limiting, and middleware.Logger via r.Use(...) prior to defining routes or mounting middleware.SupressNotFound. Position middleware.Logger before middleware.Recoverer to ensure panic stack traces are successfully logged.
chi: All Security Cards
Approximately 2,926 tokens
On this card
Category: access control
Enforce Access Control Middleware Using Router Groups and Subrouters
Use when
When organizing routes in Chi and securing endpoints against unauthorized actions or cross-user access
Secure rules
Rule 1: Apply access control and authorization middleware at the router group or subrouter level before defining protected endpoints.
Use chi.NewRouter(), r.Route(), or r.Group() to isolate middleware stacks and ensure authentication, role checks, and resource loading are executed before handling requests.
r := chi.NewRouter()// Authenticated endpoints grouped under auth middlewarer.Group(func(r chi.Router) { r.Use(AuthMiddleware) r.Get("/user/profile", ProfileHandler) r.Post("/user/settings", SettingsHandler)})
Rule 2: Restrict access to sensitive administrative or profiler endpoints using authentication and network boundaries.
Mount debugging endpoints like middleware.Profiler() or administrative subrouters inside protected route groups that enforce strict authorization checks or restrict exposure to internal networks.
r := chi.NewRouter()// Restrict pprof endpoints to authenticated administratorsr.Group(func(r chi.Router) { r.Use(RequireAdminAuth) r.Mount("/debug", middleware.Profiler())})
Category: api contract misuse
Adhere to Chi API Contracts and Correct Argument Signatures
Use when
Integrating Chi routers, middleware components, and custom HTTP method handlers into a Go application.
Secure rules
Rule 1: Pass strictly valid MIME types or trailing wildcard suffixes into compressor configuration constructors.
When configuring middleware.Compress or middleware.NewCompressor, specify exact MIME types or trailing wildcard suffixes such as text/*. Avoid passing unsupported wildcard patterns like */* to prevent immediate runtime panics during router initialization.
Rule 2: Register custom HTTP methods sequentially during application initialization prior to routing.
Always call chi.RegisterMethod during initial setup before starting the HTTP server or registering routes. Never invoke it dynamically from request handlers or concurrent goroutines to prevent data races and runtime crashes.
func main() { chi.RegisterMethod("PURGE") r := chi.NewRouter() r.MethodFunc("PURGE", "/cache", handlePurge)}
Rule 3: Construct pass-through middleware functions that correctly invoke the next handler in the execution chain.
Do not use middleware.New for wrapping pass-through middleware such as authentication or context injection, as it deliberately ignores downstream handlers and routes. Instead, write middleware functions that accept next http.Handler and explicitly call next.ServeHTTP(w, r).
Validate Authentication Credentials Before Setting Request Context
Use when
Developing authentication middleware to verify user identity before storing session or user state in the request context.
Secure rules
Rule 1: Populate authentication state in the request context only after cryptographically validating session tokens, bearer tokens, or credentials, and never accept unvalidated inputs like URL query parameters.
Authentication state must be strictly derived from verified credentials such as the Authorization header rather than untrusted query parameters. Use custom unexported types for context keys to prevent collisions, validate the incoming token or credentials securely, and pass the updated context down the request chain using r.WithContext.
Configure ClientIPFromHeader with unconditionally overwritten proxy headers
Use when
Setting up boundary trust verification and client IP retrieval using middleware.ClientIPFromHeader in chi applications behind a reverse proxy.
Secure rules
Rule 1: Select only HTTP header names that your reverse proxy unconditionally overwrites on every request when using middleware.ClientIPFromHeader.
When configuring middleware.ClientIPFromHeader, ensure you only supply header names such as X-Real-IP or CF-Connecting-IP that are unconditionally overwritten by your edge proxy. Avoid pass-through headers like True-Client-IP, X-Azure-ClientIP, or Fastly-Client-IP unless your edge proxy explicitly strips inbound client-supplied values to prevent IP spoofing at the network boundary.
r := chi.NewRouter()r.Use(middleware.ClientIPFromHeader("X-Real-IP"))
Category: input driven boundary selection
Enforce explicit default fallback handlers in header-based routing
Use when
When routing middleware execution using middleware.RouteHeaders() to apply security checks or access controls based on untrusted request headers.
Secure rules
Rule 1: Explicitly configure a default handler via RouteDefault when routing requests using RouteHeaders to prevent unmatched requests from bypassing security policies.
When using middleware.RouteHeaders(), requests that do not match configured header patterns or RouteAny rules will silently fall through to downstream handlers unless a fallback is provided. You must explicitly declare a RouteDefault handler to enforce safe default-deny or fallback policies for unrecognized header values.
Rule 2: Reject requests with unexpected header values in the RouteDefault handler.
Ensure that the handler passed to RouteDefault explicitly handles or rejects requests failing header verification rather than allowing them to proceed unrestricted.
Category: input interpretation safety
Canonicalize and Sanitize Request Paths and Parameters
Use when
When building HTTP routers and handling URL parameters, path extensions, or request path normalization in Chi
Secure rules
Rule 1: Apply middleware.CleanPath early on the top-level router to canonicalize request paths and remove duplicate slashes before route matching.
Use middleware.CleanPath as early middleware on the root router to canonicalize request paths by removing duplicate slashes before route matching occurs. Ensure it is added at the top-level router before sub-routers or route definitions, as it only modifies rctx.RoutePath when it has not yet been set.
r := chi.NewRouter()r.Use(middleware.CleanPath)r.Get("/users/{id}", getUserHandler)
Rule 2: Sanitize wildcard route parameters to prevent path traversal.
When extracting parameters from wildcard routes using chi.URLParam(r, "*"), explicitly clean and sanitize the resulting path prior to using it in file system access or downstream request forwarding.
r := chi.NewRouter()r.Get("/docs/*", func(w http.ResponseWriter, r *http.Request) { param := chi.URLParam(r, "*") cleanPath := filepath.Clean(param) if strings.HasPrefix(cleanPath, "..") || strings.Contains(cleanPath, "/..") { http.Error(w, "Invalid path", http.StatusBadRequest) return }})
Category: interface protocol hardening
Enforce Strict HTTP Method Restrictions and Semantics
Use when
When registering endpoints and configuring routers to ensure requests strictly adhere to expected HTTP methods and protocol semantics.
Secure rules
Rule 1: Use explicit HTTP method routing functions to restrict endpoints to expected methods.
When defining endpoints, use HTTP method-specific routing functions such as r.Get and r.Post rather than generic catch-all handlers. Chi automatically responds with HTTP status 405 Method Not Allowed and populates the Allow header for requests using unhandled HTTP methods.
r := chi.NewRouter()// Explicitly register supported methods for the pathr.Get("/items", listItemsHandler)r.Post("/items", createItemHandler)// Unregistered methods (e.g., PUT /items) automatically receive HTTP 405
Category: network boundary
Configure explicit trusted proxy settings for client IP parsing
Use when
Setting up HTTP routers behind reverse proxies or load balancers where client IP addresses must be securely resolved without falling back to vulnerable default header parsing.
Secure rules
Rule 1: Replace deprecated RealIP middleware with explicit ClientIPFrom* trust configurations tailored to your deployment topology.
The legacy RealIP middleware was vulnerable to IP spoofing because it blindly trusted leftmost X-Forwarded-For headers. Instead, use explicit trust configurations such as ClientIPFromXFF with explicit CIDR ranges to ensure attacker-injected header values cannot bypass IP-based access controls or logging.
r := chi.NewRouter()r.Use(middleware.ClientIPFromXFF( "13.32.0.0/15", "2600:9000::/28",))r.Get("/profile", func(w http.ResponseWriter, r *http.Request) { clientIP := middleware.GetClientIP(r.Context()) // Use clientIP for rate limiting or access control})
Category: resource exhaustion
Limit Request Body Size and Concurrency for Resource-Heavy Endpoints
Use when
When building endpoints or route groups that accept client payloads or execute resource-intensive operations and require protection against resource exhaustion and Denial of Service.
Secure rules
Rule 1: Apply request size limits and concurrency controls to prevent server resource exhaustion.
Use middleware.RequestSize to restrict allowable request body sizes and prevent unconstrained payload reading. Additionally, apply middleware.Throttle or middleware.ThrottleWithOpts to establish capacity ceilings and manage concurrent in-flight requests on expensive or sensitive routes.
Exclude Sensitive Data from Request Query Parameters and Logs
Use when
When building HTTP endpoints and configuring logging middleware with chi to prevent plaintext exposure of secrets in request URIs.
Secure rules
Rule 1: Avoid passing secrets, tokens, or personal identifiers in URL query strings where middleware.Logger or DefaultLogFormatter can capture them in logs.
Transmit sensitive tokens via HTTP headers rather than query parameters so that plaintext credentials are not logged in standard output or log sinks by request logging middleware.
// Transmit sensitive tokens via HTTP headers rather than query parameters// BAD: GET /api/data?access_token=secret_key// GOOD: Pass token in Authorization headerr := chi.NewRouter()r.Use(middleware.Logger)r.Get("/api/data", func(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") // Process request securely...})
Category: security control integrity
Order Security Middlewares and Controls First in the Chi Router Pipeline
Use when
When registering global middleware, constructing middleware chains, or setting up routing middleware in Chi applications to ensure security mechanisms execute reliably.
Secure rules
Rule 1: Register global security middleware before declaring routes on the router mux.
Always add global middleware functions like authentication, rate-limiting, and logging via r.Use(...) prior to defining any routes or sub-routers. Chi enforces this ordering by design and will panic if Mux.Use is invoked after routes have already been configured.
r := chi.NewRouter()r.Use(middleware.Logger)r.Use(AuthMiddleware)r.Get("/protected", ProtectedHandler)
Rule 2: Mount global security controls before SupressNotFound in the middleware stack.
Ensure that mandatory security controls intended for all traffic, such as rate limiters and IP blockers, are mounted before middleware.SupressNotFound in the middleware chain. Placing SupressNotFound above security middleware causes unmapped requests to short-circuit and bypass those controls.
r := chi.NewRouter()r.Use(rateLimiterMiddleware)r.Use(middleware.SupressNotFound(r))r.Use(expensiveBusinessMiddleware)
Rule 3: Position the Logger middleware before Recoverer in the router pipeline.
Always register middleware.Logger before middleware.Recoverer so that a LogEntry is attached to the request context via WithLogEntry, allowing the recoverer middleware to successfully format and log panic stack traces.
r := chi.NewRouter()r.Use(middleware.Logger)r.Use(middleware.Recoverer)r.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok"))})
Enforce Access Control Middleware Using Router Groups and Subrouters
Approximately 296 tokens
Use when
When organizing routes in Chi and securing endpoints against unauthorized actions or cross-user access
Secure rules
Rule 1: Apply access control and authorization middleware at the router group or subrouter level before defining protected endpoints.
Use chi.NewRouter(), r.Route(), or r.Group() to isolate middleware stacks and ensure authentication, role checks, and resource loading are executed before handling requests.
r := chi.NewRouter()// Authenticated endpoints grouped under auth middlewarer.Group(func(r chi.Router) { r.Use(AuthMiddleware) r.Get("/user/profile", ProfileHandler) r.Post("/user/settings", SettingsHandler)})
Rule 2: Restrict access to sensitive administrative or profiler endpoints using authentication and network boundaries.
Mount debugging endpoints like middleware.Profiler() or administrative subrouters inside protected route groups that enforce strict authorization checks or restrict exposure to internal networks.
r := chi.NewRouter()// Restrict pprof endpoints to authenticated administratorsr.Group(func(r chi.Router) { r.Use(RequireAdminAuth) r.Mount("/debug", middleware.Profiler())})
Adhere to Chi API Contracts and Correct Argument Signatures
Approximately 435 tokens
Use when
Integrating Chi routers, middleware components, and custom HTTP method handlers into a Go application.
Secure rules
Rule 1: Pass strictly valid MIME types or trailing wildcard suffixes into compressor configuration constructors.
When configuring middleware.Compress or middleware.NewCompressor, specify exact MIME types or trailing wildcard suffixes such as text/*. Avoid passing unsupported wildcard patterns like */* to prevent immediate runtime panics during router initialization.
Rule 2: Register custom HTTP methods sequentially during application initialization prior to routing.
Always call chi.RegisterMethod during initial setup before starting the HTTP server or registering routes. Never invoke it dynamically from request handlers or concurrent goroutines to prevent data races and runtime crashes.
func main() { chi.RegisterMethod("PURGE") r := chi.NewRouter() r.MethodFunc("PURGE", "/cache", handlePurge)}
Rule 3: Construct pass-through middleware functions that correctly invoke the next handler in the execution chain.
Do not use middleware.New for wrapping pass-through middleware such as authentication or context injection, as it deliberately ignores downstream handlers and routes. Instead, write middleware functions that accept next http.Handler and explicitly call next.ServeHTTP(w, r).
Validate Authentication Credentials Before Setting Request Context
Approximately 298 tokens
Use when
Developing authentication middleware to verify user identity before storing session or user state in the request context.
Secure rules
Rule 1: Populate authentication state in the request context only after cryptographically validating session tokens, bearer tokens, or credentials, and never accept unvalidated inputs like URL query parameters.
Authentication state must be strictly derived from verified credentials such as the Authorization header rather than untrusted query parameters. Use custom unexported types for context keys to prevent collisions, validate the incoming token or credentials securely, and pass the updated context down the request chain using r.WithContext.
Configure ClientIPFromHeader with unconditionally overwritten proxy headers
Approximately 236 tokens
Use when
Setting up boundary trust verification and client IP retrieval using middleware.ClientIPFromHeader in chi applications behind a reverse proxy.
Secure rules
Rule 1: Select only HTTP header names that your reverse proxy unconditionally overwrites on every request when using middleware.ClientIPFromHeader.
When configuring middleware.ClientIPFromHeader, ensure you only supply header names such as X-Real-IP or CF-Connecting-IP that are unconditionally overwritten by your edge proxy. Avoid pass-through headers like True-Client-IP, X-Azure-ClientIP, or Fastly-Client-IP unless your edge proxy explicitly strips inbound client-supplied values to prevent IP spoofing at the network boundary.
r := chi.NewRouter()r.Use(middleware.ClientIPFromHeader("X-Real-IP"))
Enforce explicit default fallback handlers in header-based routing
Approximately 301 tokens
Use when
When routing middleware execution using middleware.RouteHeaders() to apply security checks or access controls based on untrusted request headers.
Secure rules
Rule 1: Explicitly configure a default handler via RouteDefault when routing requests using RouteHeaders to prevent unmatched requests from bypassing security policies.
When using middleware.RouteHeaders(), requests that do not match configured header patterns or RouteAny rules will silently fall through to downstream handlers unless a fallback is provided. You must explicitly declare a RouteDefault handler to enforce safe default-deny or fallback policies for unrecognized header values.
Rule 2: Reject requests with unexpected header values in the RouteDefault handler.
Ensure that the handler passed to RouteDefault explicitly handles or rejects requests failing header verification rather than allowing them to proceed unrestricted.
Canonicalize and Sanitize Request Paths and Parameters
Approximately 354 tokens
Use when
When building HTTP routers and handling URL parameters, path extensions, or request path normalization in Chi
Secure rules
Rule 1: Apply middleware.CleanPath early on the top-level router to canonicalize request paths and remove duplicate slashes before route matching.
Use middleware.CleanPath as early middleware on the root router to canonicalize request paths by removing duplicate slashes before route matching occurs. Ensure it is added at the top-level router before sub-routers or route definitions, as it only modifies rctx.RoutePath when it has not yet been set.
r := chi.NewRouter()r.Use(middleware.CleanPath)r.Get("/users/{id}", getUserHandler)
Rule 2: Sanitize wildcard route parameters to prevent path traversal.
When extracting parameters from wildcard routes using chi.URLParam(r, "*"), explicitly clean and sanitize the resulting path prior to using it in file system access or downstream request forwarding.
r := chi.NewRouter()r.Get("/docs/*", func(w http.ResponseWriter, r *http.Request) { param := chi.URLParam(r, "*") cleanPath := filepath.Clean(param) if strings.HasPrefix(cleanPath, "..") || strings.Contains(cleanPath, "/..") { http.Error(w, "Invalid path", http.StatusBadRequest) return }})
Enforce Strict HTTP Method Restrictions and Semantics
Approximately 220 tokens
Use when
When registering endpoints and configuring routers to ensure requests strictly adhere to expected HTTP methods and protocol semantics.
Secure rules
Rule 1: Use explicit HTTP method routing functions to restrict endpoints to expected methods.
When defining endpoints, use HTTP method-specific routing functions such as r.Get and r.Post rather than generic catch-all handlers. Chi automatically responds with HTTP status 405 Method Not Allowed and populates the Allow header for requests using unhandled HTTP methods.
r := chi.NewRouter()// Explicitly register supported methods for the pathr.Get("/items", listItemsHandler)r.Post("/items", createItemHandler)// Unregistered methods (e.g., PUT /items) automatically receive HTTP 405
Configure explicit trusted proxy settings for client IP parsing
Approximately 274 tokens
Use when
Setting up HTTP routers behind reverse proxies or load balancers where client IP addresses must be securely resolved without falling back to vulnerable default header parsing.
Secure rules
Rule 1: Replace deprecated RealIP middleware with explicit ClientIPFrom* trust configurations tailored to your deployment topology.
The legacy RealIP middleware was vulnerable to IP spoofing because it blindly trusted leftmost X-Forwarded-For headers. Instead, use explicit trust configurations such as ClientIPFromXFF with explicit CIDR ranges to ensure attacker-injected header values cannot bypass IP-based access controls or logging.
r := chi.NewRouter()r.Use(middleware.ClientIPFromXFF( "13.32.0.0/15", "2600:9000::/28",))r.Get("/profile", func(w http.ResponseWriter, r *http.Request) { clientIP := middleware.GetClientIP(r.Context()) // Use clientIP for rate limiting or access control})
Limit Request Body Size and Concurrency for Resource-Heavy Endpoints
Approximately 276 tokens
Use when
When building endpoints or route groups that accept client payloads or execute resource-intensive operations and require protection against resource exhaustion and Denial of Service.
Secure rules
Rule 1: Apply request size limits and concurrency controls to prevent server resource exhaustion.
Use middleware.RequestSize to restrict allowable request body sizes and prevent unconstrained payload reading. Additionally, apply middleware.Throttle or middleware.ThrottleWithOpts to establish capacity ceilings and manage concurrent in-flight requests on expensive or sensitive routes.
Exclude Sensitive Data from Request Query Parameters and Logs
Approximately 240 tokens
Use when
When building HTTP endpoints and configuring logging middleware with chi to prevent plaintext exposure of secrets in request URIs.
Secure rules
Rule 1: Avoid passing secrets, tokens, or personal identifiers in URL query strings where middleware.Logger or DefaultLogFormatter can capture them in logs.
Transmit sensitive tokens via HTTP headers rather than query parameters so that plaintext credentials are not logged in standard output or log sinks by request logging middleware.
// Transmit sensitive tokens via HTTP headers rather than query parameters// BAD: GET /api/data?access_token=secret_key// GOOD: Pass token in Authorization headerr := chi.NewRouter()r.Use(middleware.Logger)r.Get("/api/data", func(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") // Process request securely...})
Order Security Middlewares and Controls First in the Chi Router Pipeline
Approximately 425 tokens
Use when
When registering global middleware, constructing middleware chains, or setting up routing middleware in Chi applications to ensure security mechanisms execute reliably.
Secure rules
Rule 1: Register global security middleware before declaring routes on the router mux.
Always add global middleware functions like authentication, rate-limiting, and logging via r.Use(...) prior to defining any routes or sub-routers. Chi enforces this ordering by design and will panic if Mux.Use is invoked after routes have already been configured.
r := chi.NewRouter()r.Use(middleware.Logger)r.Use(AuthMiddleware)r.Get("/protected", ProtectedHandler)
Rule 2: Mount global security controls before SupressNotFound in the middleware stack.
Ensure that mandatory security controls intended for all traffic, such as rate limiters and IP blockers, are mounted before middleware.SupressNotFound in the middleware chain. Placing SupressNotFound above security middleware causes unmapped requests to short-circuit and bypass those controls.
r := chi.NewRouter()r.Use(rateLimiterMiddleware)r.Use(middleware.SupressNotFound(r))r.Use(expensiveBusinessMiddleware)
Rule 3: Position the Logger middleware before Recoverer in the router pipeline.
Always register middleware.Logger before middleware.Recoverer so that a LogEntry is attached to the request context via WithLogEntry, allowing the recoverer middleware to successfully format and log panic stack traces.
r := chi.NewRouter()r.Use(middleware.Logger)r.Use(middleware.Recoverer)r.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok"))})