Static files served for django runserver but not for gunicorn

1. The problem I’m having:

Hello all,
First question, so I’ll try to follow the template as good as I can. I tried other posts but none of them fixed this.
I had three different domains for the same django app (served by manage.py runserver) and static files were serving without any problems. Now I put one of those domains to serve that app with gunicorn (to keep it for testing, but it uses same static files folder) and static files get a 403 error.

So this works (manage.py runserver is using port 7000):

some-large-temporary-domain.com,
x.mydomain.com,
y.mydomain.com {
	root * /home/user/www/webapp/assets/
	file_server
	reverse_proxy localhost:7000
}

And this doesn’t (gunicorn binds to port 7001 and manage.py runserver still in 7000):

some-large-temporary-domain.com {
	handle_path /static/* {
		root * /home/user/www/webapp/assets/
		file_server		
	}
	reverse_proxy localhost:7001
}
x.mydomain.com,
y.mydomain.com {
	root * /home/user/www/webapp/assets/
	file_server
	reverse_proxy localhost:7000
}

At first I tried:

some-large-temporary-domain.com {
	root * /home/user/www/webapp/assets/
	file_server
	reverse_proxy localhost:7001
}
x.mydomain.com,
y.mydomain.com {
	root * /home/user/www/webapp/assets/
	file_server
	reverse_proxy localhost:7000
}

but then I got 404 despite 403.
I tried as well to keep only gunicorn and stop manage.py runserver

some-large-temporary-domain.com,
x.mydomain.com,
y.mydomain.com {
	handle_path /static/* {
		root * /home/user/www/webapp/assets/
		file_server		
	}
	reverse_proxy localhost:7001
}

But then every domain looses static files. putting root * /home/user/www/webapp/assets/ or root /home/user/www/webapp/assets/ ends with the same result.
I really don’t understand why it’s different with gunicorn as static files are supposed to be served directly by caddy.

Thank you.

2. Error messages and/or full log output:

For example an image, but with css and js files are the same:

GET
https://some-large-temporary-domain.com/static/images/logo.png
[HTTP/2 403  40ms]

3. Caddy version:

v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=

4. How I installed and ran Caddy:

sudo apt install caddy
sudo systemctl enable caddy

a. System environment:

Linux linuxbox 6.5.0-1021-azure #22~22.04.1-Ubuntu

b. Command:

sudo systemctl start/restart caddy

c. Service/unit/compose file:

[Unit]
Description=Gunicorn instance to serve Mastedoc webapp
After=network.target

[Service]
User=user
Group=user
WorkingDirectory=/home/user/www/webapp
Environment="PATH=/home/user/.pyenv/versions/webapp/bin"
ExecStart=/home/user/.pyenv/versions/webapp/bin/gunicorn --workers 3 --bind 0.0.0.0:7001 webapp.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=True

[Install]
WantedBy=multi-user.target

d. My complete Caddy config:

Right now it’s like this:

some-large-temporary-domain.com {
	handle_path /static/* {
		root * /home/user/www/webapp/assets/
		file_server		
	}
	reverse_proxy localhost:7001
}
x.mydomain.com,
y.mydomain.com {
	root * /home/user/www/webapp/assets/
	file_server
	reverse_proxy localhost:7000
}

What a noob! solved by chmod -R 755 to my user home. Shame on me :frowning:

That’s the wrong solution. You should not change your user’s home directory permissions.

Move your site files to somewhere like /srv or /var/www instead, and make sure the files are owned or group-owned by the caddy user (which Caddy runs as).

Also, it doesn’t make sense to have both file_server and reverse_proxy in the same site block without a matcher. Using handle is better, allowing you to split up the traffic between the two if needed. You need to tell Caddy which requests get handled one way vs the other, other wise Caddy will only ever use the directive that’s higher on the directive order, so in this case reverse_proxy will always shadow/cover file_server.

1 Like

Thank you so much for your answer, you’re right indeed.
I put it in /var/www/webapp and webapp owner is caddy:customgroup where I’m also in customgroup and permissions are set to 770 but it still dosn’t load static files.
I changed Caddyfile like this

some-large-temporary-domain.com {
        handle /static/* {
            root * /var/www/webapp/assets/
            file_server		
        }
        handle {
         	reverse_proxy localhost:7001
        }
}
1 Like

If you use handle (instead of handle_path) and you make a request like /static/foo.txt then Caddy will look for /var/www/webapp/assets/static/foo.txt, i.e. appending the request path to the root. If you use handle_path instead, then it will strip away the /static part before joining the paths together.

1 Like

Yes I ended using handle_path, but it seemed in the end a permissions issue (770 prevents others to read so it didn’t work, but 775 was ok to start and when not needed to modify it anymore I changed it so now its working with 555).
The caddy configuration you recommended worked perfectly.

For clarity this is the current Caddyfile.

some-large-temporary-domain.com{
	handle_path /static/* {
		root /var/www/webapp/assets/
		file_server		
	}
	handle {
		reverse_proxy localhost:8000
	}
}

Good work with Caddy! very neat piece of software :slight_smile:
Thanks

1 Like