Prerequisites & VPS Selection
💡 Tip: Caddy automatically obtains TLS certificates from Let's Encrypt. Ensure your domain's DNS A/AAAA records point to your server before starting Caddy.
Install Caddy
Install Caddy from the official repository for the latest stable version:
# Install required packages
sudo apt update
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
# Add Caddy GPG key
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
# Add Caddy repository
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
# Install Caddy
sudo apt update
sudo apt install caddy# Check Caddy version
caddy version
# Check service status
sudo systemctl status caddyConfigure the firewall to allow HTTP and HTTPS traffic:
# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reloadBasic Configuration
Caddy uses the Caddyfile for configuration, located at /etc/caddy/Caddyfile:
# /etc/caddy/Caddyfile
example.com {
root * /var/www/example.com
file_server
}# Site 1
example.com {
root * /var/www/example.com
file_server
}
# Site 2
blog.example.com {
root * /var/www/blog
file_server
}
# WWW redirect
www.example.com {
redir https://example.com{uri} permanent
}# Validate configuration
caddy validate --config /etc/caddy/Caddyfile
# Format and reload (zero-downtime)
caddy fmt --overwrite /etc/caddy/Caddyfile
sudo systemctl reload caddyStatic File Server
Serve static files with compression and custom error pages:
example.com {
root * /var/www/html
file_server
# Enable compression
encode gzip zstd
# Custom error pages
handle_errors {
@404 {
expression {http.error.status_code} == 404
}
rewrite @404 /404.html
file_server
}
}example.com {
root * /var/www/spa
encode gzip zstd
# Try files, fallback to index.html for SPA routing
try_files {path} /index.html
file_server
}files.example.com {
root * /var/www/files
file_server browse
# Basic authentication for private directory
basicauth /private/* {
admin $2a$14$hashedpassword
}
}Reverse Proxy
Proxy requests to backend applications with automatic HTTPS:
# Proxy to local application
app.example.com {
reverse_proxy localhost:3000
}
# Proxy with custom headers
api.example.com {
reverse_proxy localhost:8080 {
header_up Host {upstream_hostport}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}example.com {
# API requests to backend
handle /api/* {
reverse_proxy localhost:8080
}
# Static assets
handle /static/* {
root * /var/www/static
file_server
}
# Default to frontend
handle {
reverse_proxy localhost:3000
}
}ws.example.com {
reverse_proxy localhost:8080 {
# WebSocket support is automatic
# Optimize connection handling
flush_interval -1
}
}Load Balancing
Distribute traffic across multiple backend servers:
app.example.com {
reverse_proxy 10.0.0.1:8080 10.0.0.2:8080 10.0.0.3:8080 {
lb_policy round_robin
}
}app.example.com {
reverse_proxy {
to 10.0.0.1:8080
to 10.0.0.2:8080
to 10.0.0.3:8080
# Load balancing policy
lb_policy least_conn
# Active health checks
health_uri /health
health_interval 10s
health_timeout 5s
health_status 200
# Passive health checks (circuit breaker)
fail_duration 30s
max_fails 3
unhealthy_status 500 502 503 504
}
}Load Balancing Policies
round_robin
Distributes requests evenly across servers. Default policy.
least_conn
Sends to server with fewest active connections.
ip_hash
Routes based on client IP for session persistence.
cookie
Sticky sessions using a cookie.
app.example.com {
reverse_proxy {
to 10.0.0.1:8080
to 10.0.0.2:8080
lb_policy cookie {
name sticky_session
ttl 24h
}
}
}PHP Applications
Configure Caddy to serve PHP applications via FastCGI:
sudo apt install php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zipexample.com {
root * /var/www/html
encode gzip
php_fastcgi unix//run/php/php8.2-fpm.sock
file_server
}wordpress.example.com {
root * /var/www/wordpress
encode gzip
# PHP handling
php_fastcgi unix//run/php/php8.2-fpm.sock
file_server
# Block access to sensitive files
@blocked {
path /xmlrpc.php
path /.htaccess
path /wp-config.php
}
respond @blocked 403
# Enable pretty permalinks
try_files {path} {path}/ /index.php?{query}
}laravel.example.com {
root * /var/www/laravel/public
encode gzip
php_fastcgi unix//run/php/php8.2-fpm.sock
file_server
# Handle Laravel routing
@notStatic {
not path /build/* /vendor/* *.ico *.css *.js *.gif *.jpg *.jpeg *.png *.svg *.woff *.woff2
file {
try_files {path} {path}/
}
not
}
rewrite @notStatic /index.php
}Security Headers
Add security headers to protect your site:
(security_headers) {
header {
# Enable HSTS
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Prevent clickjacking
X-Frame-Options "SAMEORIGIN"
# Prevent MIME type sniffing
X-Content-Type-Options "nosniff"
# XSS protection
X-XSS-Protection "1; mode=block"
# Referrer policy
Referrer-Policy "strict-origin-when-cross-origin"
# Permissions policy
Permissions-Policy "geolocation=(), microphone=(), camera=()"
# Remove server header
-Server
}
}
example.com {
import security_headers
root * /var/www/html
file_server
}example.com {
# Rate limit API endpoints
@api path /api/*
rate_limit @api {
zone api_zone {
key {remote_host}
events 100
window 1m
}
}
reverse_proxy localhost:8080
}Logging
Configure access logging for monitoring and debugging:
example.com {
log {
output file /var/log/caddy/example.com.access.log {
roll_size 100mb
roll_keep 10
roll_keep_for 720h
}
format json
}
root * /var/www/html
file_server
}example.com {
log {
output file /var/log/caddy/access.log
format filter {
wrap json
fields {
request>headers>Authorization delete
request>headers>Cookie delete
}
}
}
root * /var/www/html
file_server
}JSON API Configuration
Caddy supports JSON configuration via its admin API for programmatic management:
# /etc/caddy/Caddyfile
{
admin localhost:2019
}
example.com {
respond "Hello, World!"
}# Get current configuration
curl localhost:2019/config/
# Get specific app configuration
curl localhost:2019/config/apps/http/
# Update configuration
curl -X POST localhost:2019/config/ \
-H "Content-Type: application/json" \
-d @config.jsonTroubleshooting
Common Issues
Certificate Not Issuing
- Verify DNS A/AAAA records point to your server
- Ensure ports 80 and 443 are open and not blocked
- Check if another service is using port 80 or 443
502 Bad Gateway
- Verify the upstream service is running
- Check the upstream address and port
- Ensure firewall allows internal connections
Configuration Errors
- Validate:
caddy validate --config /etc/caddy/Caddyfile - Format:
caddy fmt --overwrite /etc/caddy/Caddyfile
# View logs
journalctl -u caddy -f
# Test configuration
caddy validate --config /etc/caddy/Caddyfile
# Reload without downtime
sudo systemctl reload caddy
# Restart service
sudo systemctl restart caddy
# Check listening ports
sudo ss -tlnp | grep caddy
# Run in foreground for debugging
caddy run --config /etc/caddy/Caddyfile{
# Global options
servers {
protocol {
experimental_http3
}
}
}
example.com {
encode gzip zstd
# Cache static assets
@static {
path *.css *.js *.ico *.gif *.jpg *.jpeg *.png *.svg *.woff *.woff2
}
header @static Cache-Control "public, max-age=31536000"
reverse_proxy localhost:8080
}Ready to Deploy Caddy?
Get started with a RamNode Cloud VPS and have Caddy running with automatic HTTPS in minutes.
