DevToolBoxGRATUIT
Blog

Guide Complet Caddy Server 2026 : HTTPS Automatique et Proxy Inverse

28 min de lecturepar DevToolBox Team

TL;DR

Caddy is a modern, Go-based web server that provides automatic HTTPS via Let's Encrypt with zero configuration. It serves as a drop-in replacement for Nginx and Apache with a dramatically simpler config syntax (Caddyfile). Caddy handles TLS certificate provisioning, renewal, and OCSP stapling automatically. It supports reverse proxying, load balancing, static file serving, HTTP/3, and dynamic configuration via its admin API. For most web serving use cases in 2026, Caddy offers the best developer experience with production-grade reliability.

Key Takeaways

  • Caddy automatically provisions and renews HTTPS certificates from Let's Encrypt — no certbot, no cron jobs, no manual renewal
  • The Caddyfile syntax is dramatically simpler than Nginx/Apache configs: a basic reverse proxy is just 3 lines
  • Caddy supports HTTP/3 (QUIC), on-demand TLS, wildcard certificates, and the ACME protocol out of the box
  • Caddy's admin API allows live configuration changes without restarts or reloads
  • Caddy excels as a Docker reverse proxy with automatic HTTPS for containerized microservices
  • Migration from Nginx to Caddy typically reduces configuration complexity by 60-80%

Caddy is an open-source web server written in Go that has gained significant traction as a modern alternative to Nginx and Apache. Its standout feature is automatic HTTPS: Caddy obtains and renews TLS certificates from Let's Encrypt (or ZeroSSL) without any configuration. In 2026, Caddy powers millions of sites and has become the default choice for developers who want production-grade web serving without the complexity of traditional server configuration. This guide covers everything from basic setup to advanced production deployment patterns.

What Is Caddy and Why It Matters

Caddy is a powerful, extensible web server platform written in Go. It was created by Matt Holt in 2015 and has since grown into a mature, production-ready server used by companies like Cloudflare, Ardan Labs, and thousands of startups. Caddy v2 (the current major version) was a complete rewrite that introduced a modular architecture, a JSON config system, and the Caddyfile adapter.

What makes Caddy unique is its philosophy of secure defaults. HTTPS is automatic and on by default. HTTP requests are automatically redirected to HTTPS. OCSP stapling is enabled. Modern TLS versions are enforced. Headers are secure by default. This means a fresh Caddy installation with zero configuration is already more secure than most manually configured Nginx or Apache setups.

  • Automatic HTTPS via Let's Encrypt and ZeroSSL with zero configuration
  • Written in Go — single static binary, no dependencies, cross-platform
  • Caddyfile: human-readable configuration that is dramatically simpler than Nginx or Apache
  • Native HTTP/3 (QUIC) support enabled by default
  • Admin API for live configuration changes without downtime
  • Extensible module system — add functionality without forking
  • Graceful reloads, automatic certificate management, and OCSP stapling

Caddy vs Nginx vs Apache vs Traefik

Each web server has strengths suited to different use cases. This comparison helps you decide which is right for your project.

FeatureCaddyNginxApacheTraefik
Automatic HTTPSBuilt-in (zero config)Manual (certbot)Manual (certbot)Built-in (config needed)
Config syntaxCaddyfile (simple)nginx.conf (complex)httpd.conf + .htaccessYAML/TOML/labels
Raw performanceVery goodExcellentGoodGood
Memory usageLow (~20MB)Very low (~5MB)High (~50-200MB)Low (~30MB)
HTTP/3 supportNative (default on)ExperimentalNot supportedExperimental
Docker integrationGood (labels/networks)Manual configManual configExcellent (native)
Plugin systemGo modulesC modules (compile)Dynamic modulesGo plugins
Learning curveEasyModerateModerate-HardModerate
Written inGoCCGo
LicenseApache 2.0BSD 2-ClauseApache 2.0MIT

Installation

Caddy distributes as a single static binary. No runtime dependencies are needed.

Package Managers

# Debian / Ubuntu
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
  | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
  | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddy

