404 when load css/js use reverse_proxy

1. Caddy version (caddy version):


2. How I run Caddy:

docker-compose up -d

a. System environment:


b. Command:

docker-compose up -d

c. Service/unit/compose file:

    image: caddy
    restart: unless-stopped
    container_name: website-server
      - 443:443
      - ca
      - home
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - vol_home:/var/opt/website/home:ro
      - vol_ca:/var/opt/ca/:ro

d. My complete Caddyfile or JSON config:

#    @nextcloud_respond {
#        header X-USER-TYPE *nextcloud*
#    }

#    uri @nextcloud_respond replace / /nc/

    @nextcloud {
        path /nc
        path /nc/*

    route @nextcloud {
        rewrite /nc /
        uri strip_prefix /nc
        reverse_proxy https://XXXXXX.net:2443
#        reverse_proxy https://XXXXXX.net:2443 {
#            header_down X-USER-TYPE *nextcloud*
#        }

    route * {
        file_server * {
            root /var/opt/website/home
            index index.html index.htm

3. The problem I’m having:

Sub folder error when use reverse_proxy!
The main html can be obtained normally but the internal css/js file path lacks prefix.
How can I fix this problem.

4. Error messages and/or full log output:

Request URL: https://XXXXXX.com/nc
Request Method: GET
Status Code: 200
Remote Address: xxx.xxx.xxx.xxx:443
Referrer Policy: strict-origin-when-cross-origin

Request URL: https://XXXXXX.com/core/img/manifest.json
Request Method: GET
Status Code: 404
Remote Address: XXX.XXX.XXX.XXX:443
Referrer Policy: no-referrer

inner direct:
Request URL: https://XXXXXX.com/nc/core/img/manifest.json
Request Method: GET
Status Code: 304
Remote Address: XXX.XXX.XXX.XXX:443
Referrer Policy: strict-origin-when-cross-origin

5. What I already tried:

I try to add a special header field to proxy respond and rewrite request path as commented but failed.
Is there an easy way to fix this problem or I must run multiple instances of caddy?

6. Links to relevant resources:


Basically, you’re running into this (as far as I understand from your post)

uri cannot rewrite responses, it only rewrites the request on the way in.

I’m not sure I understand what you’re trying to do though. Where is this file you’re trying to serve? Is it actually on disk at /var/opt/website/home/core/img/manifest.json? That’s where file_server is looking for it with your current config.

Please don’t forget to persist /data when running in Docker, especially when using Let’s Encrypt for certificates, otherwise you’re throwing away your certificates and keys every time you re-create your Caddy container. https://hub.docker.com/_/caddy/

For what it’s worth, I’d write your config (what you have so far) like this:

    redir /nc /nc/
    handle_path /nc/* {
        reverse_proxy https://XXXXXX.net:2443

    handle {
        root * /var/opt/website/home
        file_server {
            index index.html index.htm

Thanks for your reply!!!

I have two vps and domains, One is XXXXXX.com for gate, the other XXXXXX.net provides services through different port.

This Caddy is running on gate server( XXXXXX.com ) and has a static website local at /var/opt/website/home.
So that I can use https://XXXXXX.com to access home website and https://XXXXXX.com/xxx to access another server like nextcloud.

core/img/manifest.json is in XXXXXX.net. What I need is make sure the reponse from reverse_proxy also load css/js through reverse_proxy.

Applying for a CA through Let’s Encrypt is a terrible thing if you don’t have a store and waste all your opportunities in a week.Especially debug without --dry-run. I have already make a backup.

I test your solution but not work for this.

I’m not sure what you’re saying here. Yes, you’ll hit rate limits if you don’t have a volume that persists /data, like I said. If you add the volume, you won’t hit rate limits. You should use Let’s Encrypt’s ACME staging endpoint when you’re testing your setup, because it has looser rate limits (but its certificates re not trusted).

Why not just serve a subdomain like nc.example.com for nextcloud instead of serving it under a subpath? I think that would simplify things and fix your problem.