Skip to main content

HTTPS Configuration

Caddy is the first web server to enable HTTPS by default. It automatically obtains and renews certificates from Let’s Encrypt and other ACME CAs.

Automatic HTTPS

HTTPS is enabled automatically for all sites with hostnames:
example.com {
    respond "Hello, HTTPS!"
}
Caddy automatically:
  • Obtains certificates from Let’s Encrypt
  • Renews certificates before expiration
  • Staples OCSP responses
  • Redirects HTTP to HTTPS

Certificate Automation

Automation Policies

Configure how certificates are obtained and managed:
{
  "apps": {
    "tls": {
      "automation": {
        "policies": [
          {
            "subjects": ["example.com", "*.example.com"],
            "issuers": [
              {
                "module": "acme",
                "email": "[email protected]",
                "ca": "https://acme-v02.api.letsencrypt.org/directory"
              }
            ],
            "renewal_window_ratio": 0.33,
            "key_type": "ec256",
            "must_staple": false
          }
        ],
        "ocsp_interval": "1h",
        "renew_interval": "10m"
      }
    }
  }
}

Key Types

Supported key types:
  • ec256 - ECDSA P-256 (default, recommended)
  • ec384 - ECDSA P-384
  • rsa2048 - RSA 2048-bit
  • rsa4096 - RSA 4096-bit
  • ed25519 - Ed25519
ECDSA keys (ec256) provide better performance and smaller certificate sizes compared to RSA.

Certificate Issuers

Let’s Encrypt

Default production CA:
tls {
    issuer acme {
        ca https://acme-v02.api.letsencrypt.org/directory
        email [email protected]
    }
}

ZeroSSL

Alternative CA with longer validity:
tls {
    issuer zerossl {
        email [email protected]
    }
}

Internal Certificates

For development or internal services:
localhost {
    tls internal
}

# Or for specific domains
intranet.local {
    tls internal {
        ca internal_ca
    }
}

On-Demand TLS

Obtain certificates during TLS handshakes:
{
  "apps": {
    "tls": {
      "automation": {
        "on_demand": {
          "ask": "https://api.example.com/check-domain",
          "rate_limit": {
            "interval": "1m",
            "burst": 5
          }
        },
        "policies": [
          {
            "on_demand": true
          }
        ]
      }
    }
  }
}
On-Demand TLS is powerful but must be protected with rate limits or an ask endpoint to prevent abuse. Without protections, attackers could trigger unlimited certificate requests.

Permission Module

Validate domains before issuing certificates:
{
  "on_demand": {
    "permission": {
      "module": "http",
      "endpoint": "https://api.example.com/check-domain?domain={domain}"
    }
  }
}

Manual Certificates

Load from Files

{
  "apps": {
    "tls": {
      "certificates": {
        "load_files": [
          {
            "certificate": "/path/to/cert.pem",
            "key": "/path/to/key.pem"
          }
        ]
      }
    }
  }
}

Load from Folder

Automatically load all certificates from a directory:
{
  "apps": {
    "tls": {
      "certificates": {
        "load_folders": ["/path/to/certs"]
      }
    }
  }
}

DNS Challenge

Use DNS-01 challenge for wildcard certificates or when HTTP-01 is unavailable:
{
  "issuer": {
    "module": "acme",
    "challenges": {
      "dns": {
        "provider": {
          "name": "cloudflare",
          "api_token": "{env.CLOUDFLARE_API_TOKEN}"
        }
      }
    }
  }
}

TLS Settings

Connection Policy

Configure TLS settings per hostname:
{
  "apps": {
    "tls": {
      "connection_policies": [
        {
          "match": {
            "sni": ["example.com"]
          },
          "protocol_min": "tls1.2",
          "protocol_max": "tls1.3",
          "cipher_suites": [
            "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
          ],
          "curves": ["x25519", "secp256r1"],
          "alpn": ["h2", "http/1.1"]
        }
      ]
    }
  }
}

Client Authentication

Require client certificates (mutual TLS):
example.com {
    tls {
        client_auth {
            mode require_and_verify
            trusted_ca_cert_file /path/to/ca.pem
        }
    }
}

Certificate Storage

File System (Default)

Certificates are stored in:
  • Linux: $XDG_DATA_HOME/caddy or $HOME/.local/share/caddy
  • macOS: $HOME/Library/Application Support/Caddy
  • Windows: %APPDATA%\Caddy

Custom Storage

Use different storage backends:
{
  "storage": {
    "module": "file_system",
    "root": "/custom/path/to/storage"
  }
}

Session Tickets

Configure session ticket rotation:
{
  "apps": {
    "tls": {
      "session_tickets": {
        "key_source": {
          "module": "standard"
        },
        "rotation_interval": "24h",
        "max_keys": 4,
        "disabled": false
      }
    }
  }
}

OCSP Stapling

Caddy automatically staples OCSP responses. Configure check intervals:
{
  "automation": {
    "ocsp_interval": "1h",
    "policies": [
      {
        "disable_ocsp_stapling": false
      }
    ]
  }
}

Complete Example

# Main site with automatic HTTPS
example.com {
    tls [email protected] {
        # Use Let's Encrypt
        issuer acme {
            ca https://acme-v02.api.letsencrypt.org/directory
        }
        
        # Use ECDSA key
        key_type ec256
        
        # TLS settings
        protocols tls1.2 tls1.3
        
        # Require client certificates
        client_auth {
            mode request
            trusted_ca_cert_file /path/to/client-ca.pem
        }
    }
    
    respond "Secure site"
}

# Wildcard with DNS challenge
*.api.example.com {
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }
    
    reverse_proxy backend:8080
}

# Internal development site
localhost {
    tls internal
    
    file_server
}

Best Practices

1
Security Recommendations
2
  • Use ECDSA keys - Better performance than RSA
  • Enable OCSP stapling - Enabled by default
  • Keep TLS 1.2+ only - Disable older protocols
  • Monitor certificate expiration - Caddy renews automatically at 1/3 lifetime remaining
  • Protect on-demand TLS - Always use rate limits or validation
  • Caddy checks certificates for renewal every 10 minutes by default. Certificates are renewed when they have about 1/3 of their lifetime remaining.

    Build docs developers (and LLMs) love