# Fedora / RHEL
dnf copr enable @caddy/caddy && dnf install caddy

# macOS
brew install caddy

# Windows
choco install caddy

Docker

# Pull official Caddy image
docker pull caddy:latest

# Run with Caddyfile
docker run -d --name caddy \
  -p 80:80 -p 443:443 -p 443:443/udp \
  -v $PWD/Caddyfile:/etc/caddy/Caddyfile \
  -v caddy_data:/data \
  -v caddy_config:/config \
  caddy:latest

Direct Binary Download

# Download from GitHub releases
curl -OL https://github.com/caddyserver/caddy/releases/latest/download/caddy_2.9.1_linux_amd64.tar.gz
tar xzf caddy_2.9.1_linux_amd64.tar.gz
sudo mv caddy /usr/bin/caddy
sudo chmod +x /usr/bin/caddy

# Verify installation
caddy version

Building from Source with xcaddy

# Install xcaddy (Caddy build tool)
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

# Build Caddy with custom plugins
xcaddy build \
  --with github.com/caddy-dns/cloudflare \
  --with github.com/mholt/caddy-ratelimit \
  --with github.com/caddyserver/transform-encoder

Caddyfile Basics

The Caddyfile is Caddy's human-friendly configuration format. It is designed to be easy to read and write, even for complex setups.

Automatic HTTPS (Default Behavior)

When you specify a domain name in your Caddyfile, Caddy automatically obtains a TLS certificate from Let's Encrypt, redirects HTTP to HTTPS, and enables OCSP stapling. No additional configuration is needed.

# Minimal Caddyfile — automatic HTTPS!
# Just specify your domain and Caddy does the rest
example.com {
    respond "Hello, world!"
}

# What Caddy does automatically:
# 1. Obtains TLS certificate from Let's Encrypt
# 2. Redirects http://example.com → https://example.com
# 3. Enables OCSP stapling
# 4. Enforces modern TLS (1.2+)
# 5. Renews certificate before expiry

Static File Server

# Serve static files with compression and caching
example.com {
    root * /var/www/html
    encode gzip zstd
    file_server

    # Cache static assets for 1 year
    @static path *.css *.js *.png *.jpg *.svg *.woff2
    header @static Cache-Control "public, max-age=31536000, immutable"

    # Cache HTML for 1 hour
    @html path *.html
    header @html Cache-Control "public, max-age=3600"
}

Redirects

# Redirect www to non-www
www.example.com {
    redir https://example.com{uri} permanent
}

# Redirect old paths
example.com {
    redir /old-page /new-page permanent
    redir /blog/old-slug /blog/new-slug 301
}

Reverse Proxy Configuration

Caddy is an excellent reverse proxy. It supports single backends, load balancing, health checks, WebSocket proxying, and header manipulation.

Single Backend

# Simple reverse proxy — 3 lines!
app.example.com {
    reverse_proxy localhost:3000
}

# With header manipulation
api.example.com {
    reverse_proxy localhost:8080 {
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-Proto {scheme}
        header_down -Server
    }
}

Load Balancing

# Load balancing across multiple backends
app.example.com {
    reverse_proxy {
        to localhost:3001
        to localhost:3002
        to localhost:3003

        lb_policy round_robin
        # Other policies: random, first, ip_hash,
        # uri_hash, least_conn, header
    }
}

Health Checks

# Active health checks
app.example.com {
    reverse_proxy {
        to localhost:3001
        to localhost:3002

        health_uri /health
        health_interval 10s
        health_timeout 5s
        health_status 200

        # Passive health checks (circuit breaker)
        fail_duration 30s
        max_fails 3
        unhealthy_latency 500ms
    }
}

WebSocket Proxying

# WebSocket proxying — Caddy handles upgrade automatically
ws.example.com {
    reverse_proxy localhost:8080
    # That's it! Caddy detects WebSocket upgrades
    # and handles them transparently.
}

