Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Caddy Web Server — Administrator & Developer Guide

Generated May 2026 from caddyserver/caddy at 41aee97

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

1. Architecture Overview

Caddy is a modular, extensible server platform written in Go. At its core, Caddy is a configuration manager: it loads a JSON configuration document, provisions modules according to that configuration, starts the configured applications, and manages their lifecycle. The standard distribution bundles modules for HTTP serving, TLS certificate management, PKI (certificate authority), logging, metrics, and events.

Architecture Diagram

┌──────────────────────────────────────────────────┐
│                   caddy process                   │
│                                                  │
│  ┌──────────────────────────────────────────┐    │
│  │        Admin API (localhost:2019)         │    │
│  │  /load  /config/  /stop  /metrics        │    │
│  └──────────┬───────────────────────────────┘    │
│             │ config changes                     │
│  ┌──────────▼───────────────────────────────┐    │
│  │         Config Manager (caddy.go)         │    │
│  │  Load → Validate → Provision → Start      │    │
│  │  Context, Replacer, UsagePool, Modules    │    │
│  └──────────┬───────────────────────────────┘    │
│             │ provisions apps                    │
│  ┌──────────▼──────────────────┐ ┌────────────┐ │
│  │    HTTP App (caddyhttp)     │ │  TLS App   │ │
│  │  Servers → Routes → Handlers│ │ (caddytls) │ │
│  │  Matchers, Subroutes        │ │ CertMagic  │ │
│  │  AutoHTTPS, Metrics         │ │ ACME/ZeroSSL│ │
│  │  HTTP/1.1, HTTP/2, HTTP/3   │ │ On-demand  │ │
│  └──────────┬──────────────────┘ └──────┬─────┘ │
│             │                           │        │
│  ┌──────────▼──────────┐  ┌─────────────▼─────┐ │
│  │   Logging App       │  │    PKI App        │ │
│  │   (modules/logging) │  │   (caddypki)      │ │
│  │   FileWriter, Net   │  │   Local CA, ACME  │ │
│  │   Filters, Encoders │  │   Server          │ │
│  └─────────────────────┘  └───────────────────┘ │
│                                                  │
│  ┌──────────────────────┐  ┌──────────────────┐ │
│  │   Events App         │  │   Metrics App    │ │
│  │   (caddyevents)      │  │   Prometheus     │ │
│  └──────────────────────┘  └──────────────────┘ │
└──────────────────────────────────────────────────┘

Component Inventory

Component Path Role
Core runtime caddy.go, context.go, modules.go Config lifecycle, module registry, context propagation
Admin API admin.go REST API for runtime config management
HTTP App modules/caddyhttp/ HTTP server, routing, handlers, matchers, auto-HTTPS
TLS App modules/caddytls/ Certificate management, ACME issuers, connection policies
PKI App modules/caddypki/ Internal certificate authority, ACME server
Logging modules/logging/ Structured logging with encoders, writers, filters
Metrics modules/metrics/ Prometheus metrics exposition
Events modules/caddyevents/ Event subscription and handler dispatch
Caddyfile adapter caddyconfig/caddyfile/, caddyconfig/httpcaddyfile/ Parse Caddyfile → JSON config
CLI cmd/ Cobra-based command-line interface
File storage modules/filestorage/ Default certificate/key storage on disk
Filesystem modules/caddyfs/ Virtual filesystem abstraction

Technology Stack

Layer Technology
Language Go 1.25+
CLI framework spf13/cobra + pflag
HTTP server Go net/http + golang.org/x/net/http2
HTTP/3 (QUIC) quic-go/quic-go
TLS/ACME caddyserver/certmagic, mholt/acmez
Certificate issuers Let's Encrypt, ZeroSSL (built-in), smallstep/certificates
PKI smallstep/certificates, go.step.sm/crypto
Logging uber-go/zap
Metrics prometheus/client_golang
Tracing OpenTelemetry (contrib/instrumentation/net/http/otelhttp)
Template engine yuin/goldmark (Markdown), Go text/template
Compression klauspost/compress (gzip, zstd), brotli
CEL expressions google/cel-go
DNS miekg/dns, libdns/libdns
Config formats JSON (native), YAML (BurntSushi/toml, gopkg.in/yaml.v3)
PROXY protocol pires/go-proxyproto

Data Flow: Request Processing

Client Request
  ↓
Listener (TCP/QUIC, optional PROXY protocol wrapper)
  ↓
TLS Handshake (connection policy selection via SNI/ALPN matchers)
  ↓
HTTP Server (server.go: ServeHTTP)
  ↓
Route Matching (matchers evaluated in order: host, path, method, header, expression, etc.)
  ↓
