← All articles
INFRASTRUCTURE Caddy: The Easiest Self-Hosted Web Server with Autom... 2026-02-08 · caddy · reverse-proxy · https

Caddy: The Easiest Self-Hosted Web Server with Automatic HTTPS

Infrastructure 2026-02-08 caddy reverse-proxy https web-server

Caddy is a modern web server that does something no other server does out of the box: it automatically provisions and renews HTTPS certificates for every site you configure. No certbot cron jobs, no manual Let's Encrypt setup, no expired certificate surprises at 3 AM.

For self-hosters running multiple services behind a reverse proxy, Caddy can be a dramatically simpler alternative to Nginx or Apache.

Why Caddy?

The self-hosting reverse proxy landscape has several strong options. Here's where Caddy fits:

The trade-off compared to Nginx: Caddy uses slightly more memory and has fewer third-party modules. Compared to Traefik: Caddy doesn't auto-discover Docker containers (though plugins exist for this).

Installation with Docker

# docker-compose.yml
services:
  caddy:
    image: caddy:2
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"  # HTTP/3
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    restart: unless-stopped

volumes:
  caddy_data:    # Stores certificates
  caddy_config:  # Stores configuration state

The caddy_data volume is critical — it stores your TLS certificates. Don't delete it or you'll re-request certs (and potentially hit rate limits).

Basic Caddyfile Configuration

Static File Server

yourdomain.com {
    root * /srv/www
    file_server
}

That's it. Caddy will:

  1. Obtain a TLS certificate for yourdomain.com
  2. Redirect HTTP to HTTPS
  3. Serve files from /srv/www
  4. Renew the certificate automatically before it expires

Reverse Proxy

The most common self-hosting use case — proxying to internal services:

grafana.yourdomain.com {
    reverse_proxy grafana:3000
}

nextcloud.yourdomain.com {
    reverse_proxy nextcloud:80
}

jellyfin.yourdomain.com {
    reverse_proxy jellyfin:8096
}

Each subdomain automatically gets its own HTTPS certificate. No certificate configuration needed.

Reverse Proxy with Path-Based Routing

If you prefer paths over subdomains:

yourdomain.com {
    handle /grafana/* {
        uri strip_prefix /grafana
        reverse_proxy grafana:3000
    }

    handle /jellyfin/* {
        reverse_proxy jellyfin:8096
    }

    handle {
        root * /srv/www
        file_server
    }
}

Advanced Configuration

Headers and Security

yourdomain.com {
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        Referrer-Policy "strict-origin-when-cross-origin"
        -Server  # Remove server header
    }
    reverse_proxy app:8080
}

Basic Authentication

admin.yourdomain.com {
    basicauth {
        admin $2a$14$... # bcrypt hash
    }
    reverse_proxy admin-panel:8080
}

Generate the hash with:

docker exec -it caddy caddy hash-password

Rate Limiting

api.yourdomain.com {
    rate_limit {
        zone api {
            key {remote_host}
            events 100
            window 1m
        }
    }
    reverse_proxy api:3000
}

Wildcard Certificates

For many subdomains, a wildcard cert avoids individual certificate requests:

*.yourdomain.com {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }

    @grafana host grafana.yourdomain.com
    handle @grafana {
        reverse_proxy grafana:3000
    }

    @nextcloud host nextcloud.yourdomain.com
    handle @nextcloud {
        reverse_proxy nextcloud:80
    }
}

Wildcard certs require DNS challenge validation. Caddy supports DNS providers through plugins — the Cloudflare plugin is the most popular.

Using Caddy with Cloudflare DNS Plugin

Build a custom Caddy image with the Cloudflare DNS module:

FROM caddy:2-builder AS builder
RUN xcaddy build --with github.com/caddy-dns/cloudflare

FROM caddy:2
COPY --from=builder /usr/bin/caddy /usr/bin/caddy

Then in your Caddyfile:

{
    acme_dns cloudflare {env.CF_API_TOKEN}
}

Caddy vs Nginx vs Traefik

Feature Caddy Nginx Traefik
Auto HTTPS Built-in Manual (certbot) Built-in
Config format Caddyfile (simple) nginx.conf (verbose) YAML/TOML/labels
Docker integration Manual/plugin Manual Native (labels)
Performance Good Excellent Good
Memory usage ~30 MB ~10 MB ~50 MB
HTTP/3 Built-in Experimental Built-in
Community size Growing Massive Large
Middleware Plugins Modules Extensive
Dashboard No (API only) No (paid Plus) Yes (built-in)
Learning curve Low Medium Medium-High

When to Choose Caddy

When to Choose Nginx

When to Choose Traefik

Common Patterns for Self-Hosters

All-in-One with Docker Compose

services:
  caddy:
    image: caddy:2
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    restart: unless-stopped
    networks:
      - proxy

  whoami:
    image: traefik/whoami
    networks:
      - proxy

networks:
  proxy:
    name: proxy

volumes:
  caddy_data:
  caddy_config:

Put all your services on the proxy network and reference them by container name in the Caddyfile.

Local HTTPS for Development

Caddy can issue locally-trusted certificates for development:

localhost {
    reverse_proxy app:3000
}

Caddy installs a local CA and issues certificates that your browser trusts. No more "your connection is not private" warnings during development.

Migrating from Nginx

The translation is usually straightforward:

Nginx:

server {
    listen 80;
    server_name app.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name app.example.com;
    ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Caddy:

app.example.com {
    reverse_proxy localhost:8080
}

Caddy handles the HTTP→HTTPS redirect, certificate provisioning, and proxy headers automatically.

Verdict

Caddy is the best choice for self-hosters who want a reverse proxy that just works. The automatic HTTPS alone saves hours of certificate management headaches. The Caddyfile format is the most readable of any web server configuration.

If you're starting fresh with a self-hosted setup and don't need Traefik's Docker auto-discovery or Nginx's raw performance, Caddy is the easiest path to a secure, well-configured reverse proxy. The five minutes you spend writing a Caddyfile will save you hours of debugging certificate renewals and nginx configuration.