CSS stylesheet not being loaded with reverse proxy and uri strip_prefix

1. Caddy version (caddy version):

v2.4.0-beta.1 h1:Ed/tIaN3p6z8M3pEiXWJL/T8JmCqV62FrSJCHKquW/I=
downloaded with caddy-auth-jwt and caddy-duckdns

2. How I run Caddy:

I use caddy to reverse proxy various services from my local network. I’m implementing an authentication system using caddy-auth-jwt plugin and a node.js app to handle login and token creation.

a. System environment:

Operating System: Ubuntu 20.10
Kernel: Linux 5.8.0-48-generic
Architecture: x86-64

b. Command:

sudo systemctl start caddy

c. Service/unit/compose file:

# caddy.service
#
# For using Caddy with a config file.
#
# Make sure the ExecStart and ExecReload commands are correct
# for your installation.
#
# See https://caddyserver.com/docs/install for instructions.
#
# WARNING: This service does not use the --resume flag, so if you
# use the API to make changes, they will be overwritten by the
# Caddyfile next time the service is restarted. If you intend to
# use Caddy's API to configure it, add the --resume flag to the
# caddy run command or use the caddy-api.service file instead.

[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
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

d. My complete Caddyfile or JSON config:

test.palmiotto.duckdns.org {
tls {
        issuer acme {
                disable_http_challenge
        }
        issuer zerossl {
                disable_http_challenge
        }
}

reverse_proxy localhost:3000

reverse_proxy /jellyfin/* 192.168.1.199:8096

route /auth/* {
     uri strip_prefix /auth
     reverse_proxy 192.168.1.199:5000
}

route /planes/* {
     uri strip_prefix /planes
     reverse_proxy 192.168.1.134:8754
}

handle_errors {
        rewrite * /{http.error.status_code}
        reverse_proxy https://http.cat {
                header_up Host http.cat
        }
}
}

3. The problem I’m having:

When I access /auth/login I am correctly displayed the login page of my app, running on port 5000. Static files in my app are served from a folder name public at the root of the app that has this structure:

public
├───images
├───javascripts
└───stylesheets

I wanted the app to be accessible by a specific path and not on the root of my website, so I used the route directive to remove the prefix from the uri before sending the request to the service. The strange behaviour that i’m having is that while the javascript and images for the site are correctly loaded, the stylesheet is not. I can see the log for my express path and i can see that the javascript and image request got through without any problem, the same way it would happen if I tried to access the app from the local network, however there is no trace of the stylesheet request

Normal behaviour when accessing from local network:

GET /login 304 16.533 ms - -
GET /stylesheets/style.css 304 3.289 ms - -
GET /images/red-john-WjwM9UvhVs8-unsplash.jpg 304 1.151 ms - -
GET /javascripts/login.js 200 5.480 ms - 4214

Behaviour accessing via reverse proxy:

GET /login 304 16.771 ms - -
GET /javascripts/login.js 304 2.505 ms - -
GET /images/red-john-WjwM9UvhVs8-unsplash.jpg 304 0.802 ms - -

I am aware that serving app that are written to be on root on a different path can be challenging, but since static file as javascripts and images are served i thought the problem was solved. What can I do to resolve this issue? Thanks

4. Error messages and/or full log output:

Apr 23 19:09:02 palmiottos-server systemd[1]: Starting Caddy...
Apr 23 19:09:02 palmiottos-server caddy[21062]: caddy.HomeDir=/var/lib/caddy
Apr 23 19:09:02 palmiottos-server caddy[21062]: caddy.AppDataDir=/var/lib/caddy/.local/share/caddy
Apr 23 19:09:02 palmiottos-server caddy[21062]: caddy.AppConfigDir=/var/lib/caddy/.config/caddy
Apr 23 19:09:02 palmiottos-server caddy[21062]: caddy.ConfigAutosavePath=/var/lib/caddy/.config/caddy/autosave.json
Apr 23 19:09:02 palmiottos-server caddy[21062]: caddy.Version=v2.4.0-beta.1 h1:Ed/tIaN3p6z8M3pEiXWJL/T8JmCqV62FrSJCHKquW/I=
Apr 23 19:09:02 palmiottos-server caddy[21062]: runtime.GOOS=linux
Apr 23 19:09:02 palmiottos-server caddy[21062]: runtime.GOARCH=amd64
Apr 23 19:09:02 palmiottos-server caddy[21062]: runtime.Compiler=gc
Apr 23 19:09:02 palmiottos-server caddy[21062]: runtime.NumCPU=4
Apr 23 19:09:02 palmiottos-server caddy[21062]: runtime.GOMAXPROCS=4
Apr 23 19:09:02 palmiottos-server caddy[21062]: runtime.Version=go1.16
Apr 23 19:09:02 palmiottos-server caddy[21062]: os.Getwd=/
Apr 23 19:09:02 palmiottos-server caddy[21062]: LANG=C.UTF-8
Apr 23 19:09:02 palmiottos-server caddy[21062]: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
Apr 23 19:09:02 palmiottos-server caddy[21062]: NOTIFY_SOCKET=/run/systemd/notify
Apr 23 19:09:02 palmiottos-server caddy[21062]: HOME=/var/lib/caddy
Apr 23 19:09:02 palmiottos-server caddy[21062]: LOGNAME=caddy
Apr 23 19:09:02 palmiottos-server caddy[21062]: USER=caddy
Apr 23 19:09:02 palmiottos-server caddy[21062]: INVOCATION_ID=77942e4a9e024248a018c4728b453d9c
Apr 23 19:09:02 palmiottos-server caddy[21062]: JOURNAL_STREAM=8:126174
Apr 23 19:09:02 palmiottos-server caddy[21062]: {"level":"info","ts":1619197742.477989,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":""}
Apr 23 19:09:02 palmiottos-server caddy[21062]: [WARNING][caddyfile] /etc/caddy/Caddyfile:2: input is not formatted with 'caddy fmt'
Apr 23 19:09:02 palmiottos-server caddy[21062]: {"level":"info","ts":1619197742.4831996,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["localhost:2019","[::1]:2019","127.0.0.1:2019"]}
Apr 23 19:09:02 palmiottos-server caddy[21062]: {"level":"info","ts":1619197742.483876,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000340c40"}
Apr 23 19:09:02 palmiottos-server caddy[21062]: {"level":"info","ts":1619197742.4839208,"logger":"http","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}
Apr 23 19:09:02 palmiottos-server caddy[21062]: {"level":"info","ts":1619197742.4839482,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
Apr 23 19:09:02 palmiottos-server caddy[21062]: {"level":"info","ts":1619197742.48528,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["test.palmiotto.duckdns.org"]}
Apr 23 19:09:02 palmiottos-server caddy[21062]: {"level":"info","ts":1619197742.486422,"logger":"tls","msg":"cleaned up storage units"}
Apr 23 19:09:02 palmiottos-server caddy[21062]: {"level":"info","ts":1619197742.5059712,"msg":"autosaved config","file":"/var/lib/caddy/.config/caddy/autosave.json"}
Apr 23 19:09:02 palmiottos-server caddy[21062]: {"level":"info","ts":1619197742.505993,"msg":"serving initial configuration"}
Apr 23 19:09:02 palmiottos-server systemd[1]: Started Caddy.
Apr 23 19:09:12 palmiottos-server caddy[21062]: {"level":"error","ts":1619197752.4540646,"logger":"http.log.error","msg":"dial tcp 127.0.0.1:3000: connect: connection refused","request":{"remote_addr":"188.116.60.224:3348","proto":"HTTP/2.0","method":"GET","host":"test.palmiotto.duckdns.org","uri":"/favicon.ico","headers":{"Accept-Encoding":["gzip, deflate, br"],"Accept-Language":["it-IT,it;q=0.9,en-GB;q=0.8,en;q=0.7,en-US;q=0.6"],"Pragma":["no-cache"],"Cache-Control":["no-cache"],"Dnt":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36"],"Accept":["image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"],"Sec-Fetch-Dest":["image"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-origin"],"Sec-Fetch-Mode":["no-cors"],"Referer":["https://test.palmiotto.duckdns.org/auth/login"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","proto_mutual":true,"server_name":"test.palmiotto.duckdns.org"}},"duration":0.000813781,"status":502,"err_id":"wybtryp0w","err_trace":"reverseproxy.statusError (reverseproxy.go:817)"}
Apr 23 19:09:28 palmiottos-server caddy[21062]: {"level":"error","ts":1619197768.4289196,"logger":"http.log.error","msg":"dial tcp 127.0.0.1:3000: connect: connection refused","request":{"remote_addr":"188.116.60.224:3348","proto":"HTTP/2.0","method":"GET","host":"test.palmiotto.duckdns.org","uri":"/favicon.ico","headers":{"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36"],"Sec-Fetch-Site":["same-origin"],"Sec-Fetch-Dest":["image"],"Referer":["https://test.palmiotto.duckdns.org/auth/login"],"Accept-Encoding":["gzip, deflate, br"],"Pragma":["no-cache"],"Dnt":["1"],"Accept":["image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"],"Sec-Fetch-Mode":["no-cors"],"Accept-Language":["it-IT,it;q=0.9,en-GB;q=0.8,en;q=0.7,en-US;q=0.6"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\""]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","proto_mutual":true,"server_name":"test.palmiotto.duckdns.org"}},"duration":0.000435132,"status":502,"err_id":"85aq3iv3b","err_trace":"reverseproxy.statusError (reverseproxy.go:817)"}

5. What I already tried:

I tried changing the link to the resource in the view file but it seems it did no effect on the ultimate resoult.
if I try to get the resource directly in the browser i get the stylesheet without problem
https://test.palmiotto.duckdns.org/auth/stylesheets/style.css
I am sorry if I haven’t done anything significant to solve the problem, it is just that I am a bit helpless and dont know where to start. I would be grateful if someone could help me.

6. Links to relevant resources:

You can replace these two lines with handle_path /auth/* { which has built-in strip_prefix logic.

That said, what you’re running into is this:

The better approach is to use a subdomain for each service instead of just one domain + subpaths.

This topic was automatically closed after 30 days. New replies are no longer allowed.