Handler Chain (matched route's handlers execute in order)
  ↓
Response (with optional response matchers for intercept/handle_response)

Data Flow: Configuration Lifecycle

Config Input (JSON or Caddyfile)
  ↓
Adapter (if non-JSON: caddyfile adapter converts to JSON)
  ↓
caddy.Load() → validates JSON, builds Config struct
  ↓
Context.LoadModulesFromConfig() → instantiates module values
  ↓
Module.Provision(ctx) → each module sets up internal state
  ↓
Module.Validate() → optional validation
  ↓
App.Start() → apps begin serving
  ↓
[Running — admin API accepts changes → caddy.Load() again]
  ↓
App.Stop() → graceful shutdown with optional grace period

2. System Requirements

Requirement Detail
Operating systems Linux, macOS, Windows, FreeBSD
Architectures amd64, arm64, armv5/v6/v7, s390x, ppc64le, riscv64
Go version (source) 1.25.0 or later
Minimum memory ~64 MB (increases with number of managed certificates)
Ports 80 (HTTP challenges/redirects), 443 (HTTPS), 443/UDP (HTTP/3), 2019 (admin API)
Privileges cap_net_bind_service on Linux for ports < 1024
DNS Public DNS resolution required for automatic ACME certificates

Build Tags

Tag Effect
nobadger Exclude BadgerDB storage backend
nomysql Exclude MySQL storage backend
nopgx Exclude PostgreSQL storage backend

These are set by default in release builds to reduce binary size.

3. Configuration Reference

Caddy's native configuration format is JSON. The Caddyfile is an alternative syntax that is adapted to JSON before being applied. This section documents the JSON structure.

Top-Level Structure

{
  "admin": { ... },
  "logging": { ... },
  "storage": { ... },
  "apps": {
    "http": { ... },
    "tls": { ... },
    "pki": { ... },
    "events": { ... }
  }
}

Admin Configuration (admin)

Field Type Default Description
disabled bool false Disable admin endpoint entirely
listen string localhost:2019 Admin API listen address (overridable via CADDY_ADMIN env)
enforce_origin bool false Enforce CORS origin checking
origins []string [listen address] Allowed origins for CORS
config.persist *bool true Auto-save active config to disk
config.load object — Dynamic config loader module (experimental)
config.load_delay duration — Delay before loading dynamic config (experimental)
identity.identifiers []string — Server identity names/IPs for remote admin
identity.issuers []object ZeroSSL + LE Certificate issuers for server identity
remote.listen string :2021 Remote admin listener (experimental)
remote.access_control []object — mTLS access control for remote admin (experimental)

Important

The CADDY_ADMIN environment variable overrides the default admin listen address (localhost:2019). This is primarily for package maintainers to set platform-appropriate defaults.

HTTP App Configuration (apps.http)

Field Type Default Description
http_port int 80 Port for HTTP (redirects, ACME challenges)
https_port int 443 Port for HTTPS
grace_period duration 0 (eternal) Max time for active connections during shutdown
shutdown_delay duration 0 Delay before shutdown begins (for LB draining)
servers map[string]Server — Named server configurations
metrics object — Enable HTTP metrics (experimental)

Server Configuration (apps.http.servers.*)

Field Type Default Description
listen []string — Listener addresses (e.g., ":443")
routes []Route — Ordered routing rules
errors object — Error handling routes
named_routes map — Reusable named route definitions
listener_wrappers []object — Listener middleware (e.g., proxy_protocol, tls)
protocols []string ["h1", "h2", "h3"] Enabled protocols
strict_sni_host *bool — Require TLS SNI to match HTTP Host
trusted_proxies object — Trusted proxy IP ranges for X-Forwarded-*
metrics object — Per-server metrics configuration
logs object — Access log configuration per server

TLS App Configuration (apps.tls)

Field Type Default Description
certificates object — Manual certificate loading (file, folder, PEM, storage)
automation object — ACME automation policies
automation.policies []AutomationPolicy — Per-subject certificate management policies
automation.on_demand object — On-demand TLS configuration
session_tickets object — TLS session ticket configuration
cache object — Certificate cache options
ech object — Encrypted Client Hello configuration

Automation Policy

Field Type Default Description
subjects []string — Hostnames this policy applies to
issuers []object ACME (LE + ZeroSSL) Certificate issuers
key_type string "" (CA default) Key type: ed25519, p256, p384, rsa2048, rsa4096
must_staple bool false Request OCSP Must-Staple
renewal_window_ratio float — Ratio of cert lifetime at which to renew
storage object — Per-policy storage module
on_demand bool false Enable on-demand certificate issuance

Warning

On-demand TLS requires a permission module (or deprecated ask endpoint) to prevent abuse. Without permission controls, anyone could trigger certificate issuance for arbitrary domains by connecting to your server.

PKI App Configuration (apps.pki)

Field Type Default Description
certificate_authorities map[string]CA — Named CA configurations
certificate_authorities.*.root_common_name string "Caddy Local Authority - <id>" Root CA CN
certificate_authorities.*.intermediate_common_name string "Caddy Local Authority - <id> Intermediate" Intermediate CA CN
certificate_authorities.*.intermediate_lifetime duration — Intermediate cert validity period
certificate_authorities.*.install_trust bool true Install root into system trust store

Logging Configuration (logging)

Field Type Default Description
sink object — Sink log (errors from log system itself)
logs map[string]CustomLog — Named log configurations
logs.default CustomLog stderr, console The default log
logs.*.writer object — Log output (file, net, stderr, stdout)
logs.*.encoder object — Log format (json, console, filter, append, journald)
logs.*.level string "INFO" Min log level: DEBUG, INFO, WARN, ERROR, PANIC, FATAL
logs.*.include []string — Logger name prefixes to include
logs.*.exclude []string — Logger name prefixes to exclude
logs.*.sampling object — Log sampling configuration

File Writer Options

Field Type Default Description
filename string — Log file path
roll_size_mb int 100 Max file size before rolling (MB)
roll_keep int 10 Number of rolled files to keep
roll_keep_for duration 2160h (90 days) Max age of rolled files
roll_local_time bool false Use local time in rolled filenames
roll_compression string — Compression for rolled files: gzip, zstd
mode string 0600 File permissions (octal)
dir_mode string 0700 Directory permissions (octal)

Log Filter Types

Filter Module ID Purpose
Delete caddy.logging.encoders.filter.delete Remove a field entirely
Hash caddy.logging.encoders.filter.hash SHA-256 hash a field value
Replace caddy.logging.encoders.filter.replace Replace field with static value
IP Mask caddy.logging.encoders.filter.ip_mask Mask IPv4/IPv6 addresses
Query caddy.logging.encoders.filter.query Filter/redact query parameters
Cookie caddy.logging.encoders.filter.cookie Filter/redact cookies
Regexp caddy.logging.encoders.filter.regexp Regex-based field modification
Multi-Regexp caddy.logging.encoders.filter.multi_regexp Multiple regex operations on a field
Rename caddy.logging.encoders.filter.rename Rename a field

Environment Variables

Variable Purpose Default
CADDY_ADMIN Override default admin listen address localhost:2019
HOME / USERPROFILE Home directory for data/config paths System default
XDG_DATA_HOME Override data directory ~/.local/share
XDG_CONFIG_HOME Override config directory ~/.config
XDG_CACHE_HOME Override cache directory ~/.cache
NO_COLOR Disable colored console output —
USERAGENT Prepend custom string to ACME User-Agent —

Note

Caddy follows the XDG Base Directory Specification on Linux. On Windows, it uses %AppData% and %LocalAppData%. On macOS, it uses ~/Library/Application Support.

CLI Commands Reference

Command Usage Description
run --config <path> [--adapter <name>] [--envfile <path>] [--environ] [--resume] [--watch] [--pidfile <file>] Start in foreground (daemon mode)
start [--config <path>] [--adapter <name>] [--envfile <path>] [--watch] [--pidfile <file>] Start in background
stop [--config <path>] [--adapter <name>] [--address <interface>] Gracefully stop running instance
reload --config <path> [--adapter <name>] [--address <interface>] [--force] Reload configuration
adapt --config <path> [--adapter <name>] [--pretty] [--validate] [--envfile <path>] Convert config to JSON
validate --config <path> [--adapter <name>] [--envfile <path>] Validate config without starting
fmt [--overwrite] [--diff] [<path>] Format Caddyfile
version — Print version
build-info — Print Go build info
list-modules [--packages] [--versions] [--skip-standard] [--json] List installed modules
environ [--envfile <path>] Print environment
storage export/import --config <path> --output/--input <path> Export/import storage (experimental)
upgrade [--keep-backup] Self-upgrade binary (experimental)
add-package <package[@version]...> [--keep-backup] Add modules (experimental)
remove-package <packages...> [--keep-backup] Remove modules (experimental)
manpage --directory <path> Generate man pages
completion [bash|zsh|fish|powershell] Generate shell completions

Additional commands registered by standard modules:

Command Module Description
file-server caddyhttp/fileserver Quick static file server
reverse-proxy caddyhttp/reverseproxy Quick reverse proxy
respond caddyhttp/staticresp Quick static response server
trust caddypki Install CA root cert to trust store
untrust caddypki Remove CA root cert from trust store
hash-password caddyhttp/caddyauth Hash a password for basicauth

4. Deployment & Scaling

systemd Deployment (Linux)

The Debian package installs two systemd unit files:

  • caddy.service — runs Caddy with a Caddyfile (/etc/caddy/Caddyfile)
  • caddy-api.service — runs Caddy with no initial config (API-driven configuration)
sudo systemctl enable --now caddy

Configuration path: /etc/caddy/Caddyfile Data directory: /var/lib/caddy/.local/share/caddy/ (certificates, keys) Log output: journal (journalctl -u caddy)

Docker Deployment

version: "3.9"
services:
  caddy:
    image: caddy:latest
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    restart: unless-stopped

volumes:
  caddy_data:
  caddy_config:

Important

Always persist /data and /config volumes. /data contains TLS certificates and private keys — losing this volume triggers re-issuance, which may hit ACME rate limits.

Graceful Shutdown & Zero-Downtime Reloads

Caddy supports two-phase shutdown:

  1. Shutdown Delay (shutdown_delay): Caddy continues accepting new connections while downstream load balancers drain this instance. The placeholders {http.shutting_down} and {http.time_until_shutdown} are available for health check endpoints during this phase.

  2. Grace Period (grace_period): No new connections accepted. Idle connections close immediately. Active connections are given the full grace period to complete. After the grace period, connections are forcefully terminated.

Config reload (caddy reload) is seamless — the new config is loaded, validated, and swapped in atomically. Active connections continue on the old config until they complete.

Tip

For zero-downtime deployments behind a load balancer, set shutdown_delay to a value slightly longer than your load balancer's health check interval. This ensures the LB stops routing traffic to this instance before the grace period begins.

Windows Service

Caddy can run as a Windows service. The service_windows.go module implements the Windows Service Control Manager interface. Caddy registers as a service handler and responds to stop/shutdown signals from the SCM.

Port Binding

On Linux, binding to ports below 1024 requires either root or the cap_net_bind_service capability:

sudo setcap cap_net_bind_service=+ep /usr/bin/caddy

The package installation handles this automatically. A helper script at cmd/caddy/setcap.sh is provided in the source.

Resource Limits

Caddy automatically adjusts resource limits on startup:

  • GOMAXPROCS: Set to match Linux container CPU quota via uber-go/automaxprocs
  • GOMEMLIMIT: Set to match container/system memory via KimMachineGun/automemlimit (reads cgroup limits, falls back to system memory)

These ensure Caddy behaves well in containerized environments without manual tuning.

5. Operations

Admin API

The admin API listens on localhost:2019 by default and provides full control over the running Caddy instance.

Core endpoints:

Method Path Description
POST /load Load a complete new configuration
GET /config/[path] Read config at path
POST /config/[path] Set config at path
PUT /config/[path] Create or replace config at path
PATCH /config/[path] Replace config at path
DELETE /config/[path] Delete config at path
POST /stop Gracefully stop Caddy
GET /reverse_proxy/upstreams List reverse proxy upstream status
GET /pki/ca/<id> Get CA info
GET /pki/ca/<id>/certificates Get CA certificates

Security:

  • Local endpoint (plaintext): Protected by origin checking. Only localhost origins are allowed by default. CORS headers enforce same-origin policy. WebSocket connections are rejected. Content-Type must be application/json for request bodies.
  • Remote endpoint (mTLS, experimental): Requires identity configuration with TLS certificates. Access controlled via public key allowlists and path/method permissions.
  • The If-Match header supports ETag-based conditional updates to prevent config race conditions.

Note

The admin API rejects requests with non-localhost origins by default. If accessing from a different host, configure the origins list in the admin config.

Config Autosave

By default, Caddy saves its active configuration to autosave.json in the config directory after every successful load. This enables the --resume flag to restore the last known-good configuration on restart. Disable with admin.config.persist: false.

Monitoring with Prometheus

Caddy exposes Prometheus metrics at the admin API endpoint. Enable per-server HTTP metrics:

{
  "apps": {
    "http": {
      "metrics": {},
      "servers": {
        "srv0": {
          "metrics": {}
        }
      }
    }
  }
}

Or via Caddyfile:

{
    servers {
        metrics
    }
}

Scrape from http://localhost:2019/metrics.

Tip

Use the metrics global option or the metrics Caddyfile directive to expose per-host HTTP metrics. Combine with OpenTelemetry exporters (OTLP) for distributed tracing.

OpenTelemetry Tracing

Caddy supports distributed tracing via OpenTelemetry:

example.com {
    tracing {
        span my-span
    }
    reverse_proxy backend:8080
}

The tracing module uses go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp and supports automatic export via OTEL_EXPORTER_* environment variables. Propagation is auto-configured via go.opentelemetry.io/contrib/propagators/autoprop.

Health Checks

For load balancer integration, use a dedicated health endpoint:

:8080 {
    respond /health 200
}

During shutdown (when shutdown_delay is configured), use the {http.shutting_down} placeholder to signal health check failures:

@health path /health
handle @health {
    @shuttingDown expression {http.shutting_down}
    respond @shuttingDown 503
    respond 200
}

Signal Handling

Signal Behavior
SIGINT / SIGTERM Graceful shutdown
SIGUSR1 Reload config from the same source file

On Windows, Caddy handles SCM stop/shutdown commands.

Storage

Caddy stores certificates, OCSP staples, and other assets in a configurable storage backend. The default is file_system:

{
  "storage": {
    "module": "file_system",
    "root": "/var/lib/caddy"
  }
}

The caddy storage export and caddy storage import commands allow migrating storage contents between instances or backends as tar archives.

Warning

The storage directory contains TLS private keys. Ensure proper file permissions (default: 0600 for files, 0700 for directories). Back up the storage directory regularly.

Certificate Lifecycle

  1. Automatic issuance: Caddy provisions certificates for qualified hostnames at startup using ACME (Let's Encrypt primary, ZeroSSL fallback).
  2. Renewal: Certificates are renewed before expiry based on the renewal_window_ratio (default: start renewing when less than 1/3 of lifetime remains).
  3. On-demand TLS: Certificates can be obtained on first TLS handshake for the domain, controlled by a permission module.
  4. OCSP stapling: Automatic OCSP staple fetching and caching.
  5. Storage: All certificate material is persisted to the configured storage backend.

6. Performance

HTTP/3 (QUIC)

HTTP/3 is enabled by default on HTTPS servers. Caddy creates a QUIC listener on the same port as HTTPS (typically 443/UDP) and advertises HTTP/3 availability via the Alt-Svc response header.

Disable for specific servers:

{
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "protocols": ["h1", "h2"]
        }
      }
    }
  }
}

