This episode dissects the Caddyfile syntax from scratch: basic structure, comments, indentation, and environment variables. You'll also learn site block variations and essential directives like root, file_server, respond, reverse_proxy, redir, and encode.

The Caddyfile is Caddy's main configuration file — and possibly the biggest reason so many developers fall in love with Caddy. Its human-friendly format makes tasks that are complicated on other web servers extremely concise. Episode 4 is the foundation: syntax, site blocks, and essential directives.
You'll learn to read and write a Caddyfile with confidence. From file structure, how to write comments, correct indentation, to using environment variables so your configuration doesn't contain sensitive data in plain text.
By the end of this episode, you'll have a set of ready-to-use Caddyfile blocks: a static site, simple responses, reverse proxy, and multiple sites in a single file. This is essential preparation before moving to advanced material.
The Caddyfile is made up of global options at the top, followed by site blocks. Comments are written with # and ignored by the parser:
# Global options
email admin@example.com
# Site block
example.com {
root * /var/www/example
file_server
}Indentation uses spaces or tabs consistently. Caddy tolerates both as long as they're consistent within a block. Empty lines are fine and actually make the file easier to read.
The Caddyfile can read environment variables with the {$NAME} syntax — the right way to store secrets and values that differ between environments:
{$SITE_DOMAIN} {
reverse_proxy localhost:{$APP_PORT}
}Replace the {$SITE_DOMAIN} placeholder with the environment value when running Caddy:
SITE_DOMAIN=app.example.com APP_PORT=8080 caddy run --config CaddyfileA single Caddyfile can contain many site blocks — one per address:
a.example.com {
respond "Site A"
}
b.example.com {
respond "Site B"
}
localhost:8080 {
respond "Local"
}If several addresses share the same configuration, list the addresses separated by commas:
example.com, www.example.com {
root * /var/www
file_server
}A site address can be a wildcard subdomain. For local development, specify the port explicitly:
*.example.com {
respond "Any subdomain"
}
localhost:3000 {
reverse_proxy 127.0.0.1:8080
}Note: addresses that are localhost or IP addresses won't trigger automatic HTTPS; only public domain names will get certificates.
respond returns a static response. To serve files, set root then enable file_server:
localhost {
respond "Welcome to Caddy!"
respond /health 200 "OK"
}
localhost {
root * /var/www/mysite
file_server
}root * /var/www/mysite sets the document directory for all paths (marked by the asterisk), and file_server serves files from that directory. File serving details are covered in episode 5.
A reverse proxy forwards requests to another application; a redirect moves a request; encode compresses responses:
api.example.com {
reverse_proxy localhost:8080
}
old.example.com {
redir https://new.example.com{uri} permanent
}
example.com {
encode zstd gzip
root * /var/www
file_server
}redir ... with the permanent status produces HTTP 301 — important for SEO when moving domains. encode zstd gzip compresses responses according to browser capabilities. Details for each are in episodes 6, 13, and 21.
Global options are written before the site block, without indentation:
{
email admin@example.com
admin 127.0.0.1:2019
servers {
trusted_proxies static 10.0.0.0/8
}
}
example.com {
root * /var/www
file_server
}email is used for ACME notifications (episode 7).admin sets the admin API address, default localhost:2019.servers holds global HTTP server settings, including trusted proxies (episode 19).The global block opens with an empty pair of curly braces on the first line. These options affect every site in a single Caddy process.
Episode 4 equipped you with the Caddyfile foundation: basic syntax with comments and indentation, environment variables for dynamic values, site block variations from a single site to wildcards, essential directives like respond, root, file_server, reverse_proxy, redir, and encode, plus global options.
Key takeaways:
#, and indentation must be consistent.{$NAME} for dynamic values.file_server needs root; respond is for static responses.redir ... permanent produces HTTP 301.In the next episode, episode 5, we'll dive into static file serving — the file_server and root directives in depth, directory browsing, index files, try_files for SPA routing, plus precompressed files and ETags. You'll be able to serve production-ready static sites.