Caddy server recipes

Unless indicated otherwise, these recipes are for the Caddy web server version 2.


Multiple sites in separate files (Caddyfile.d)

Create a Caddyfile consisting of the following single line:

import Caddyfile.d/*.conf

Create the directory Caddyfile.d/ next to the Caddyfile. Put your site configurations in Caddyfiles with the extension .conf in Caddyfile.d/. Note that with this setup one broken Caddyfile will prevent your server from starting.

Overriding an error status code

Use the respond or the file_server directive in the error handler.

handle_errors {
	file_server {
		status 200
	rewrite * /templates/index.html

Redirecting a query parameter to a path

This will only work with a single query parameter. The example is something useful for the Fossil SCM wiki. It prevents search engines and users from accessing your pages as both /wiki?name=foo and /wiki/foo. (The latter seems more search engine-friendly.)

@wiki_query {
	path_regexp ^/wiki(?:/*|)$
	query name=*
route @wiki_query {
	uri replace name= /
	redir * /wiki{query}

Reverse proxy for…

Everything except some static files

root * /opt/foo/static
@not_static {
	not {
		path /BingSiteAuth.xml /robots.txt
		path /favicon.ico
reverse_proxy @not_static localhost:8100

Generic non-PHP FastCGI

reverse_proxy /path* {
	to localhost:9000
	transport fastcgi


You can use the unofficial third-party scgi-transport module.

reverse_proxy /path* {
	to localhost:9999
	transport scgi


See the Glowing Bear wiki page “Proxying WeeChat relay with a web server”. {
	reverse_proxy /weechat localhost:9001

Running CGI scripts

Caddy can run CGI script either with a plugin or with a FastCGI-to-CGI proxy. See my caddy-cgi repository.

Serving Markdown files as pages

You can serve Markdown files as adequately good-looking minimal web pages. See my caddy-markdown-site repository.

Showing an error page on errors

This will give your server a human-readable, if not the most user-friendly, error page. It will tell the user an error has occurred and give them something to report to you. The page will contain a custom error message for the most common errors: 403, 404, 500. Otherwise the error message will be the description text of the HTTP status code.


handle_errors {
	rewrite * /error.html



<!DOCTYPE html>
{{- $code := placeholder "http.error.status_code" -}}
{{- $text := placeholder "http.error.status_text" }}
<html lang="en">

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>{{ $code }} {{ $text }}</title>

    <link rel="icon" href="/favicon.ico" type="image/x-icon">

    <h1>Error {{ $code }}</h1>

    {{ if eq $code "403" -}}
    <p>You don't have permission to access this resource.</p>
    {{- else if eq $code "404" -}}
    <p>The requested URL was not found on this server.</p>
    {{- else if eq $code "500" -}}
    <p>An internal server error has occurred.</p>
    {{- else -}}
    <p>{{ $text }}.</p>
    {{- end}}

        <li><a href="/">Home</a></li>
        <li><a href="#" onclick="javascript:history.back(); return false;">Back</a></li>
        <li>{{- /* The `href` does not preserve the fragment identifier; only the JavaScript does. */ -}}<a href="{{ .OriginalReq.URL }}" onclick="javascript:window.location.reload(); return false;">Reload</a></li>