Compression

Caddy supports response compression with gzip, Zstandard (zstd), and Brotli. The encode handler negotiates the best encoding based on the client's Accept-Encoding header.

Pre-compressed files (.gz, .br, .zst) are served when available, avoiding runtime compression overhead.

Connection Pooling

The reverse proxy maintains connection pools to upstream backends with configurable:

  • Maximum idle connections
  • Idle timeout
  • Response header timeout
  • Buffer sizes (read/write)
  • Stream buffer size for WebSocket/streaming

TLS Session Tickets

Caddy supports TLS session ticket rotation for improved TLS handshake performance. The SessionTicketService manages automatic key rotation with configurable intervals and key lifetime. A distributed STEK (Session Ticket Encryption Key) provider is available for multi-instance deployments.

7. Security

Automatic HTTPS

Caddy obtains and manages TLS certificates automatically for all qualifying hostnames. This is the primary security feature: HTTPS by default, with no configuration required.

Challenge types supported:

Challenge Port Use Case
HTTP-01 80 Default; requires port 80 reachable
TLS-ALPN-01 443 Used when port 80 is unavailable
DNS-01 — Private networks, wildcard certs (requires DNS provider plugin)

Caution

By using Caddy, you automatically agree to the ACME CA's terms of service. This is intentional for non-interactive operation. If you need to review terms before agreeing, configure issuers manually.

