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:
- The web application will be listening on a unix socket
app.sock
atunix//var/run/{ project_name }/app.sock
created by an application server like gunicorn - We also want to serve static content without hitting the application server, from directories like
/var/www/{ project_name }/static/
or/var/www/{ project_name }/media/
(these two are the common Django static content directories to take care of) - We want to produce Caddy access log at
/var/log/caddy
- We want to serve the application over https with automatic certificate, but also have the option to deploy the app at a static IP address without https
- We want to utilize gzip compression
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.