Configure Caddyfile for Django static files

1. The problem I’m having:

Hello,
I’ve been trying to deploy a Django server on a Raspberry Pi 4 and I’m having trouble with configuring the Caddyfile to serve the static files (or maybe I haven’t configured Django correctly?). I’ve run out of ideas how to debug this situation.
Sorry if this is a duplicate, I’ve seen almost every thread with the same problem but still couldn’t manage to make it work for me.

Server is running, but all images are missing and also .css file is missing.
One thing that I’m sure is that permissions are correct. The project folder ‘FORT-Production-Plus’ has the owner ‘adminfort’ (my user), and ‘www-data’ the Group. Caddy is running with the caddy user and it is added in the ‘www-data’ group.

Another problem is that https is not working, I tried running the server on “fort.local” but https is still missing.

2. Error messages and/or full log output:

Nov 01 20:21:38 fort systemd[1]: Starting caddy.service - Caddy...
Nov 01 20:21:38 fort caddy[1022]: caddy.HomeDir=/var/lib/caddy
Nov 01 20:21:38 fort caddy[1022]: caddy.AppDataDir=/var/lib/caddy/.local/share/caddy
Nov 01 20:21:38 fort caddy[1022]: caddy.AppConfigDir=/var/lib/caddy/.config/caddy
Nov 01 20:21:38 fort caddy[1022]: caddy.ConfigAutosavePath=/var/lib/caddy/.config/caddy/autosave.json
Nov 01 20:21:38 fort caddy[1022]: caddy.Version=v2.7.5 h1:HoysvZkLcN2xJExEepaFHK92Qgs7xAiCFydN5x5Hs6Q=
Nov 01 20:21:38 fort caddy[1022]: runtime.GOOS=linux
Nov 01 20:21:38 fort caddy[1022]: runtime.GOARCH=arm64
Nov 01 20:21:38 fort caddy[1022]: runtime.Compiler=gc
Nov 01 20:21:38 fort caddy[1022]: runtime.NumCPU=4
Nov 01 20:21:38 fort caddy[1022]: runtime.GOMAXPROCS=4
Nov 01 20:21:38 fort caddy[1022]: runtime.Version=go1.21.3
Nov 01 20:21:38 fort caddy[1022]: os.Getwd=/
Nov 01 20:21:38 fort caddy[1022]: LANG=en_GB.UTF-8
Nov 01 20:21:38 fort caddy[1022]: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Nov 01 20:21:38 fort caddy[1022]: NOTIFY_SOCKET=/run/systemd/notify
Nov 01 20:21:38 fort caddy[1022]: HOME=/var/lib/caddy
Nov 01 20:21:38 fort caddy[1022]: LOGNAME=caddy
Nov 01 20:21:38 fort caddy[1022]: USER=caddy
Nov 01 20:21:38 fort caddy[1022]: INVOCATION_ID=e14303ac4e314847be0950807f882740
Nov 01 20:21:38 fort caddy[1022]: JOURNAL_STREAM=8:16853
Nov 01 20:21:38 fort caddy[1022]: SYSTEMD_EXEC_PID=1022
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.1953335,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":""}
Nov 01 20:21:38 fort caddy[1022]: {"level":"warn","ts":1698862898.1987553,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":10}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.2013333,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.201698,"logger":"http.auto_https","msg":"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}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.2017465,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.2021184,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0x40002d4580"}
Nov 01 20:21:38 fort caddy[1022]: {"level":"debug","ts":1698862898.2027724,"logger":"http.auto_https","msg":"adjusted config","tls":{"automation":{"policies":[{"subjects":["192.168.3.105"]},{}]}},"http":{"servers":{"remaining_auto_https_redirects":{"listen":[":80"],"routes":[{},{}]},"srv0":{"listen":[":443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"group":"group3","handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"rewrite","strip_path_prefix":"/static"}]},{"handle":[{"handler":"vars","root":"/home/adminfort/FORT-Production-Plus/staticfiles/"},{"handler":"file_server","hide":["/etc/caddy/Caddyfile"]}]}]}],"match":[{"path":["/static/*"]}]},{"group":"group3","handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"rewrite","strip_path_prefix":"/media"}]},{"handle":[{"handler":"vars","root":"/home/adminfort/FORT-Production-Plus/media/"},{"handler":"file_server","hide":["/etc/caddy/Caddyfile"]}]}]}],"match":[{"path":["/media/*"]}]},{"group":"group3","handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"reverse_proxy","upstreams":[{"dial":"unix//run/gunicorn.sock"}]}],"match":[{"not":[{"path":["/static/*","/media/*"]}]}]}]}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{}}}}}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.2044845,"logger":"tls","msg":"cleaning storage unit","description":"FileStorage:/var/lib/caddy/.local/share/caddy"}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.208088,"logger":"tls","msg":"finished cleaning storage units"}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.2500734,"logger":"pki.ca.local","msg":"root certificate is already trusted by system","path":"storage:pki/authorities/local/root.crt"}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.2510543,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
Nov 01 20:21:38 fort caddy[1022]: {"level":"debug","ts":1698862898.2516968,"logger":"http","msg":"starting server loop","address":"[::]:443","tls":true,"http3":true}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.251977,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
Nov 01 20:21:38 fort caddy[1022]: {"level":"debug","ts":1698862898.2522757,"logger":"http","msg":"starting server loop","address":"[::]:80","tls":false,"http3":false}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.252457,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.252609,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["192.168.3.105"]}
Nov 01 20:21:38 fort caddy[1022]: {"level":"warn","ts":1698862898.2536309,"logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [192.168.3.105]: no OCSP server specified in certificate","identifiers":["192.168.3.105"]}
Nov 01 20:21:38 fort caddy[1022]: {"level":"debug","ts":1698862898.253922,"logger":"tls.cache","msg":"added certificate to cache","subjects":["192.168.3.105"],"expiration":1698897780,"managed":true,"issuer_key":"local","hash":"79f17581122deaeab2bef3989a512badfcf64767979af48af352485c4e0cf66c","cache_size":1,"cache_capacity":10000}
Nov 01 20:21:38 fort caddy[1022]: {"level":"debug","ts":1698862898.2541237,"logger":"events","msg":"event","name":"cached_managed_cert","id":"9cc5b682-7edb-4214-a8a6-fed3655df65a","origin":"tls","data":{"sans":["192.168.3.105"]}}
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.2548063,"msg":"autosaved config (load with --resume flag)","file":"/var/lib/caddy/.config/caddy/autosave.json"}
Nov 01 20:21:38 fort systemd[1]: Started caddy.service - Caddy.
Nov 01 20:21:38 fort caddy[1022]: {"level":"info","ts":1698862898.2576544,"msg":"serving initial configuration"}

3. Caddy version:

v2.7.5

4. How I installed and ran Caddy:

I run the commands from the official website:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

a. System environment:

Raspberry Pi OS Lite (64bit)

PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

b. Command:

Systemctl service.
I just restart it when necesarry

sudo systemctl restart caddy

d. My complete Caddy config:

# The Caddyfile is an easy way to configure your Caddy web server.
#
# Unless the file starts with a global options block, the first
# uncommented line is always the address of your site.
#
# To use your own domain name (with automatic HTTPS), first make
# sure your domain's A/AAAA DNS records are properly pointed to
# this machine's public IP, then replace ":80" below with your
# domain name.
{
        debug
}

192.168.3.105 {
        # Set this path to your site's directory.
        handle_path /static/* {
                root * /home/adminfort/FORT-Production-Plus/staticfiles/
                file_server
        }

        handle_path /media/* {
                root * /home/adminfort/FORT-Production-Plus/media/
                file_server
        }

        @notStatic {
                not path /static/* /media/*
        }

        handle {
                reverse_proxy @notStatic unix//run/gunicorn.sock
        }
}

#forty.localhost {
#       root * /home/adminfort/FORT-Production-Plus
#       proxy / localhost:8000 {
#               transparent
#       }
#}

5. Links to relevant resources:

I used this caddyfile as base for mine: TIL: Using Caddy with Django apps instead of Nginx – Reads, Takes and Links
Also updated it since I found on more sites to use a directive like @notStatic to avoid passing static through reverse proxy.

Also here is a piece of the Django config responsible for the static files:

STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

You don’t need this @notStatic matcher because it’s already covered by your handle blocks which are mutually exclusive. You can simplify it to just this:

        handle {
                reverse_proxy unix//run/gunicorn.sock
        }

What do you see when you make requests with curl -v for those files? Show your Caddy logs that are produced after making a request (with debug mode on).

Oh right, the problem is probably that you have the files in /home. You should have your files in /srv or /var/www/html typically.

The way Linux permissions work is that every directory parent to the one you want to access must have the x (executable) permission set to be traversable, and /home/adminfort will not have executable set for the caddy user (because they aren’t the same user, don’t share a group, and it’s not executable for “world” either to protect from snooping in multi-user systems).

1 Like

This worked! I moved the project into /var/www/ and had to reinstall venv and now everything works! Thanks!

Any idea how to get https working on local network? I plan to use this only on the local network.

You’ll need to set tls internal in your config, then you’ll need to install Caddy’s root CA cert on all devices that need to connect to your Caddy server.

Or use a real domain with the DNS challenge if your server isn’t publicly accessible, and you’ll be able to get a publicly trusted cert that won’t require installing a CA cert on each device.