Client Certificate Authentication (mTLS)

Caddy supports mutual TLS with fine-grained trust configuration:

  • Trust pools: Inline PEM, file-based, folder-based, PKI root/intermediate, storage-backed, system, HTTP-fetched, or combined
  • Client auth modes: require, request, require_and_verify, verify_if_given
  • Leaf certificate verification: Verify specific client certificates by fingerprint via file, folder, PEM, or storage loaders
  • Custom verifiers: External certificate verification modules

Encrypted Client Hello (ECH)

Caddy supports ECH (Encrypted Client Hello) for enhanced privacy. ECH encrypts the SNI in the TLS ClientHello, preventing network observers from seeing which hostname the client is connecting to. Configuration manages ECH key generation, rotation, and DNS publication.

Warning

ECH is an advanced privacy feature. It requires DNS HTTPS record publication and client support. Misconfiguration can prevent clients from connecting.

Admin API Security

The local admin endpoint uses multiple security layers:

  1. Origin enforcement: Validates Origin header matches expected values
  2. Host validation: Rejects requests with non-matching Host headers (except for Unix/fd sockets)
  3. Content-Type validation: Requires application/json for request bodies
  4. CORS prevention: Blocks no-cors mode requests
  5. WebSocket rejection: WebSocket upgrade requests are denied
  6. Null origin rejection: Requests with Origin: null are rejected

