So You Want to Write a Caddyfile

Say you’re an aspiring blogger. You would like to conquer the World of blogging with you new blog. You prefer your blog to be self-hosted so you can maintain, upgrade, and customize however you like. You want something simple that does not lead to you pulling your hair out of the roots. You pick Caddy as your web server for obvious reasons. You are in luck, because this wiki post is for you!

Baby Steps

You start with your development environment being your own computer. The host name is localhost. Create a new file named Caddyfile in an empty directory. Open the file in your favorite editor to add the configuration. Your very first and simple Caddyfile is:


The localhost here is your website address, aka the host, aka site label. Start a new terminal session in your favorite terminal emulator (e.g. iTerm, Powershell, CMD, konsole, etc.), navigate to the directory where your Caddyfile is, and run:

$ caddy start

You will then see logs like:

2020/05/16 19:25:59.385 INFO    using adjacent Caddyfile
2020/05/16 19:25:59.394 INFO    admin   admin endpoint started  {"address": "tcp/localhost:2019", "enforce_origin": false, "origins": ["localhost:2019", "[::1]:2019", ""]}
2020/05/16 22:25:59 [INFO][cache:0xc0005fc960] Started certificate maintenance routine
2020/05/16 19:25:59.399 INFO    http    server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS {"server_name": "srv0", "https_port": 443}
2020/05/16 19:25:59.399 INFO    http    enabling automatic HTTP->HTTPS redirects        {"server_name": "srv0"}
2020/05/16 19:25:59.411 INFO    tls     setting internal issuer for automation policy that has only internal subjects but no issuer configured  {"subjects": ["localhost"]}

Caddy might ask you for your password to install local CA (Certificate Authority) into your computer’s trust store. Why? Your production environment will have HTTPS. Caddy tries to reduce the friction of change between your development environment and production environment, and one of those is the change of the scheme. So Caddy creates a local certificate authority that’s only valid for a week, auto-renews it, and generates certificates to be used in localhost environment. If you inspect the log lines above you will also see Caddy has set up automatic HTTP->HTTPS redirect. This is because the site label does not explicitly limit it to plain HTTP, so Caddy will assume it is to be served on HTTPS and acts accordingly.

Great, so the web server is running and serving https://localhost. Open you favorite browser, type localhost in the address bar, and hit Enter (Firefox might display a warning because the certificate is self-signed; click Advanced then click Accept the Risk and Continue). You will see blank white page. This is because Caddy has not been told to serve anything, so Caddy writes an empty response. You need an index for your blog, so create an index.html file next to the Caddyfile. Now you need the web server to serve files. Edit the Caddyfile to look like this:



$ caddy reload 

The command reload tells Caddy to re-read its configuration. Switch to the browser and navigate to https://localhost. Voila! You should now see the content of your index.html file rendered by your browser. What just happened?

The file_server directive tells Caddy to serve files described by the path in the address bar of your browser relative to a root path. The root value is assumed to be the current working directory if it is never set in the Caddyfile for the website. Stepping back, so Caddy receives a request asking for a file on some path relative to root, and it goes looking for the file at the designated path. Caddy received a bare path, so it looks for index.html within the directory described by the bare path to serve. Now to create a blog post titled On the Way to Caddy, create a directory with the name as you’d like to see it in the part after the slash following your website host. So if the desired blog post path is https://localhost/on-the-way-to-caddy, the directory name must be on-the-way-to-caddy, and create an index.html within the directory and fill it with whatever your heart desires. The eventual directory tree of your working directory becomes:

├── Caddyfile
├── index.html
└── on-the-way-to-caddy
    └── index.html

Without restarting your web server, navigate to https://localhost/on-the-way-to-caddy. Voila! Now add the <a href="/on-the-way-to-caddy">On the Way to Caddy</a> on your homepage to link to it. You can keep adding more directories and more index.html inside them for each blog post, then link to them in the index.html at the root directory.


You are probably tired from having to change your index.html at the root of your website everytime you create a new post just so you can add that one more link to the directory. Look no further than templates! The templates directive tells Caddy to treat the HTML files as Go’s text/template files. In addition to the standard funcs of Go’s text/template, Caddy includes all of Sprig funcs plus some of Caddy’s own funcs listed the documentation page of the templates module. Change the index.html file by including the following simplistic snippet somewhere inside it:

{{range $index, $element := listFiles "/"}}
		{{ if and (ne $element "Caddyfile") (ne $element "index.html") }}
				<a href="/{{$element}}">{{$element}}</a>

This snippet uses one of Caddy’s own funcs that lists the files in the specified directory (relative to root). It ranges over the files, excludes the files named Caddyfile or index.html, and prints out HTML anchor tags <a href="/{{$element}}">{{$element}}</a> where $element is the name of the directory (if you don’t understand what this implies, refer to the Baby Steps section). Change your Caddyfile to be:


then reload the configuration and refresh your browser. The template will add more links as you add more directories. However, the links now look like this on-the-way-to-caddy. The href should have this format, but the text should be On the Way to Caddy. Go’s text/template and Sprig funcs can help in transforming on-the-way-to-caddy to On the Way to Caddy. Sprig has 2 funcs that can help: replace, and title. Change the <a href...> line in the template index.html file to:

<a href="/{{$element}}">{{$element | replace "-" " " | title}}</a>

then refresh the page. The links on the page should now look like this On The Way To Caddy with the target link https://localhost/on-the-way-to-caddy. The page will automatically include the links as you add directories representing blog posts.

Your site is now ready to move to production, what should you do? Well, first you should run caddy stop to stop the server running in your development environment.

1- Change the Caddyfile to this:

root /path/to/where/your/first/index/file/at/root/of/your/website/directory

2- Checkout the Install page on Caddy’s documentation website.


Hello Mohammed,

Thank you for this, I will reuse some of it here :
(it’s just notes mostly for me but I can structure it for this wiki)

1 Like