Software development and beyond

Caddy 2 config for serving Django, FastAPI and other web apps

I decided to replace NGINX as my web server of choice with Caddy mainly to remove the hassle of managing https certificates. Overall the Caddy configuration is simpler and it took less time to figure it out.

So, if you are like me, and you need to configure a web server to serve web applications like Django or FastAPI applications, buckle up!

My configuration assumes that:

Replace { project_name } with the name of the project (identifying the project's folder) and { domain_name } identifying the web domain in all examples.

Caddy config with https on a domain

Caddy supports the ability to separate individual application/domain configurations, so we can have a main Caddyfile in /etc/caddy/Caddyfile, and { project_name }.caddy in /etc/caddy/{ project_name }.caddy.

The main Caddy file will only import all individual configurations:

import *.caddy

This is fairly straightforward. The main config will be in { project_name }.caddy:

www.{ domain_name } {
  redir https://{ domain_name }{uri}
}
{ domain_name } {
  log {
    output file /var/log/caddy/access.log
    format filter {
      wrap console
      fields {
        request>headers>Authorization delete
      }
    }
  }
  encode gzip
  header Strict-Transport-Security "max-age=31536000; preload"
  @excludeDirs {
    not path /static/* /media/*
  }
  reverse_proxy @excludeDirs unix//var/run/{ project_name }/app.sock
  file_server {
    root /var/www/{ project_name }/
  }
}

The first three lines serve as a redirect from the www subdomain to the main domain to remove duplicit addresses.

The main block is started with the domain name. The first instruction after that tells Caddy to produce an access log. We can not only pick a location but also tell Caddy not to log authorization (or other) headers. This will prevent various access tokens from appearing in the logs.

We set gzip compression with encode gzip and set a strict transport security policy. The policy will make sure that users always connect to https version of the website.

reverse_proxy configures a reverse proxy to the web socket, optionally taking @excludeDirs instructions to not point the request to the socket if it is a request to one of the static content directories static and media.

Finally, we set up a file server for serving the content that doesn't get connected to the socket.

Caddy config without https

The first change would be to turn off auto_https in the Caddyfile:

{
  auto_https off
}
import *.caddy

If we need to combine multiple projects where some do need to be served over https, then the automatic https can be enabled individually in the specific project's .caddy file.

The .caddy file for serving web applications on the port :80 without https will be almost identical, we only need to replace the main block and use the port number instead of the domain name:

:80 {
  log {
    output file /var/log/caddy/access.log
    format filter {
      wrap console
      fields {
        request>headers>Authorization delete
      }
    }
  }
  encode gzip
  @excludeDirs {
    not path /static/* /media/*
  }
  reverse_proxy @excludeDirs unix//var/run/{ project_name }/app.sock
  file_server {
    root /var/www/{ project_name }/
  }
}

And that's it! Short and sweet.

Last updated on 9.1.2023.

devops django fastapi