caddyserver/caddy
TLS
The tls app handles certificate loading, ACME issuance, the in-memory certificate cache, and TLS connection policies. Module ID tls, defined in modules/caddytls/tls.go.
Purpose
Provide and manage TLS material for the rest of Caddy. The http app (and any other app that wants TLS) registers hostnames with the TLS app, and the TLS app:
- Loads certificates from disk, PEM blobs, storage, or remote managers.
- Drives CertMagic to obtain certs from ACME issuers (Let's Encrypt, ZeroSSL, internal).
- Caches certificates in memory and renews them before expiry.
- Provides connection policies that pick the right cert for each handshake.
Directory layout
| Path | Role |
|---|---|
modules/caddytls/tls.go |
The TLS app type and its lifecycle (~35 KB) |
modules/caddytls/automation.go |
AutomationConfig, AutomationPolicy — what to do per host (~24 KB) |
modules/caddytls/connpolicy.go |
ConnectionPolicy (cert selection + TLS settings, ~39 KB) |
modules/caddytls/acmeissuer.go |
acme issuer module (~24 KB) |
modules/caddytls/zerosslissuer.go |
zerossl issuer (uses ZeroSSL's EAB API) |
modules/caddytls/internalissuer.go |
internal issuer (uses the local PKI app) |
modules/caddytls/fileloader.go |
tls.certificates.load_files — load PEM pairs from paths |
modules/caddytls/folderloader.go |
tls.certificates.load_folders |
modules/caddytls/pemloader.go |
tls.certificates.load_pem — inline PEM data |
modules/caddytls/storageloader.go |
tls.certificates.load_storage |
modules/caddytls/leaffileloader.go, leaffolderloader.go, leafpemloader.go, leafstorageloader.go |
Trust-anchor loaders for client cert validation |
modules/caddytls/capools.go |
Reusable CA pool definitions for client cert verification (~28 KB) |
modules/caddytls/certselection.go |
Custom certificate-selection policy hooks |
modules/caddytls/certmanagers.go |
External cert manager modules (e.g. tailscale) |
modules/caddytls/matchers.go |
TLS-handshake matchers (SNI, ALPN) |
modules/caddytls/sessiontickets.go |
Session ticket key management |
modules/caddytls/distributedstek/, standardstek/ |
STEK (Session Ticket Encryption Key) providers |
modules/caddytls/ondemand.go |
On-demand certificate issuance gating |
modules/caddytls/ech.go |
Encrypted ClientHello support (~43 KB) |
modules/caddytls/values.go |
Cipher / curve / protocol enumeration |
Key abstractions
| Type | Where | Description |
|---|---|---|
TLS |
tls.go |
The app; holds the global cert cache, issuers, and automation config |
AutomationConfig / AutomationPolicy |
automation.go |
Per-hostname rules: which issuers to use, when to renew, on-demand gating |
ConnectionPolicy |
connpolicy.go |
Per-server TLS settings: alpn, curves, ciphers, client auth, cert selection |
Issuer (CertMagic) |
(external) | Implemented by acme, zerossl, internal modules |
CertificateLoader |
various *loader.go files |
Loads pre-existing cert/key pairs |
Certificate |
(CertMagic) | The in-memory cert object stored in certCache |
How it works
graph TD
Config[Config.AppsRaw.tls] --> Provision
Provision --> Loaders[Run cert loaders<br/>file/folder/pem/storage]
Provision --> Automation[Provision AutomationPolicies]
Automation --> Issuers[Provision issuers<br/>acme / zerossl / internal]
Provision --> Cache[certmagic.Cache<br/>shared singleton]
Loaders --> Cache
Issuers --> Cache
HTTPApp[http app] -->|register hostnames| Automation
Handshake[client TLS handshake] --> ConnPol[ConnectionPolicy]
ConnPol -->|GetCertificate| Cache
Cache -->|maintenance loop| IssuersOne global certificate cache
tls.go holds a package-level singleton certCache *certmagic.Cache. All apps share it — multiple HTTP servers across multiple configs all reach into the same in-memory cache. The cache is created lazily on the first TLS app provisioning and torn down when no app needs it.
A test hook testCertMagicStorageOverride in caddy.go lets tests inject a temporary in-memory storage so they don't pollute disk.
Automation
AutomationConfig.Policies is a list of AutomationPolicys, each with:
- A subject filter (specific hostnames or wildcards).
- Issuers in priority order.
- Renewal/cert-management knobs (storage override, key type, must-staple, etc.).
- On-demand gating (
ondemand.go).
The HTTP app's auto-HTTPS phase (modules/caddyhttp/autohttps.go) builds these policies for you. You can also write them by hand for non-HTTP apps.
Issuers
Three built-ins, all registered under tls.issuance.*:
acme(acmeissuer.go) — generic ACME (Let's Encrypt by default). Supports HTTP-01, TLS-ALPN-01, and DNS-01 challenges. DNS providers are loaded from thedns.providers.*namespace (a separate plugin tree).zerossl(zerosslissuer.go) — ZeroSSL with their External Account Binding (EAB) API.internal(internalissuer.go) — uses the localpkiapp to mint certificates for internal-only names.
Connection policies
ConnectionPolicy (connpolicy.go) is per-server:
match— predicates on the ClientHello (SNI, ALPN, etc.) for choosing which policy applies.certificate_selection— picks among multiple matching certs.cipher_suites,curves,protocol_min,protocol_max,alpn.client_authentication— requires client certs, validates against a trust pool.
The HTTP app reads these from each Server.TLSConnPolicies and hands them to CertMagic via tls.GetCertificate.
Encrypted ClientHello (ECH)
ech.go (~43 KB) implements ECH: Caddy can publish ECH config keys via DNS records (managed via the same DNS provider plumbing), accept the inner ClientHello, decrypt it, and route based on the true SNI. ECH is one of the larger and newer pieces of the TLS app.
Integration points
- HTTP app: registers hostnames via
automation.go's policy machinery and consumesConnectionPolicyper server. - PKI app: the
internalissuer asks the local CA for certificates. - Storage: all CertMagic state (certs, OCSP, locks) goes through
Caddy's configured storage module (storage.go, defaultcaddy.storage.file_system). - Events: TLS emits events for cert renewal, OCSP refresh, and lifecycle hooks.
Entry points for modification
- A new issuer? Add a module under
modules/caddytls/registeringtls.issuance.<name>and implementing the CertMagicIssuerinterface. - A new cert loader? Implement
caddytls.CertificateLoaderand registertls.certificates.<name>. - TLS-handshake matching? Add a matcher under
tls.handshake_match.*(matchers.go). - Per-policy customization? Extend
ConnectionPolicyinconnpolicy.go(with care — this is security-critical perAGENTS.md).
Related pages
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.