File server root in user's home directory causes permission errors

1. The problem I’m having:

I am trying to have a static site with the root in a directory in a user’s home directory. Caddyfile:

joe.in {
  root * /home/joe/www/blog
  encode zstd gzip
  file_server
}

I get permission errors connected to opening /home/joe/www/blog. All subdirectories in blog have permission 775. All files have permission 664.

When I copy ‘blog’ to /var/www with the Caddyfile:

joe.in {
  root * /var/www/blog
  encode zstd gzip
  file_server
}

it works. /var/www and all subdirectories are owned by root. As I seem to have read elsewhere I didn’t have to change the ownership of /var/www to ‘caddy’.

2. Error messages and/or full log output:

Nov 14 16:51:05 joe-basic caddy[779]: {"level":"debug","ts":1731583265.296464,"logger":"http.log.error","msg":"open /home/joe/www/blog: permission denied","request":{"remote_ip":"103.203.72.136","remote_port":"27446","client_ip":"103.203.72.136","proto":"HTTP/3.0","method":"GET","host":"joe.in","uri":"/","headers":{"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"],"Alt-Used":["joe.in"],"Sec-Fetch-Site":["none"],"User-Agent":["Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:132.0) Gecko/20100101 Firefox/132.0"],"Sec-Fetch-Dest":["document"],"Sec-Fetch-Mode":["navigate"],"Accept-Language":["en-US,en;q=0.5"],"Upgrade-Insecure-Requests":["1"],"Sec-Fetch-User":["?1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Priority":["u=0, i"]},"tls":{"resumed":true,"version":772,"cipher_suite":4865,"proto":"h3","server_name":"joe.in"}},"duration":0.000533153,"status":403,"err_id":"fxjtazwgb","err_trace":"fileserver.(*FileServer).ServeHTTP (staticfiles.go:283)"}

3. Caddy version:

v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=

4. How I installed and ran Caddy:

Used standard installation for Debian, Ubuntu, Raspbian as given in the docs.

a. System environment:

DigitalOcean droplet running Ubuntu 24.10.

b. Command:

Caddy is running as a systemd service after the installation.

c. Service/unit/compose file:

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

d. My complete Caddy config:

This gives permission errors:

joe.in {
  root * /home/joe/www/blog
  encode zstd gzip
  file_server
}

This does not give any errors( it works):

joe.in {
  root * /var/www/blog
  encode zstd gzip
  file_server
}

5. Other Info

Caddyfile is /etc/caddy/Caddyfile owned by root. /var/www is owned by root and everything works. All directories in ‘blog’ are 775 and files are 664.

I did get an answer from the DigitalOcean community that said that Caddy has access to the /var/www directory though it is owned by root. I have not yet understood why but apparently it is due to some special properties of that directory.
And if I wanted to have the static site in a user’s home directory I would have to do a sudo chown -R caddy:caddy on that site.

This is not true.

The reason you can’t use the user’s home directory is because the standard systemd unit file shipped by Caddy is hardened and prohibits access to users’ home directories. This protects against accidental leak of user data. The /var tree is special by systemd when the protection we’re using is activated.

1 Like

Thanks for joining the conversation. Are you referring to the ‘ProtectHome’ entry in the service file? I don’t have it in mine.

The reason is because the /home/joe directory itself does not have x permission for all (and it shouldn’t), so it cannot be navigated to. For a file to be visible, every parent directory to the target must have x permission for the user attempting to see the file. Caddy runs as the caddy user, which won’t have those permissions.

You should use /srv or /var/www for your site files, not your user’s home.

1 Like

Thank you. I have seen your replies to similar questions in this forum. I am still not clear how Caddy can access files in /var/www though it is owned by root.

It depends on the permission bits. It can be owned by root, but allow read/execute from group/world. Use ls -la to see the permissions for files.

2 Likes

Thanks. /var has x permission for all(others). And /home/joe does not have x permission for all(others). I know you have said this already but it registered with me only now. I looked only at the permission of files and directories inside the home directory ‘joe’ without realizing that ‘joe’ itself does not have the ‘x’ permission. Thanks once again.

1 Like