Open-Source Wikis

/

Caddy

/

Apps

/

TLS

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| Issuers

One 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 the dns.providers.* namespace (a separate plugin tree).
  • zerossl (zerosslissuer.go) — ZeroSSL with their External Account Binding (EAB) API.
  • internal (internalissuer.go) — uses the local pki app 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 consumes ConnectionPolicy per server.
  • PKI app: the internal issuer asks the local CA for certificates.
  • Storage: all CertMagic state (certs, OCSP, locks) goes through Caddy's configured storage module (storage.go, default caddy.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/ registering tls.issuance.<name> and implementing the CertMagic Issuer interface.
  • A new cert loader? Implement caddytls.CertificateLoader and register tls.certificates.<name>.
  • TLS-handshake matching? Add a matcher under tls.handshake_match.* (matchers.go).
  • Per-policy customization? Extend ConnectionPolicy in connpolicy.go (with care — this is security-critical per AGENTS.md).

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

TLS – Caddy wiki | Factory