Skip to content
The exact Caddyfile I run in production — automatic HTTPS, HTTP/3, security headers, rate limiting, asset caching, all i...

Caddy + Laravel: A 50-Line Production Config

Al Amin Ahamed

Al Amin Ahamed

Senior Software Engineer

· Updated 7 hours ago 8 min read

Caddy is the cleanest web server config I've ever written. Automatic HTTPS, an actual readable config language, and zero Let's Encrypt fiddling. Here's the production setup for this Laravel portfolio.

The Whole Caddyfile

CADDY
{
    email admin@alaminahamed.com
    servers {
        protocols h1 h2 h3
    }
}

alaminahamed.com {
    root * /var/www/portfolio/current/public
    encode zstd gzip
    php_fastcgi unix//run/php/php8.4-fpm.sock

    @static {
        path *.css *.js *.png *.jpg *.jpeg *.svg *.webp *.woff2 *.ico
    }
    header @static Cache-Control "public, max-age=31536000, immutable"

    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "SAMEORIGIN"
        Referrer-Policy "strict-origin-when-cross-origin"
        Permissions-Policy "camera=(), microphone=(), geolocation=()"
        -Server
    }

    @blocked {
        path /vendor/* /storage/* /.env /.git/* /node_modules/*
    }
    respond @blocked 404

    rate_limit {
        zone api {
            key {remote_ip}
            events 60
            window 1m
        }
        zone login {
            key {remote_ip}
            events 5
            window 1m
        }
    }

    file_server
    log {
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 7
        }
    }
}

# Redirect www to apex
www.alaminahamed.com {
    redir https://alaminahamed.com{uri} permanent
}

That's it. ~50 lines for a production-ready Laravel deployment with HTTPS, HTTP/3, security headers, rate limiting, asset caching, and access logs.

What's Doing the Heavy Lifting

php_fastcgi — Caddy's one-liner replaces 20 lines of Nginx FastCGI config. It handles index.php rewrites, sets SCRIPT_FILENAME, and connects to PHP-FPM over a unix socket.

encode zstd gzip — zstd is faster and compresses better than gzip. Caddy negotiates with the browser; modern browsers get zstd.

@static { path ... } — named matcher. Reusable in multiple directives. The header directive applies cache control only to static asset responses.

rate_limit — Caddy's official rate limit module. Apply different limits per zone via path matchers.

HTTP/3

CADDY
{
    servers {
        protocols h1 h2 h3
    }
}

Caddy enables HTTP/3 on UDP 443 automatically. Confirm with:

BASH
curl -I --http3 https://alaminahamed.com
# HTTP/3 200

You'll need to open UDP 443 on the firewall:

BASH
sudo ufw allow 443/udp

Reload Without Downtime

BASH
caddy reload --config /etc/caddy/Caddyfile

Validates the config and hot-swaps. Failed validation leaves the running config untouched. No nginx -t && systemctl reload.

Multiple Sites

Need to host multiple Laravel apps on one server?

CADDY
import Caddyfile.d/*.caddy

Then drop one file per site in /etc/caddy/Caddyfile.d/. Caddy reads them all on reload.

Security Headers — Why Each One

  • HSTS — forces HTTPS for the next year. Without this, an attacker on the same wifi can MITM the first request.
  • X-Content-Type-Options: nosniff — stops the browser from guessing MIME types. Prevents an uploaded .txt from being executed as JavaScript.
  • X-Frame-Options: SAMEORIGIN — clickjacking defense. Your site can't be loaded in someone else's <iframe>.
  • Referrer-Policy: strict-origin-when-cross-origin — outbound clicks don't leak full URLs. Important if URLs contain tokens or PII.
  • Permissions-Policy — denies camera/mic/geolocation by default. Override per-route if needed.
  • -Server — strips the Caddy version header. Reduces fingerprinting.

What I Don't Do

  • Custom error pages in Caddy. Laravel handles 404/500 — Caddy just proxies.
  • Caching at the edge. Cloudflare in front handles this. Caddy's cache module is good but redundant if you have a CDN.
  • Hand-managing certs. Caddy's auto-HTTPS is the entire point. If you need wildcard certs, use the DNS challenge.

The whole stack — Caddy, PHP-FPM, Supervisor, Postgres — fits on a $10/month VPS for this site's traffic. The simplicity buys time I'd otherwise spend on infrastructure.

Share 𝕏 in
Al Amin Ahamed

Al Amin Ahamed

Senior software engineer & AI practitioner. 5+ years shipping Laravel platforms, WordPress plugins, WooCommerce extensions, and AI-driven products.

About me →

More from the blog

Need this kind of work shipped?

Available for freelance and consulting.

Laravel platforms, WordPress plugins, WooCommerce extensions, and AI integrations.