The remote admin endpoint (experimental) adds:

  • Mutual TLS: Client certificates required
  • Public key authorization: Access controlled by base64-encoded DER public keys
  • Path/method permissions: Fine-grained API access control

PROXY Protocol

Caddy supports HAProxy's PROXY protocol (v1 and v2) via the proxy_protocol listener wrapper, allowing it to receive the real client IP when deployed behind a layer 4 load balancer:

{
    servers {
        listener_wrappers {
            proxy_protocol {
                timeout 5s
                allow 10.0.0.0/8
            }
            tls
        }
    }
}

Important

The proxy_protocol listener wrapper must be placed before the tls wrapper in the listener wrapper chain, as PROXY protocol encapsulates TLS data.

Host Validation

Caddy validates the Host header on incoming requests:

  • Empty Host on HTTP/1.1 is rejected per RFC 9112
  • When strict_sni_host is enabled, the TLS ServerName must match the HTTP Host header
  • The admin API independently validates host/origin headers

IP-Based Matchers

Two IP matchers are available:

  • remote_ip: Matches the client's IP address (from the network connection)
  • client_ip: Matches the client's IP after parsing X-Forwarded-For headers (requires trusted_proxies configuration)

Warning

Using client_ip without trusted_proxies configuration can allow IP spoofing. Always configure the trusted proxy ranges when using client_ip behind a load balancer.

