This page documents Caddy's HTTP handler and middleware architecture, including the core handler interfaces, middleware chaining mechanism, route compilation, and request/response flow. For details on specific built-in handlers, see Reverse Proxy, Static File Server, Response Encoding, and Templates. For information about request matching, see Matchers and Routing.
HTTP handlers are modular components that process HTTP requests and generate responses. Handlers can act as middleware (modifying requests/responses and passing control to the next handler) or as responders (originating the response). This document covers:
Caddy defines two primary handler interfaces that form the foundation of request processing:
The Handler interface is similar to Go's standard http.Handler but returns an error. Handlers may modify the request, generate a response, invoke other handlers, or return an error to trigger error handling.
Handler Diagram
Sources: modules/caddyhttp/caddyhttp.go59-76 modules/caddyhttp/caddyhttp.go94-112
The MiddlewareHandler interface takes a third parameter: the next handler in the chain. Middleware handlers typically:
next.ServeHTTP(w, r) to continue the chainHandlers that originate responses (e.g., reverse_proxy, file_server) typically do not invoke the next handler.
Sources: modules/caddyhttp/caddyhttp.go81-92
The Middleware type is a function that wraps a Handler, returning a new Handler. This enables functional composition of handlers into a chain. The wrapMiddleware() and wrapRoute() functions convert MiddlewareHandler and Route types into Middleware functions for chain compilation.
Sources: modules/caddyhttp/caddyhttp.go77-79 modules/caddyhttp/routes.go236-301 modules/caddyhttp/routes.go303-322
Caddy compiles handlers into a middleware chain where each handler can delegate to the next. The chain is built in reverse order and executes in forward order.
Handler Chain Compilation
Sources: modules/caddyhttp/routes.go220-234
The RouteList.Compile() method builds the handler chain:
Middleware function using wrapRoute()emptyHandler), wrap each middleware around the previous handlerSources: modules/caddyhttp/routes.go220-234
Requests flow down the chain (first handler to last), while responses flow up the chain (last handler to first):
Request/Response Flow Diagram
Sources: modules/caddyhttp/routes.go69-89
Key Insight: Handlers that modify responses must be placed before handlers that originate responses in the handler list, because the response flows back up. For example, encode must come before file_server so that encoding can wrap the response writer before the file is written.
Routes combine matchers with handler chains to enable conditional request processing.
Sources: modules/caddyhttp/routes.go25-100
The wrapRoute() function creates a middleware that:
MatcherSets.AnyMatchWithError() returns trueTerminal is true, replaces the next handler with emptyHandler or errorEmptyHandlerRoute Matching and Execution
Sources: modules/caddyhttp/routes.go236-301
Routes use MatcherSets, which are groups of MatcherSet. Each MatcherSet is a list of matchers that are AND'ed together, while MatcherSets are OR'ed together:
Sources: modules/caddyhttp/routes.go324-439
Handlers undergo a multi-stage lifecycle: unmarshaling, provisioning, validation, and compilation.
Handler Lifecycle
Sources: modules/caddyhttp/routes.go123-170 modules/caddyhttp/routes.go303-322 modules/caddyhttp/app.go366-375
The Route.ProvisionHandlers() method:
HandlersRaw JSONroute.HandlerswrapMiddleware()route.middlewareSources: modules/caddyhttp/routes.go149-170
When metrics are enabled, wrapMiddleware() wraps handlers with metricsInstrumentedHandler to track handler invocations and durations:
Sources: modules/caddyhttp/routes.go303-322
When a request enters Caddy, it flows through several layers before reaching the handler chain.
The Server.ServeHTTP() method is the entry point for all HTTP requests. It:
Server headerAlt-Svc header if enabledPrepareRequest() (adds context, replacer, vars)tlsApp.HandleHTTPChallenge()s.serveHTTP() to execute the primary handler chains.errorHandlerChain if an error is returnedServer Request Processing
Sources: modules/caddyhttp/server.go299-459 modules/caddyhttp/server.go920-946
The PrepareRequest() function enriches the request with context values needed throughout the handler chain:
| Context Key | Value | Purpose |
|---|---|---|
caddy.ReplacerCtxKey | *caddy.Replacer | Variable substitution |
ServerCtxKey | *Server | Access to server configuration |
VarsCtxKey | map[string]any | Handler-specific variables, including TrustedProxyVarKey and ClientIPVarKey |
routeGroupCtxKey | map[string]struct{} | Track satisfied route groups |
OriginalRequestCtxKey | http.Request | Original request for error handling |
ExtraLogFieldsCtxKey | *ExtraLogFields | Additional fields for access logs |
Sources: modules/caddyhttp/server.go920-946 modules/caddyhttp/server.go969-1017
The Server.serveHTTP() method performs validation and executes the primary handler chain:
s.primaryHandlerChain.ServeHTTP(w, r)wrapPrimaryRoute(), which runs enforcementHandler() for security checks (e.g., strict SNI-Host matching)Sources: modules/caddyhttp/server.go461-488 modules/caddyhttp/server.go490-519
Caddy includes several built-in handlers that cover common web server tasks. This section provides an overview; see the linked subsections for detailed information.
Built-in handlers are registered in the http.handlers namespace:
| Handler | Module ID | Purpose | Details |
|---|---|---|---|
reverse_proxy | http.handlers.reverse_proxy | Proxies requests to upstream servers with load balancing and health checks | Reverse Proxy |
file_server | http.handlers.file_server | Serves static files and directory listings | Static File Server |
encode | http.handlers.encode | Compresses responses dynamically | Response Encoding |
templates | http.handlers.templates | Executes Go templates in responses | Templates |
rewrite | http.handlers.rewrite | Rewrites request URI and headers | - |
headers | http.handlers.headers | Manipulates request/response headers | - |
respond | http.handlers.respond | Writes a static response | - |
error | http.handlers.error | Returns an error to trigger error handling | - |
subroute | http.handlers.subroute | Executes a nested route list | - |
vars | http.handlers.vars | Sets variables in the request context | - |
Handlers can be categorized by their behavior:
Middleware Handlers (call next handler):
encode - Wraps response writer, calls nexttemplates - Wraps response writer, calls nextheaders - Modifies headers, calls nextrewrite - Modifies request, calls nextvars - Sets variables, calls nextResponder Handlers (terminate chain):
reverse_proxy - Proxies to upstream, writes responsefile_server - Reads from filesystem, writes responserespond - Writes static responseControl Flow Handlers:
subroute - Executes nested routeserror - Triggers error handlingSources: File paths for various handlers can be found in the modules/caddyhttp/ directory.
Caddy provides a sophisticated error handling system that allows custom error routes to be executed when handlers return errors.
When a handler returns an error:
Server.ServeHTTP() receives the error from serveHTTP()OriginalRequestCtxKeyHTTPErrorConfig.WithError() adds error to request context and sets replacer placeholderss.errorHandlerChain.ServeHTTP(w, r) is invokedError Handling Flow
Sources: modules/caddyhttp/server.go382-458 modules/caddyhttp/server.go732-776
The HTTPErrorConfig.WithError() method sets the following placeholders for use in error routes:
| Placeholder | Description |
|---|---|
{http.error} | The full error value |
{http.error.status_code} | HTTP status code from HandlerError |
{http.error.status_text} | Status text for the status code |
{http.error.message} | Error message string |
{http.error.trace} | Stack trace or error origin |
{http.error.id} | Unique identifier for this error occurrence |
Sources: modules/caddyhttp/server.go754-776
Handlers should return HandlerError to provide structured error information:
This allows error routes to make decisions based on error properties (e.g., serve different error pages for 404 vs 500).
Sources: modules/caddyhttp/server.go754-776
Custom handlers can be implemented as Caddy modules by implementing the MiddlewareHandler interface.
Middleware Handlers should:
next.ServeHTTP(w, r) to continue the chainResponder Handlers should:
next.ServeHTTP()All Handlers should:
HandlerError for structured error informationSources: modules/caddyhttp/caddyhttp.go59-92
Caddy's handler and middleware system provides a flexible, composable architecture for HTTP request processing:
Handler for simple handlers, MiddlewareHandler for handlers that delegate to nextMiddleware function typeThe architecture balances simplicity with power, enabling both straightforward configurations and complex request processing pipelines.
Sources: modules/caddyhttp/caddyhttp.go15-342 modules/caddyhttp/routes.go15-453 modules/caddyhttp/server.go15-1619 modules/caddyhttp/app.go15-1050
Refresh this wiki