Caddy is a modern web server written in Go whose headline feature is automatic HTTPS: point a domain at the server, name it in the config, and Caddy obtains and renews certificates by itself. It also enables HTTP/2 and HTTP/3 by default and has a compact, readable config format.

1. Install from the official repository

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 -y caddy
caddy version

(Check caddyserver.com for current instructions; RHEL-family systems use a COPR repository.)

The package creates a caddy system user and a systemd service, and serves a welcome page on port 80.

2. Open the firewall

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp      # HTTP/3

Port 80 must be reachable for certificate issuance (HTTP challenge) and redirects.

3. Serve a static site

Put files in place:

sudo mkdir -p /var/www/example.com
echo '<h1>Hello from Caddy</h1>' | sudo tee /var/www/example.com/index.html

Edit /etc/caddy/Caddyfile:

example.com, www.example.com {
	root * /var/www/example.com
	file_server
	encode zstd gzip
}

Reload:

sudo systemctl reload caddy

That’s it. With DNS pointing to the server, Caddy obtains certificates for both names within seconds, redirects HTTP to HTTPS, and serves the site. Check progress with journalctl -u caddy -f.

Redirect www to the bare domain instead:

www.example.com {
	redir https://example.com{uri} permanent
}
example.com {
	root * /var/www/example.com
	file_server
}

4. Reverse proxy an app

app.example.com {
	reverse_proxy 127.0.0.1:3000
}

Headers (X-Forwarded-For, X-Forwarded-Proto), WebSockets and HTTP/2 are handled automatically. More in Caddy as a reverse proxy.

5. PHP with PHP-FPM

sudo apt install -y php-fpm
blog.example.com {
	root * /var/www/blog
	php_fastcgi unix//run/php/php8.3-fpm.sock
	file_server
}

php_fastcgi includes the usual front-controller rewrites, so WordPress and Laravel-style pretty URLs work without extra rules. PHP-FPM’s socket must be accessible to the caddy user — add it to the www-data group (sudo usermod -aG www-data caddy) or adjust the pool’s listen.owner/listen.group, then restart both services.

6. Global options

At the top of the Caddyfile:

{
	email [email protected]
	# admin off
}

The email is used for the ACME account (expiry notices). Caddy exposes an admin API on localhost:2019 by default, which caddy reload uses; you can disable it if you don’t need it.

7. Managing Caddy

sudo systemctl reload caddy            # graceful config reload
caddy validate --config /etc/caddy/Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile
journalctl -u caddy --since today

Certificates and keys live in /var/lib/caddy/.local/share/caddy/ — include it in backups to avoid re-issuing everything after a rebuild.

8. Logs

example.com {
	log {
		output file /var/log/caddy/example.com.log
		format json
	}
	root * /var/www/example.com
	file_server
}

Create /var/log/caddy owned by caddy. Caddy rotates its log files itself.

Caddy vs Nginx

Caddy Nginx
HTTPS Automatic Certbot or manual
Config Short Caddyfile More verbose, very flexible
HTTP/3 On by default Supported, needs configuration
Ecosystem/tutorials Growing Enormous
Raw performance Excellent Excellent

For most sites either is fine. Caddy wins on simplicity; Nginx on ubiquity and advanced tuning — see installing Nginx. Want Caddy in containers? See Caddy with Docker Compose.

  • Caddy
  • Web Server
  • Linux