Skip to content

Tracing a Real Web Request in Caddy

This guide walks you through the exact path a web request takes when it hits a Caddy server.


The Request Flow Sequence

1. Sockets & Listeners (TCP/UDP connection accepted)
     └── 2. HTTP App Module (net/http server routes request to Caddy's engine)
           └── 3. Route Evaluator (matches URI paths, hosts, headers)
                 └── 4. Middleware Chain (executes handlers: headers, gzip, etc.)
                       └── 5. Terminal Handler (returns file or proxy response)

1. Sockets and Listeners

  • Where: listeners.go
  • What happens:
  • Caddy's socket manager maintains active listeners (TCP sockets) for incoming traffic on ports (like :80 or :443).
  • When a TCP connection is established, Caddy performs the TLS handshake (negotiating SSL certificates) using its caddytls module and hands off the raw decrypted connection to Go's standard HTTP server.

2. Entering Caddy's HTTP Server

  • Where: modules/caddyhttp/
  • What happens:
  • The HTTP app module manages Go's standard net/http server instances.
  • Every incoming request enters the server's entry handler, which constructs a request context and passes it into Caddy's custom request router.

3. Route Evaluator (Matching)

  • Where: modules/caddyhttp/routes.go
  • What happens:
  • Caddy checks your configuration for routes that match the request.
  • Matching is done using Matchers (such as checking if the host is example.com, if the path is /api/*, or if the request method is POST).
  • Once matched, Caddy collects the list of Handlers (middlewares) configured for that route.

4. The Middleware Chain

  • Where: modules/caddyhttp/middleware.go
  • What happens:
  • Go handles middleware chains using a design pattern where handlers wrap one another.
  • In Caddy, this is represented by a chain of modules executing a ServeHTTP(w http.ResponseWriter, r *http.Request, next Handler) function.
  • Each handler performs its action (e.g. logging the request, adding security headers, compressing the response) and then calls:
    err := next.ServeHTTP(w, r)
    
    This passes the request to the next middleware in the list.

5. The Terminal Handler

  • Where: modules/caddyhttp/fileserver/ or modules/caddyhttp/reverseproxy/
  • What happens:
  • The request reaches the end of the middleware chain (the terminal handler).
  • This handler generates the actual response body:
    • For a file server, it reads the requested file from the disk and writes the bytes to the response.
    • For a reverse proxy, it forwards the request to an upstream server (like a Python/Gunicorn app), waits for the reply, and sends it back to the user.
  • Once the response is written, execution flows backward up the middleware chain, completing the request lifecycle.