8. Developer Internals

Repository Structure

caddy/
├── admin.go                   # Admin API server and handlers
├── caddy.go                   # Core config lifecycle (Load, Run, Stop)
├── context.go                 # Module provisioning context
├── modules.go                 # Module registry
├── replacer.go                # Placeholder expansion engine
├── listeners.go               # Network address parsing, listener management
├── logging.go                 # Logging configuration structs
├── storage.go                 # Storage directory resolution
├── usagepool.go               # Reference-counted resource sharing
├── cmd/
│   ├── caddy/main.go          # Binary entry point
│   ├── cobra.go               # Cobra root command setup
│   ├── commands.go            # CLI subcommand registration
│   ├── commandfuncs.go        # CLI command implementations
│   ├── packagesfuncs.go       # upgrade/add-package/remove-package
│   └── storagefuncs.go        # storage export/import
├── caddyconfig/
│   ├── configadapters.go      # Config adapter registry
│   ├── httploader.go          # HTTP-based config loader
│   ├── load.go                # /load and /adapt admin endpoints
│   ├── caddyfile/             # Caddyfile lexer, parser, formatter
│   └── httpcaddyfile/         # HTTP-specific Caddyfile adapter
├── modules/
│   ├── caddyhttp/             # HTTP app (34 files, 457K chars)
│   │   ├── app.go             # HTTP app provisioning and lifecycle
│   │   ├── server.go          # HTTP server (ServeHTTP, listeners)
│   │   ├── autohttps.go       # Automatic HTTPS logic
│   │   ├── routes.go          # Route matching and handler chains
│   │   ├── matchers.go        # Request matchers (host, path, method, etc.)
│   │   ├── celmatcher.go      # CEL expression matcher
│   │   ├── staticresp.go      # Static response handler
│   │   ├── subroute.go        # Subroute handler
│   │   ├── vars.go            # Variables handler (vars, map)
│   │   ├── reverseproxy/      # Reverse proxy handler
│   │   ├── fileserver/        # Static file server + browse
│   │   ├── caddyauth/         # Basic authentication
│   │   ├── encode/            # Response encoding (gzip, zstd, brotli)
│   │   ├── headers/           # Header manipulation
│   │   ├── rewrite/           # URI rewrite
│   │   ├── templates/         # Template rendering
│   │   ├── tracing/           # OpenTelemetry tracing
│   │   ├── push/              # HTTP/2 server push
│   │   ├── map/               # Map directive
│   │   ├── requestbody/       # Request body limits
│   │   ├── intercept/         # Response interception
│   │   ├── logging/           # Access log append
│   │   └── proxyprotocol/     # PROXY protocol listener wrapper
│   ├── caddytls/              # TLS app (30 files, 333K chars)
│   │   ├── tls.go             # TLS app lifecycle
│   │   ├── acmeissuer.go      # ACME certificate issuer
│   │   ├── automation.go      # Automation policies
│   │   ├── connpolicy.go      # TLS connection policies
│   │   ├── capools.go         # CA certificate pool providers
│   │   ├── ech.go             # Encrypted Client Hello
│   │   ├── matchers.go        # TLS handshake matchers
│   │   ├── ondemand.go        # On-demand TLS
│   │   ├── certmanagers.go    # External cert managers (Tailscale, HTTP)
│   │   └── zerosslissuer.go   # ZeroSSL-specific issuer
│   ├── caddypki/              # PKI app (9 files, 57K chars)
│   │   ├── pki.go             # PKI app provisioning
│   │   ├── ca.go              # Certificate Authority management
│   │   ├── adminapi.go        # PKI admin API endpoints
│   │   ├── acmeserver/        # Embedded ACME server
│   │   └── command.go         # trust/untrust CLI commands
│   ├── caddyevents/           # Events app
│   ├── logging/               # Logging encoders, writers, filters
│   ├── metrics/               # Prometheus metrics
│   ├── filestorage/           # File system storage module
│   └── caddyfs/               # Virtual filesystem
├── internal/                  # Internal utilities
│   ├── logbuffer.go           # Buffered logging for startup
│   ├── logmarshalers.go       # HTTP header log marshaling
│   ├── sockets.go             # Unix socket permission parsing
│   ├── metrics/               # Internal metrics helpers
│   └── filesystems/           # OS filesystem wrapper
├── caddytest/                 # Integration test framework
└── notify/                    # Platform-specific notifications (systemd, etc.)

