caddyserver/caddy

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

71,685 stars Go 8 components

12 hidden assumptions · 6-stage pipeline · 8 components

Like any codebase, this backend api makes assumptions it never checks — most are routine. The ones worth your attention are below.

Serves HTTP/HTTPS sites with automatic TLS certificates and zero-downtime config reloads

Configuration enters through CLI commands or files, gets parsed and converted to JSON by adapters, validated against module schemas, then applied to the running instance. HTTP requests flow through middleware chains defined by the configuration, while the admin API allows runtime configuration changes without restart. Certificate management happens automatically in background, with certificates stored and renewed as needed.

Under the hood, the system uses 3 feedback loops, 4 data pools, 5 control points to manage its runtime behavior.

A 8-component backend api. 309 files analyzed. Data flows through 6 distinct pipeline stages.

Hidden Assumptions

Most of what this code assumes is routine. These 3 are the ones most likely to cause trouble here. The rest are minor; they're under "Show everything".

Worth your attention first

If a config references an unregistered module (e.g., 'http.handlers.custom_auth'), Load() fails with a cryptic error instead of suggesting available modules or indicating a missing import

Worth your attention first

A bad config could leave the server in an inconsistent state with half-old, half-new configuration if reload fails partway through module provisioning

Worth your attention first

Carefully crafted Caddyfile imports could exhaust memory or stack space before hitting the cycle detection, causing OOM crashes instead of clean error messages

Show everything (9 more)
Domain

Sets certmagic.DefaultACME.Agreed = true globally, assuming all users consent to Let's Encrypt terms without any explicit agreement mechanism or documentation requirement

If this fails: Users unknowingly agree to CA terms of service they may not have read, potentially creating legal liability, especially in corporate environments with compliance requirements

cmd/main.go:init
Environment

The admin API bind address from AdminConfig.Listen is always available and not already in use by another process, with no retry or fallback mechanism

If this fails: If another process is using the admin port (default :2019), Caddy fails to start entirely instead of degrading gracefully or using an alternative port

caddy.go:Instance.Start
Temporal

Admin API requests are processed sequentially and don't interfere with each other, but concurrent config changes aren't synchronized beyond basic HTTP handler isolation

If this fails: Two simultaneous admin API calls modifying different parts of the config could race, with the second overwriting changes from the first without any conflict detection

admin.go:AdminServer
Scale

Token arrays from parsing fit entirely in memory as []Token slices, with no streaming or chunked processing for very large Caddyfiles

If this fails: Parsing a multi-megabyte Caddyfile with thousands of server blocks could exhaust available memory, causing OOM kills in memory-constrained environments

caddyconfig/caddyfile/dispenser.go:Dispenser.tokens
Ordering

The ServerType interface implementation exists and is properly initialized when Adapt() is called, but there's no null check on a.ServerType

If this fails: Calling Adapt() with an uninitialized Adapter causes a nil pointer panic instead of the documented 'no server type' error

caddyconfig/caddyfile/adapter.go:Adapter.Adapt
Environment

OS provides at least one command line argument in os.Args, specifically that len(os.Args) >= 1 with args[0] containing the executable name

If this fails: In exotic execution environments or containers where os.Args is empty, Caddy prints a fatal error but the array access could panic before the error message

cmd/main.go:Main
Domain

Environment variable substitution in {$VAR} format expects valid environment variable names following shell conventions, but doesn't validate variable name syntax

If this fails: Malformed environment variable references like {$123-invalid} or {$} could cause silent substitution failures or unexpected string replacements

caddyconfig/caddyfile/parse.go:Parse
Contract

Modules implementing Provisioner, Validator, or CleanerUpper interfaces handle errors gracefully and don't panic, but there's no error isolation between modules during lifecycle operations

If this fails: A buggy module that panics during Provision() or Cleanup() could crash the entire server instead of being isolated and reported as a module-specific failure

caddy.go:Module interfaces
Resource

Admin API request bodies have reasonable size limits and don't require streaming for large configuration uploads, but there's no explicit size limiting

If this fails: An attacker could upload extremely large JSON configs to the admin API, consuming excessive memory and potentially causing DoS through resource exhaustion

admin.go:AdminServer.ServeHTTP

Open the standalone hidden-assumptions report for caddy →

How Data Flows Through the System

Configuration enters through CLI commands or files, gets parsed and converted to JSON by adapters, validated against module schemas, then applied to the running instance. HTTP requests flow through middleware chains defined by the configuration, while the admin API allows runtime configuration changes without restart. Certificate management happens automatically in background, with certificates stored and renewed as needed.

  1. Parse configuration input — CLI commands read Caddyfile or JSON from disk, Caddyfile text gets tokenized by lexer into Token streams, then parsed into ServerBlock tree structures with import resolution
  2. Convert to unified JSON — Adapter.Adapt() transforms ServerBlock structures into standardized JSON Config format, resolving imports, substituting variables, and organizing by application type [ServerBlock → Config]
  3. Load configuration into instance — Instance.Load() validates JSON config against registered module schemas, provisions modules with their configurations, and builds dependency graphs [Config → Module]
  4. Start services and admin API — Instance.Start() initializes provisioned modules (HTTP servers, TLS managers), starts AdminServer on configured port, begins certificate management tasks [Module] (config: AdminConfig.Listen, AdminConfig.Disabled)
  5. Handle HTTP requests — Incoming requests flow through HTTP middleware chains defined in configuration, with Context providing module access, logging, and placeholder replacement
  6. Process configuration changes — Admin API receives new configurations via POST /config endpoints, validates changes, performs graceful reload by starting new modules alongside old ones, then switching atomically [Config]

