Caddy can automatically provision and renew TLS certificates for your domains, and it’s particularly slick when integrating with DNS providers like Cloudflare for the ACME challenge. Here’s how to set it up.

Let’s see Caddy in action. Imagine you have two subdomains, app.example.com and api.example.com, that you want to secure with a single wildcard certificate.

example.com {
    tls {
        dns cloudflare your_cloudflare_api_token
    }
    reverse_proxy app:8080
}

*.example.com {
    tls {
        dns cloudflare your_cloudflare_api_token
    }
    reverse_proxy api:8081
}

When Caddy starts with this configuration, it will attempt to get a certificate for example.com and a wildcard certificate for *.example.com. For each, it’ll use the Cloudflare DNS challenge. Caddy will automatically create a TXT record (e.g., _acme-challenge.app.example.com) with a specific value in your Cloudflare DNS zone. ACME servers will then query Cloudflare to verify this record. Once validated, Caddy receives the certificate.

The core problem Caddy’s DNS challenge integration solves is the inability to directly expose port 80 or 443 from every potential subdomain to the internet, which is a requirement for HTTP-01 challenges. Wildcard certificates, in particular, are difficult to manage manually across many subdomains. By using DNS validation, Caddy delegates the challenge to your DNS provider, which is accessible from anywhere.

Here’s how it works internally:

  1. ACME Protocol: Caddy acts as an ACME client, communicating with an ACME server (like Let’s Encrypt).
  2. Certificate Request: Caddy requests a certificate for example.com and *.example.com.
  3. Challenge Type: The ACME server offers challenge types. For DNS integration, it will provide a DNS-01 challenge.
  4. TXT Record Generation: Caddy generates a unique TXT record value and instructs its configured DNS provider (Cloudflare, in this case) to create a TXT record under a specific subdomain (e.g., _acme-challenge.app.example.com).
  5. DNS Propagation: The TXT record is published to DNS.
  6. Verification: The ACME server queries your DNS provider to verify the TXT record’s value.
  7. Certificate Issuance: If verification succeeds, the ACME server issues the certificate, which Caddy then downloads and configures for TLS.

The your_cloudflare_api_token is crucial. This needs to be a Cloudflare API token with the "Zone:DNS:Edit" permission for the specific zone you’re managing (e.g., example.com). You can create these tokens in your Cloudflare dashboard under "My Profile" -> "API Tokens" -> "Create Token". It’s best practice to create a token with the minimum required permissions and scope it to the specific zone.

If you have multiple domains and subdomains, you can consolidate them into a single Caddyfile block for efficiency:

example.com, *.example.com {
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }
    handle_path /app/* {
        reverse_proxy app:8080
    }
    handle_path /api/* {
        reverse_proxy api:8081
    }
}

In this example, we use an environment variable CLOUDFLARE_API_TOKEN for the API token, which is a more secure way to manage credentials. The handle_path directive allows Caddy to route different paths to different backend services while using the same certificate.

One common pitfall is assuming Caddy will automatically infer the correct Cloudflare zone if you have multiple zones associated with your API token. Caddy needs to know which zone to modify. If Caddy can’t find the correct zone, it will fail the DNS challenge. You can explicitly specify the zone ID:

example.com, *.example.com {
    tls {
        dns cloudflare {
            api_token {env.CLOUDFLARE_API_TOKEN}
            zone_id YOUR_CLOUDFLARE_ZONE_ID
        }
    }
    reverse_proxy app:8080
}

Replace YOUR_CLOUDFLARE_ZONE_ID with your actual Cloudflare Zone ID, which you can find on the overview page of your domain in the Cloudflare dashboard.

The most surprising thing is how Caddy handles the lifecycle of these certificates without any manual intervention. It doesn’t just get the cert; it automatically renews it before it expires, ensuring continuous availability. This automatic renewal process is triggered by Caddy’s internal scheduler and relies on the same DNS challenge mechanism. If your API token expires or loses permissions, renewal will fail, and you’ll eventually face an expired certificate.

The next step is to understand how to manage multiple DNS providers or how Caddy handles rate limits from ACME servers.

Want structured learning?

Take the full Caddy course →