Open-Source Wikis

/

Caddy

/

Features

/

Automatic HTTPS

caddyserver/caddy

Automatic HTTPS

Caddy turns any HTTP route with a host matcher into a TLS-enabled site by default. The whole pipeline is implemented in modules/caddyhttp/autohttps.go (~33 KB) and feeds into the TLS app.

Purpose

Take "I have a route for example.com" as the only signal needed to:

  1. Bind the configured HTTPS port (default 443).
  2. Obtain a managed certificate for example.com from an issuer (Let's Encrypt, ZeroSSL, internal).
  3. Redirect plaintext HTTP requests to HTTPS.
  4. Serve TLS handshakes against the managed cert.

Directory layout

Path Role
modules/caddyhttp/autohttps.go The whole flow: harvest hostnames, split into managed/manual/skip, build TLS automation policies, install HTTP→HTTPS redirects
modules/caddyhttp/autohttps_test.go Auto-HTTPS tests
modules/caddytls/automation.go The TLS-side data model the http app fills in
modules/caddytls/ondemand.go On-demand issuance gating
modules/caddyhttp/httpredirectlistener.go Listener that redirects plaintext HTTP traffic on a TLS port

How it works

graph TD
    Provision[App.Provision] --> Phase1[automaticHTTPSPhase1]
    Phase1 --> Harvest[Walk every server's routes,<br/>collect hostnames from host matchers]
    Harvest --> Buckets["Sort into managed / manual /<br/>skip / on-demand"]
    Buckets --> TLSPolicies[Build AutomationPolicies<br/>and push to tls app]
    Provision --> Phase2[automaticHTTPSPhase2]
    Phase2 --> Bindings[Inject :443 listeners<br/>where missing]
    Phase2 --> Redirect[Add HTTP→HTTPS redirect server<br/>on the http port]
    Start[App.Start] --> StartTLS[tls.Start obtains certs<br/>via CertMagic]
    StartTLS --> Issuers[acme / zerossl / internal]

Harvest

automaticHTTPSPhase1 walks each Server's Routes and asks every route for its host matchers. Hostnames go into one of these buckets:

  • Managed: Caddy will obtain a cert. This is the default for any non-IP hostname.
  • Manual: the user already loaded a cert covering this hostname (via tls.certificates.load_*). Caddy serves it but doesn't manage it.
  • Skip: explicitly disabled via auto_https off for this site.
  • On-demand: issuance is deferred to the first TLS handshake (gated by ondemand.go to prevent abuse).

IP-only and explicitly excluded hostnames don't get certs.

Build TLS automation policies

Phase 1 then calls tls.AddAutomationPolicy for the managed buckets. Each policy:

  • Lists the hostnames it covers.
  • Names the issuer chain (default: acme then zerossl as fallback; internal for internal-only names).
  • Carries renewal and key-type knobs.

Listener and redirect injection

automaticHTTPSPhase2 ensures every server that needs TLS has a :443 (or configured HTTPS port) entry in listen, and that the connection-policy list has a default if none was set. It also installs a small HTTP server bound to the HTTP port (default :80) that 301-redirects everything to HTTPS for managed hostnames. That server is generated dynamically — you don't see it in your config.

TLS handshake

When a client connects, caddytls's connection policy machinery (modules/caddytls/connpolicy.go) selects the right cert via CertMagic. If issuance is on-demand, the cert is fetched live during the handshake (with the configured ACME issuer and the on-demand permission check from ondemand.go).

What you see in JSON

A Caddyfile site like:

example.com {
    reverse_proxy localhost:8080
}

becomes (abbreviated):

{
  "apps": {
    "http": { "servers": { "srv0": { "listen": [":443"], "routes": [...] } } },
    "tls": {
      "automation": {
        "policies": [
          { "subjects": ["example.com"] }
        ]
      }
    }
  }
}

You can see the same with caddy adapt --config Caddyfile --pretty.

Disabling auto-HTTPS

{
    auto_https off
}

Or per-site:

example.com {
    auto_https off
    ...
}

The first form skips both cert management and the HTTP→HTTPS redirect; the second form skips for that site only.

On-demand issuance

For workloads where you don't know the hostnames in advance (multi-tenant SaaS, dynamic preview environments), enable on-demand:

{
    on_demand_tls {
        ask https://my-app/check-hostname
    }
}

* {
    tls {
        on_demand
    }
}

The ask endpoint is consulted at handshake time to authorize issuance. modules/caddytls/ondemand.go implements the rate limiting and permission flow.

Integration points

  • http app: drives the entire flow at provision time.
  • tls app: is the consumer; obtains and maintains certs.
  • pki app: the internal issuer issues certs for internal-only names like localhost.
  • Storage: all cert material persists through the configured storage module.

Entry points for modification

  • Tweak harvesting heuristics? automaticHTTPSPhase1 in autohttps.go.
  • Change the redirect server? Same file, automaticHTTPSPhase2.
  • Customize the on-demand permission flow? modules/caddytls/ondemand.go.

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

Automatic HTTPS – Caddy wiki | Factory