Directive #
Directives are the main building blocks inside a Caddyfile site block. If the site address tells Caddy where it should listen for requests, then directives tell Caddy what to do with those requests.
Without directives, Caddy is just an empty server doing nothing. By using one or more directives, you can turn Caddy into a very fast static file server, a robust reverse proxy in front of your application cluster, a PHP server with FastCGI integration, or a layered security gateway filtering access with authentication. Understanding how each major directive works, its writing anatomy, and its configuration options is the most crucial skill for mastering Caddy in production.
Directive Request Processing Order #
When an HTTP request comes in, Caddy processes it through a series of active directives. This processing order follows Caddy’s internal priority rules.
To visualize how a client request flows through various directives, gets forwarded to the backend, and returns as a response, look at the sequence diagram below:
sequenceDiagram
autonumber
participant Client as "Client (Browser)"
participant Caddy as "Caddy Web Server"
participant Backend as "Upstream App (Port 3000)"
Client->>Caddy: "GET /admin/users (HTTP Request)"
note over Caddy: 1. Evaluate Matcher & Directive Order
note over Caddy: 2. root /var/www (Set directory)
note over Caddy: 3. rewrite (Modify internal URI if any)
Caddy->>Client: "4. basicauth (Send 401 Challenge if Not Authenticated)"
Client->>Caddy: "GET /admin/users (Send credentials)"
note over Caddy: 5. basicauth (Bcrypt Hash Validation Successful)
note over Caddy: 6. encode (Prepare gzip/zstd filter)
Caddy->>Backend: "7. reverse_proxy (Send request with Updated Headers)"
Backend-->>Caddy: "8. Response (Send HTML/JSON)"
note over Caddy: 9. header (Add Security Headers & Remove Server header)
note over Caddy: 10. Compress response via encode (gzip/zstd)
Caddy-->>Client: "11. Response (Send to Client with TLS Active)"Directive Writing Anatomy #
The Caddyfile is designed to be very flexible. Directives can be written in three formats depending on the configuration complexity you need:
1. Inline Format (One Line) #
Used for simple instructions that don’t need many parameters or extra configuration.
example.com {
# Format: directive_name [arguments...]
root * /var/www/html
file_server
encode gzip
}
2. Block Format #
Used when a directive has many custom parameters (subdirectives) that need detailed setup to stay structured and readable.
example.com {
# Format:
# directive_name {
# subdirective1 argument
# subdirective2 argument
# }
log {
output file /var/log/caddy/access.log
format json
level INFO
}
}
3. Mixed Format (Inline & Block) #
Many Caddy directives support inline writing for standard cases, but also accept curly brace blocks if you want advanced tuning.
example.com {
# Inline: Fast and uses built-in defaults
encode gzip zstd
# Block: Customize compression level and minimum file size
encode {
gzip 6
zstd
minimum_length 1024
}
}
Explanation of 10+ Core Directives #
Here’s an in-depth discussion of the most frequently used directives for building real-world server configurations:
1. root (Setting the Root Folder)
#
Sets the working directory path where Caddy looks for static files to serve via file_server or process via php_fastcgi.
# Format: root [matcher] path
root * /var/www/my-app
The * argument is a wildcard request matcher telling Caddy to use this root folder for all request traffic.
2. file_server (Serving Static Files)
#
Enables Caddy’s high-performance static web server capability for serving images, HTML files, CSS, JavaScript, or other documents from local disk.
example.com {
root * /var/www/static
# Enable the file server service with extra options
file_server {
# Enable directory listing display when an index file isn't found
browse
# Hide sensitive files from directory listings and block direct access
hide .git .env *.config
# Index file search priority order (default: index.html)
index index.html index.htm
}
}
3. reverse_proxy (Proxy to Application Backends)
#
One of Caddy’s strongest features for forwarding incoming requests to one or more backend application servers (like Node.js, Go, Python, Java, or Docker containers).
example.com {
# Forward requests to the backend upstream
reverse_proxy localhost:3000 {
# Manipulate request headers before sending to the backend
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}
# Load Balancing options (if more than one upstream)
# Caddy supports round_robin, random, ip_hash, etc.
lb_policy round_robin
# Health checks to detect dead upstreams
health_uri /health-check
health_interval 10s
health_timeout 5s
# Connection timeout tuning
transport http {
dial_timeout 5s
response_header_timeout 30s
}
}
}
4. encode (Content Compression)
#
Reduces the size of response data sent to the client to save server bandwidth and speed up web page loading.
example.com {
# Enable gzip and zstd (Caddy picks the best one based on the user's browser)
encode gzip zstd {
# Set gzip compression quality (1-9, default: 4)
gzip 5
# Set zstd quality
zstd
# Don't compress files under 512 bytes (because it's inefficient)
minimum_length 512
}
}
5. header (HTTP Header Manipulation)
#
Adds, modifies, or removes HTTP headers in responses sent to the user’s browser. This is the main tool for enforcing security policies (security headers).
example.com {
header {
# Add a new header (or replace it if it already exists)
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
# Remove Caddy/backend default headers for server info security
-Server
-X-Powered-By
# Add a new value to an existing header without overwriting it (append)
+Cache-Control "public"
}
}
6. redir (External URL Redirect)
#
Sends an HTTP redirect status (like 301 for permanent or 302 for temporary) back to the client browser so they access a new URL.
# Redirect traffic permanently
redir /site-map /sitemap.xml permanent
# Redirect while preserving query parameters
redir /products/* /new-items/{path} 308
7. rewrite (Internal URI Rewriting)
#
Unlike redir, rewrite silently changes the request URI path inside the Caddy engine without telling the client browser. The browser still sees the original URL in its address bar.
example.com {
# If a user accesses /help, Caddy processes it as /support/faq.html in the background
rewrite /help /support/faq.html
# Perfect for supporting Single Page Application (SPA) architectures like React/Vue/Angular
# If the file isn't found on disk, route the request internally to index.html
@notFile {
not file {path}
not path /api/*
}
rewrite @notFile /index.html
file_server
}
The Fundamental Difference: redir vs rewrite
#
redir(External Redirect): Caddy sends an HTTP 301/302/308 response to the client browser. The browser then reconnects to the new URL. This causes two HTTP round-trips over the network. The URL in the browser changes.rewrite(Internal Redirect): Caddy directly routes the request to another handler internally. Only one HTTP round-trip happens. The URL in the client browser stays the same.
8. respond (Sending Direct Responses)
#
Sends plain text, HTML, or JSON responses instantly to the client without reading a file from disk or contacting a backend server.
# Send plain text with status code 200
respond "Server OK" 200
# Send JSON data for a health endpoint
@health path /health
respond @health `{"status":"healthy","database":"connected"}` 200 {
close
}
9. basicauth (HTTP Basic Authentication)
#
Secures site paths with a username and password protection layer. Passwords must be stored in bcrypt hash format.
example.com {
# Restrict access to the /admin/ folder
basicauth /admin/* {
# Username: admin, Password: password123 (hash generated using the Caddy CLI command)
admin $2a$14$yR41x7U715c0mpr3ss10n.hashexamplevalue
}
reverse_proxy localhost:3000
}
You can generate the password hash with the following terminal command:
caddy hash-password --plaintext "password123"
10. tls (Certificate & Encryption Configuration)
#
Used to customize Caddy’s TLS behavior, such as setting the ACME admin email, loading paid SSL certificates manually, or setting the minimum protocol.
example.com {
# Use a paid or custom SSL certificate you already own
tls /etc/ssl/certs/site.pem /etc/ssl/private/site.key
# Or configure modern encryption options
tls {
# Only allow TLS 1.3 for maximum security
protocols tls1.3
# Set a custom certificate provider (e.g., ZeroSSL)
ca https://acme.zerossl.com/v2/DV90
}
}
11. log (System Access Log)
#
Configures incoming request data logging (access logs) for traffic analytics or security audits.
example.com {
log {
# Write logs to a physical file on the system
output file /var/log/caddy/access.log {
roll_size 100mb # Rotate the log file after reaching 100MB
roll_keep 10 # Keep a maximum of 10 old log files
roll_keep_for 720h # Keep old logs for 30 days (720 hours)
}
# Use JSON format so it's easy to read by Elasticsearch or Logstash
format json
level INFO
}
reverse_proxy localhost:3000
}
12. php_fastcgi (PHP Processing)
#
A special directive that makes PHP-based web configuration (like WordPress, Laravel, or Drupal) very easy by automatically forwarding .php file requests to a local PHP-FPM service.
example.com {
root * /var/www/my-laravel-app/public
# Forward PHP requests to the PHP-FPM unix socket
php_fastcgi unix//run/php/php8.2-fpm.sock {
# Set internal PHP environment variables if needed
env PHP_ADMIN_VALUE "expose_php=Off \n memory_limit=256M"
}
file_server
}
Directive Combination Patterns (Production Use Cases) #
In real production environments, you almost always use combinations of the directives above. Here are ready-to-use configuration templates for common use cases:
1. Single Page Application (SPA) with Maximum Security #
This template suits serving React, Vue, Svelte, or Angular frontends hosted on a local static server.
app.mydomain.com {
root * /var/www/dist
# Enable modern compression
encode gzip zstd
# Apply a strict security header set
header {
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
X-Frame-Options "DENY"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline';"
-Server
-X-Powered-By
}
# SPA Router fallback: route internal route requests to index.html
@notFile {
not file {path}
}
rewrite @notFile /index.html
# Enable static file serving
file_server
}
2. API Gateway with CORS Configuration #
When Caddy acts as the front-line gateway for your microservices API cluster and must serve cross-origin requests (Cross-Origin Resource Sharing).
api.mydomain.com {
# Set CORS headers globally for this block
header {
Access-Control-Allow-Origin "https://app.mydomain.com"
Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
Access-Control-Allow-Credentials "true"
Access-Control-Max-Age "3600"
}
# Intercept browser pre-flight OPTIONS requests instantly
@options method OPTIONS
respond @options "" 204
# Forward the original request to the upstream API server
reverse_proxy localhost:8080 {
# Add the client's real IP tracking header
header_up X-Real-IP {remote_host}
}
# JSON log configuration
log {
output file /var/log/caddy/api_access.log {
roll_size 50mb
}
format json
}
}
3. WordPress Production Setup #
WordPress needs special handling because it combines static assets (images, CSS, JS), PHP processing, and blocking sensitive configuration files.
blog.mydomain.com {
root * /var/www/wordpress
# Response compression
encode gzip zstd
# Block access to sensitive WordPress files for security
@blockSens {
path /wp-config.php
path /xmlrpc.php
path /.env
path /.git/*
}
respond @blockSens "Access Denied" 403
# FastCGI integration to process PHP files
php_fastcgi unix//run/php/php8.2-fpm.sock
# Serve static assets, hide the default Apache htaccess file if present
file_server {
hide .htaccess
}
}
Summary #
- Directives are the Caddyfile’s operational instruction blocks placed inside a site block to manipulate requests and responses.
reverse_proxyis the backbone of backend application integration, whilefile_serveris the local static file serving engine.rewriteworks internally inside the Caddy server, whileredirtriggers an external redirect by sending an HTTP 3xx response to the client browser.- PHP application integration is easily done with a single
php_fastcgidirective line pointed at the PHP-FPM socket.headerhelps secure your website by removing built-in server info (-Server) and injecting modern security headers.- Passwords for
basicauthprotection must be converted to a bcrypt encryption hash using thecaddy hash-passwordCLI command.