Module System

Caddy's module system is the foundation of its extensibility:

  1. Registration: Modules call caddy.RegisterModule() in their init() function, providing a ModuleInfo with an ID and constructor.

  2. Module IDs: Hierarchical, dot-separated namespaces (e.g., http, http.handlers.reverse_proxy, tls.issuance.acme, caddy.logging.encoders.json).

  3. Lifecycle:

    • New() — constructor returns zero-value module
    • Provision(ctx caddy.Context) — set up internal state using the config context
    • Validate() (optional) — check configuration validity
    • Start() (for apps) — begin serving
    • Stop() / Cleanup() — release resources
  4. Context: caddy.Context wraps context.Context and provides:

    • LoadModule() — load and provision a sub-module from JSON
    • App() — get a reference to another app module
    • Logger() — get the module's logger
    • Module config unmarshaling with inline key support
  5. Module map (as of extraction): 228 structs, 985 functions across the standard distribution.

Caddyfile Adapter Architecture

The Caddyfile adapter (caddyconfig/caddyfile/ + caddyconfig/httpcaddyfile/) converts human-readable Caddyfile syntax to Caddy's native JSON:

Caddyfile text
  ↓
Lexer (lexer.go) → tokens (with heredoc support)
  ↓
Parser (parse.go) → ServerBlock structs
  ↓
HTTP Adapter (httptype.go) → JSON config
  ↓
  Directive handlers: each directive registers an UnmarshalCaddyfile method
  Address parsing: supports schemes, hosts, ports, paths, wildcards
  Matcher compilation: @name definitions compiled to JSON matcher sets
  Import/snippet resolution: recursive file imports with glob support
  Global options: parsed by registered option handlers

Directive ordering: The httpcaddyfile/directives.go file defines a strict ordering for directives within a site block. Directives are sorted according to this order to ensure correct handler chain construction. Custom directives can be inserted using the order global option.

Replacer (Placeholder) System

The Replacer (replacer.go) provides dynamic value substitution throughout Caddy's configuration and handlers:

  • Global placeholders: {system.hostname}, {system.slash}, {system.os}, {system.arch}, {time.now.*}, {env.*}, {file.*}
  • HTTP placeholders: Extensive set covering request, response, TLS, and variables (see HTTP App source comments above)
  • Nested placeholders: Resolved recursively with depth limit (100) to prevent infinite loops
  • Custom providers: Modules can register custom placeholder providers via caddy.NewReplacer() and Map()

UsagePool

The UsagePool (usagepool.go) is a reference-counted resource pool that ensures shared resources (like listeners, certificates) are properly cleaned up when the last user releases them. This is critical for Caddy's hot-reload capability — multiple config generations can share the same listener without re-binding.

Network Address Parsing

The NetworkAddress struct (listeners.go) provides comprehensive address parsing:

  • Supports networks: tcp, tcp4, tcp6, udp, udp4, udp6, unix, unixgram, unixpacket, fd, fdgram
  • Port ranges: localhost:8080-8085 expands to individual listeners
  • Unix socket permissions: unix//path|0660 syntax
  • File descriptor passing: fd/3 for socket activation

Reverse Proxy Internals

The reverse proxy (modules/caddyhttp/reverseproxy/) is one of the most complex modules:

Load balancing policies:

Policy Description
random Random upstream selection
random_choose Random choice from N candidates
least_conn Fewest active connections
round_robin Sequential rotation
weighted_round_robin Weighted sequential rotation
first Always use first available upstream
ip_hash Consistent hashing by client IP
uri_hash Consistent hashing by request URI
query_hash Consistent hashing by query parameter
header_hash Consistent hashing by header value
cookie Cookie-based affinity

Health checks: Active (periodic HTTP requests) and passive (monitor responses for failures). Configurable health path, headers, request body, interval, timeout, status codes, and expected body.

Retries: Configurable retry count, retry match conditions (response status codes, network errors), and retry-after backoff.

Dynamic upstreams: Support for DNS-based and SRV-based upstream discovery with configurable refresh intervals.

Admin API integration: The /reverse_proxy/upstreams admin endpoint exposes upstream health status for external monitoring.

Event System

The events app (modules/caddyevents/) provides a publish-subscribe system:

  • Modules emit events via caddy.EmitEvent()
  • Subscribers register handlers for specific event names
  • Events follow the CloudEvents specification (RFC, JSON-serializable)
  • Event handlers can abort event propagation
  • Subscriptions close after the events app starts (no late registration)

Testing Strategy