# With path-based routing
example.com {
    reverse_proxy /ws/* localhost:8080
    reverse_proxy /api/* localhost:3000
    file_server
}

Nginx Equivalent Comparison

Here is the same reverse proxy configuration in both Caddy and Nginx for comparison.

# CADDY — Reverse proxy with HTTPS (3 lines)
app.example.com {
    reverse_proxy localhost:3000
}

# NGINX equivalent (~25 lines + certbot + cron)
# server { listen 80; server_name app.example.com;
#   return 301 https://$server_name$request_uri; }
# server { listen 443 ssl http2;
#   server_name app.example.com;
#   ssl_certificate /etc/letsencrypt/live/.../fullchain.pem;
#   ssl_certificate_key /etc/letsencrypt/live/.../privkey.pem;
#   location / {
#     proxy_pass http://localhost:3000;
#     proxy_set_header Host $host;
#     proxy_set_header X-Real-IP $remote_addr;
#     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#   }
# } + certbot setup + renewal cron job

Automatic HTTPS Deep Dive

Caddy's automatic HTTPS is its most compelling feature. It handles the entire TLS lifecycle automatically.

  • Obtains certificates from Let's Encrypt (or ZeroSSL) using the ACME protocol
  • Automatically renews certificates before they expire (default: 30 days before expiry)
  • Redirects HTTP to HTTPS automatically
  • Enables OCSP stapling for faster TLS handshakes
  • Supports ACME DNS challenge for wildcard certificates
  • On-demand TLS: obtain certificates at handshake time for dynamic domains

Wildcard Certificates

Caddy supports wildcard certificates using the DNS challenge. You need a DNS provider plugin for your registrar.

# Wildcard certificate with Cloudflare DNS
# Build Caddy with: xcaddy build --with github.com/caddy-dns/cloudflare

*.example.com {
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }

    @app host app.example.com
    handle @app {
        reverse_proxy localhost:3000
    }

    @api host api.example.com
    handle @api {
        reverse_proxy localhost:8080
    }

    handle {
        respond "Unknown subdomain" 404
    }
}

On-Demand TLS

On-demand TLS obtains certificates during the TLS handshake. This is useful for SaaS platforms where customer domains are not known in advance.

# On-demand TLS for SaaS custom domains
{
    on_demand_tls {
        ask http://localhost:5555/check-domain
        interval 5m
        burst 10
    }
}

https:// {
    tls {
        on_demand
    }
    reverse_proxy localhost:3000
}

Docker Deployment

Caddy works exceptionally well in Docker environments. Here are common deployment patterns.

Basic Docker Setup

# Dockerfile for custom Caddy
FROM caddy:2-builder AS builder
RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare \
    --with github.com/mholt/caddy-ratelimit

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

Docker Compose Multi-Service

# docker-compose.yml — Multi-service with Caddy
services:
  caddy:
    image: caddy:latest
    restart: unless-stopped
    ports: ["80:80", "443:443", "443:443/udp"]
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
    networks: [web]
  app:
    build: ./app
    expose: ["3000"]
    networks: [web]
  api:
    build: ./api
    expose: ["8080"]
    networks: [web, backend]
  db:
    image: postgres:16-alpine
    volumes: [pgdata:/var/lib/postgresql/data]
    networks: [backend]
volumes: { caddy_data: {}, pgdata: {} }
networks: { web: {}, backend: {} }

# Caddyfile for the compose setup
app.example.com { reverse_proxy app:3000 }
api.example.com { reverse_proxy api:8080 }

PHP and WordPress with Caddy

Caddy has first-class PHP support through the php_fastcgi directive, which handles path splitting, index file resolution, and FastCGI communication.

Basic PHP Setup

# Basic PHP with Caddy
example.com {
    root * /var/www/html
    encode gzip
    php_fastcgi unix//run/php/php8.3-fpm.sock
    file_server
}

WordPress Configuration

# WordPress with Caddy — complete config
example.com {
    root * /var/www/wordpress
    encode gzip

    # PHP processing
    php_fastcgi unix//run/php/php8.3-fpm.sock

    # Static file serving
    file_server

    # Block access to sensitive files
    @blocked path /xmlrpc.php /wp-config.php
    respond @blocked 403

    # Cache static assets
    @static path *.css *.js *.png *.jpg *.gif *.ico *.svg *.woff2
    header @static Cache-Control "public, max-age=31536000"

    # Security headers
    header {
        X-Content-Type-Options nosniff
        X-Frame-Options SAMEORIGIN
        Referrer-Policy strict-origin-when-cross-origin
        -Server
    }
}

SPA Hosting (React, Vue, Next.js)

Caddy handles single-page applications elegantly with the try_files directive for client-side routing fallback.

# React / Vue SPA hosting
app.example.com {
    root * /var/www/app/dist
    encode gzip zstd

    # SPA client-side routing fallback
    try_files {path} /index.html
    file_server

    # Proxy API requests to backend
    reverse_proxy /api/* localhost:8080

    # Cache static assets aggressively
    @static path *.js *.css *.png *.svg *.woff2
    header @static Cache-Control "public, max-age=31536000, immutable"
}

# Next.js / Nuxt.js (SSR mode)
ssr.example.com {
    reverse_proxy localhost:3000
}

Caddy as API Gateway

Caddy can function as an API gateway, routing requests to different backend services based on path prefixes, with rate limiting and CORS headers.

# Caddy as API Gateway
api.example.com {
    # CORS headers
    header Access-Control-Allow-Origin "https://app.example.com"
    header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
    header Access-Control-Allow-Headers "Authorization, Content-Type"

    # Route to different microservices
    reverse_proxy /users/* user-service:3001
    reverse_proxy /orders/* order-service:3002
    reverse_proxy /payments/* payment-service:3003
    reverse_proxy /notifications/* notification-service:3004

    # Health check endpoint
    respond /health 200
}

HTTP/3 and QUIC Support

Caddy supports HTTP/3 (QUIC) natively and enables it by default. HTTP/3 uses UDP instead of TCP, providing faster connection establishment, better performance on lossy networks, and eliminates head-of-line blocking. No additional configuration is needed — Caddy advertises HTTP/3 support via the Alt-Svc header automatically.

# HTTP/3 is enabled by default in Caddy!
# Make sure to expose UDP port 443:
# docker run -p 443:443/udp ...

# To explicitly disable HTTP/3:
{
    servers {
        protocols h1 h2
        # Omitting h3 disables QUIC
    }
}

# Response headers will include:
# Alt-Svc: h3=":443"; ma=2592000

Caddy JSON Config vs Caddyfile

Caddy has two configuration interfaces: the Caddyfile (human-friendly) and JSON (machine-friendly). The Caddyfile is actually an adapter that converts to JSON internally. The JSON config provides full control over every Caddy feature and is ideal for programmatic configuration.

# Convert Caddyfile to JSON (see what Caddy generates internally)
caddy adapt --config Caddyfile --pretty

# JSON config gives full control — ideal for programmatic configs
# { "apps": { "http": { "servers": { "srv0": {
#   "listen": [":443"],
#   "routes": [{ "match": [{"host": ["app.example.com"]}],
#     "handle": [{"handler": "reverse_proxy",
#       "upstreams": [{"dial": "localhost:3000"}]}] }]
# }}}}}

Caddy Admin API

Caddy exposes a REST API on localhost:2019 for live configuration management. You can load, modify, and inspect configuration without restarting the server.

# Caddy Admin API (localhost:2019)

# Load new JSON config
curl localhost:2019/load -H "Content-Type: application/json" -d @caddy.json

# Load Caddyfile via adapter
curl localhost:2019/load -H "Content-Type: text/caddyfile" --data-binary @Caddyfile

# Get current config
curl localhost:2019/config/

# Graceful reload from CLI
caddy reload --config /etc/caddy/Caddyfile

Rate Limiting and Security Headers

Caddy provides built-in security features and can be extended with rate limiting modules.

# Security headers and rate limiting
example.com {
    header {
        Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        Referrer-Policy strict-origin-when-cross-origin
        Permissions-Policy "camera=(), microphone=(), geolocation=()"
        -Server
    }

    # Basic authentication
    basicauth /admin/* {
        admin $2a$14$hashed_password_here
    }

    # IP allowlist
    @blocked not remote_ip 10.0.0.0/8
    respond @blocked /internal/* 403

    reverse_proxy localhost:3000
}

Logging and Metrics

Caddy produces structured JSON logs by default, which are easy to parse with tools like jq, Loki, or Elasticsearch. It also supports Prometheus metrics via a module.

# Structured JSON logging with rotation
example.com {
    log {
        output file /var/log/caddy/access.log {
            roll_size 100MiB
            roll_keep 10
        }
        format json
    }
    reverse_proxy localhost:3000
}
# Prometheus metrics: built-in at :2019/metrics

Performance Tuning

Caddy performs well out of the box, but can be tuned for high-traffic scenarios.

  • Enable static file compression with encode gzip zstd
  • Use file_server with precompressed to serve pre-compressed files
  • Tune worker count with GOMAXPROCS environment variable
  • Enable HTTP/3 for better performance on mobile and lossy networks
  • Use header caching directives for static assets
  • Configure connection keep-alive timeouts appropriately
# Performance-optimized Caddyfile
example.com {
    root * /var/www/html

    # Enable compression (gzip + zstd)
    encode zstd gzip

    # Serve pre-compressed files if available
    file_server {
        precompressed zstd gzip br
    }

    # Aggressive caching for static assets
    @immutable path *.js *.css *.woff2 *.png *.jpg *.svg
    header @immutable {
        Cache-Control "public, max-age=31536000, immutable"
        Vary Accept-Encoding
    }

    # Keep-alive settings
    # (Caddy defaults are good for most cases)
}

Caddy Modules and Plugins

Caddy has a rich plugin ecosystem. Plugins are Go modules that extend Caddy's functionality. You build a custom Caddy binary with the plugins you need using xcaddy.

# Popular Caddy modules (install with xcaddy)
xcaddy build \
  --with github.com/caddy-dns/cloudflare \   # DNS challenge (Cloudflare)
  --with github.com/caddy-dns/route53 \      # DNS challenge (AWS)
  --with github.com/mholt/caddy-ratelimit \  # Rate limiting
  --with github.com/caddyserver/cache-handler # HTTP caching

# Prometheus metrics — built-in since v2.7 (no plugin needed)
# List installed modules
caddy list-modules

Migration from Nginx to Caddy

Migrating from Nginx to Caddy typically simplifies your configuration dramatically. Here are common Nginx patterns and their Caddy equivalents.

# Migration cheat sheet: Nginx → Caddy
#
# server { listen 443 ssl; server_name X; }  →  X { }
# location / { proxy_pass http://...; }      →  reverse_proxy localhost:3000
# location ~ \.php$ { fastcgi_pass ...; }    →  php_fastcgi unix//run/php/php8.3-fpm.sock
# try_files $uri $uri/ /index.html           →  try_files {path} /index.html
# rewrite ^/old$ /new permanent               →  redir /old /new permanent
# add_header X-Frame-Options DENY             →  header X-Frame-Options DENY
# ssl_certificate + certbot + cron            →  (automatic — nothing needed)
# gzip on; gzip_types ...                     →  encode gzip zstd

Production Deployment Best Practices

  • Run Caddy as a systemd service for automatic restart and logging
  • Use the Caddyfile for human-managed configs, JSON for automation
  • Store Caddy data directory (/data) on persistent storage for certificates
  • Set up log rotation for access and error logs
  • Monitor certificate expiry with Prometheus metrics
  • Use the admin API endpoint only on localhost (default)
  • Keep Caddy updated — security patches are released regularly
  • Test configuration changes with caddy validate before applying
# Install as systemd service (package managers do this automatically)
sudo systemctl enable --now caddy

# Validate config before applying
caddy validate --config /etc/caddy/Caddyfile

# Graceful reload (no downtime)
sudo systemctl reload caddy

# Check status and logs
sudo systemctl status caddy
journalctl -u caddy --no-pager -f

Common Issues and Troubleshooting

  • Certificate not obtained: Check that ports 80 and 443 are accessible from the internet. Caddy needs these for the ACME HTTP challenge.
  • Permission denied on port 80/443: On Linux, run setcap cap_net_bind_service=+ep /usr/bin/caddy or use systemd socket activation.
  • Too many certificates: Let's Encrypt has rate limits (50 certificates per registered domain per week). Use staging for testing.
  • Caddy not reloading config: Use caddy reload instead of restarting. Check caddy validate first.
  • Reverse proxy 502 errors: Verify the backend is running and accessible. Check if the backend expects specific Host headers.
  • WebSocket not working through proxy: Caddy handles WebSocket upgrades automatically. Check if your backend requires specific headers.
  • High memory usage: Check for excessive logging. Caddy's memory usage scales with the number of active connections and certificates.
  • Slow certificate issuance: DNS propagation can be slow for DNS challenges. Use the HTTP challenge when possible.

Frequently Asked Questions

Is Caddy ready for production use?

Yes. Caddy v2 has been production-ready since 2020 and is used by thousands of companies. It handles automatic HTTPS, graceful reloads, and has been extensively battle-tested. Companies like Cloudflare, Fly.io, and Replit use Caddy in production.

How does Caddy compare to Nginx in performance?

For most real-world workloads, Caddy and Nginx perform similarly. Nginx has a slight edge in raw static file throughput due to its C implementation, but Caddy's Go-based architecture is more than fast enough for the vast majority of use cases. The difference is typically less than 10% and is negligible compared to backend processing time.

Can Caddy replace Nginx as a reverse proxy?

Absolutely. Caddy excels as a reverse proxy with automatic HTTPS, load balancing, health checks, and WebSocket support. The configuration is dramatically simpler than Nginx. Many teams have migrated from Nginx to Caddy specifically for the reverse proxy use case.

Does Caddy support wildcard certificates?

Yes. Caddy supports wildcard certificates using the ACME DNS challenge. You need a DNS provider plugin for your registrar (Cloudflare, Route53, Google Cloud DNS, etc.). The configuration is straightforward and certificates are automatically renewed.

How does Caddy handle certificate renewal?

Caddy automatically renews certificates 30 days before expiry. It uses the same ACME challenge type that was used for initial issuance. If renewal fails, Caddy retries with exponential backoff and logs warnings. Certificates are stored in the Caddy data directory and persist across restarts.

Can I use Caddy with Docker and Kubernetes?

Yes. Caddy has an official Docker image and works well in container environments. For Kubernetes, you can use Caddy as an ingress controller or as a sidecar proxy. The Docker Compose pattern with Caddy as the entry point for multiple services is very popular.

Is Caddy free and open source?

Yes. Caddy is licensed under the Apache 2.0 license and is completely free to use, including for commercial purposes. There are no paid tiers or enterprise editions. The full source code is on GitHub.

How do I migrate from Nginx to Caddy?

Start by translating your nginx.conf to a Caddyfile. Most Nginx directives have direct Caddy equivalents but with simpler syntax. Remove all SSL/TLS configuration (Caddy handles it automatically). Test with caddy validate, then switch DNS. The migration typically reduces config complexity by 60-80%.

𝕏 Twitterin LinkedIn
Cet article vous a-t-il aidé ?

Restez informé

Recevez des astuces dev et les nouveaux outils chaque semaine.

Pas de spam. Désabonnez-vous à tout moment.

Essayez ces outils associés

{ }JSON Formatter🔄cURL to Code Converter

Articles connexes

Guide de Configuration Nginx : Du Setup de Base a la Production

Guide complet de configuration Nginx. Apprenez les server blocks, proxy inverse, SSL/TLS et load balancing.

Tutoriel Docker Compose : des bases aux stacks prets pour la production

Tutoriel complet Docker Compose : syntaxe docker-compose.yml, services, reseaux, volumes, variables d'environnement, healthchecks et exemples Node.js/Python/WordPress.

Guide Complet Coolify 2026 : PaaS Auto-Hébergé

Maîtrisez Coolify, la plateforme auto-hébergée open source : installation, déploiement, bases de données, domaines personnalisés et bonnes pratiques.