Data Models

The data structures that flow between stages — the contracts that hold the system together.

Config caddy.go
struct with Apps map[string]json.RawMessage (module configurations), Admin *AdminConfig (API settings), Logging *LoggingConfig, Storage json.RawMessage (storage backend config)
Created from JSON/Caddyfile input, validated against module schemas, applied to running instance with graceful reload
ServerBlock caddyconfig/caddyfile/parse.go
struct with Keys []string (hostnames/addresses), Segments []Segment (directive blocks), IsSnippet bool
Parsed from Caddyfile text, processed through import resolution and variable substitution, converted to JSON Config
Module modules.go
interface with CaddyModule() ModuleInfo method, plus optional Provisioner, Validator, CleanerUpper interfaces for lifecycle hooks
Registered at init time, instantiated during config loading, provisioned with dependencies, validated, then used in request handling
Token caddyconfig/caddyfile/lexer.go
struct with Text string (token content), File string, Line int (source location), Quoted bool, IsSilent bool
Generated by lexer from Caddyfile text, consumed by parser to build ServerBlock tree structure
AdminConfig admin.go
struct with Disabled bool, Listen string (API address), EnforceOrigin bool, Origins []string (CORS), Config *ConfigSettings
Configured from main Config, used to start HTTP admin API server for runtime management

System Behavior

How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.

Data Pools

Module Registry (registry)
Global map storing all registered module constructors, organized by namespace (http.handlers, tls.providers, etc)
Instance State (in-memory)
Current running configuration and provisioned module instances, maintained for graceful reloads and cleanup
Certificate Storage (file-store)
Persistent storage for TLS certificates, private keys, and ACME account data managed by CertMagic
Import Graph (in-memory)
Dependency tracking for Caddyfile imports to detect cycles and ensure proper resolution order

Feedback Loops

Delays

Control Points

Technology Stack

CertMagic (library)
Handles automatic TLS certificate provisioning, renewal, and storage with ACME provider integration
Zap (library)
Structured logging throughout the system with configurable levels and outputs
Cobra (framework)
CLI framework providing the command structure (start, stop, reload, validate, etc)
QUIC-GO (library)
HTTP/3 and QUIC protocol implementation for next-generation web serving
Prometheus Client (library)
Metrics collection and exposure for monitoring server performance and health
OpenTelemetry (library)
Distributed tracing and observability instrumentation for request tracking
Go Chi Router (framework)
HTTP routing for the admin API endpoints with middleware support
CEL (Common Expression Language) (library)
Expression evaluation for configuration conditions and dynamic behavior

Key Components

Explore the interactive analysis

See the full architecture map, data flow, and code patterns visualization.

Analyze on CodeSea

Related Backend Api Repositories

Frequently Asked Questions

What is caddy used for?

Serves HTTP/HTTPS sites with automatic TLS certificates and zero-downtime config reloads caddyserver/caddy is a 8-component backend api written in Go. Data flows through 6 distinct pipeline stages. The codebase contains 309 files.

How is caddy architected?

caddy is organized into 6 architecture layers: Command Interface, Core Instance, Module System, Configuration Layer, and 2 more. Data flows through 6 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.

How does data flow through caddy?

Data moves through 6 stages: Parse configuration input → Convert to unified JSON → Load configuration into instance → Start services and admin API → Handle HTTP requests → .... Configuration enters through CLI commands or files, gets parsed and converted to JSON by adapters, validated against module schemas, then applied to the running instance. HTTP requests flow through middleware chains defined by the configuration, while the admin API allows runtime configuration changes without restart. Certificate management happens automatically in background, with certificates stored and renewed as needed. This pipeline design reflects a complex multi-stage processing system.

What technologies does caddy use?

The core stack includes CertMagic (Handles automatic TLS certificate provisioning, renewal, and storage with ACME provider integration), Zap (Structured logging throughout the system with configurable levels and outputs), Cobra (CLI framework providing the command structure (start, stop, reload, validate, etc)), QUIC-GO (HTTP/3 and QUIC protocol implementation for next-generation web serving), Prometheus Client (Metrics collection and exposure for monitoring server performance and health), OpenTelemetry (Distributed tracing and observability instrumentation for request tracking), and 2 more. A focused set of dependencies that keeps the build manageable.

What system dynamics does caddy have?

caddy exhibits 4 data pools (Module Registry, Instance State), 3 feedback loops, 5 control points, 3 delays. The feedback loops handle self-correction and polling. These runtime behaviors shape how the system responds to load, failures, and configuration changes.

What design patterns does caddy use?

5 design patterns detected: Module Plugin Architecture, Graceful Configuration Reload, Adapter Pattern, Context Propagation, Automatic Resource Management.

Analyzed on April 20, 2026 by CodeSea. Written by .