Caddy uses several testing approaches:

  1. Unit tests: Standard Go _test.go files alongside source (e.g., matchers_test.go, connpolicy_test.go)
  2. Integration tests: caddytest/ package provides a Tester helper for starting Caddy with config and making HTTP assertions
  3. Caddyfile adapt tests: caddytest/integration/caddyfile_adapt/*.caddyfiletest files test Caddyfile → JSON conversion (over 170 test cases)
  4. Fuzz tests: *_fuzz.go files for the formatter, lexer, replacer, addresses, and duration parser
  5. Linting: golangci-lint with extensive configuration (.golangci.yml)
  6. Pre-commit hooks: gitleaks, golangci-lint, shellcheck, trailing whitespace

Run the test suite:

go test ./... -tags=nobadger,nomysql,nopgx

Code Patterns and Conventions

Pattern Usage
Module registration init() + caddy.RegisterModule()
Config unmarshaling JSON struct tags + caddy: directive for module loading
Caddyfile integration UnmarshalCaddyfile(d *caddyfile.Dispenser) interface
Error handling fmt.Errorf with %v wrapping; %w for sentinel errors
Logging zap.Logger via ctx.Logger()
Placeholders caddy.Replacer with {prefix.key} syntax
Concurrency sync.Mutex for state protection; UsagePool for shared resources
Atomic operations Config swap via caddy.Load() + context cancellation
Feature flags JSON omitempty for optional fields; *bool for tri-state (unset/true/false)
Build constraints //go:build tags for platform-specific code (listen_unix.go, service_windows.go, sigtrap_posix.go)

Potential Improvements

These are sourced from the codebase itself (TODO comments, community issues, and architectural observations):

  1. caddy status command (community issue #7453): Currently no CLI command to check if Caddy is running or view its status without directly querying the admin API.

  2. WebSocket connection preservation (community issue #7222): Active WebSocket connections are disrupted during config reloads, even for unrelated routes.

  3. Unix socket as default admin endpoint (community issue #7516): Proposal to switch admin API to AF_UNIX by default for improved security.

  4. Stricter Host validation (community issue #7628): Design proposal for lower-level HTTP error handling for invalid Host headers.

  5. WebSocket compression (community issue #7455): RFC 7692 (permessage-deflate) is not currently supported.

  6. DNS-PERSIST challenge type (community issue #7495): Proposed new ACME challenge type.

  7. OTEL_EXPORTER_OTLP_PROTOCOL support (community issue #5743): OpenTelemetry protocol negotiation via environment variable.

Contributing

Caddy is licensed under the Apache License 2.0. The source repository includes:

  • Pre-commit hooks: gitleaks, golangci-lint, shellcheck
  • CI linting: Extensive golangci-lint configuration (50+ linters enabled)
  • Code formatting: gci, gofmt, gofumpt, goimports
  • Build system: GoReleaser for cross-platform binary releases with cosign signing and SBOM generation
  • Supported platforms: Linux (amd64, arm64, arm, s390x, ppc64le, riscv64), macOS (amd64, arm64), Windows (amd64, arm64), FreeBSD (amd64, arm64, armv6/v7)
  • Package formats: tar.gz, zip (Windows), deb

To build from source:

git clone https://github.com/caddyserver/caddy.git
cd caddy
go build -tags=nobadger,nomysql,nopgx -o caddy ./cmd/caddy

9. Integration Points

Reverse Proxy Admin API

# View upstream health status
curl localhost:2019/reverse_proxy/upstreams
[
  {
    "address": "node1:8080",
    "num_requests": 1234,
    "fails": 0
  }
]

PKI Admin API

# Get local CA info
curl localhost:2019/pki/ca/local

# Get CA certificates
curl localhost:2019/pki/ca/local/certificates

ACME Server

Caddy can act as an ACME server for issuing certificates to other clients:

{
    pki {
        ca local {
            name "My Organization CA"
        }
    }
}

acme.internal {
    acme_server {
        ca local
        lifetime 24h
    }
}

The ACME server supports HTTP-01 and TLS-ALPN-01 challenges, with configurable challenge types, certificate lifetimes, and issuer policy (allow/deny lists).

Tailscale Integration

Caddy integrates with Tailscale for certificate management via the tscert package. When running on a Tailscale network, Caddy can obtain certificates for Tailscale machine names automatically.

libdns Interface

Caddy's DNS challenge support uses the libdns interface, which provides a standardized API for DNS record management. Third-party DNS provider plugins implement this interface (e.g., Cloudflare, Route53, DigitalOcean).

OpenTelemetry

Caddy exports traces and metrics via OpenTelemetry with:

  • Auto-export via go.opentelemetry.io/contrib/exporters/autoexport (OTLP gRPC/HTTP, stdout)
  • Auto-propagation via go.opentelemetry.io/contrib/propagators/autoprop (W3C, B3, Jaeger, OT, AWS X-Ray)
  • HTTP instrumentation via go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
  • Prometheus bridge via go.opentelemetry.io/contrib/bridges/prometheus

Systemd Notification

On Linux with systemd, Caddy sends sd_notify readiness notifications via the notify/notify_linux.go module, allowing systemd to track Caddy's readiness state.