Rewriting URI breaks the reverse proxied page

1. Output of caddy version:

2.5.2 (caddy:2.5.2-builder)

2. How I run Caddy:

caddy run --config caddy.json

a. System environment:

Docker (caddy:2.5.2-builder)

b. Command:

caddy run --config caddy.json

d. My complete Caddy config:

# Putting only matcher because it's a question about the matcher and handler
{
  match: [
    {
      host: [
        "fancy-app.com"
      ],
    }
  ],
  handle: [
    {
      handler: "reverse_proxy",
      upstream: [
        {
          dial: "upstream.com:443"
        }
      ],
      transport: {
        protocol: "HTTP",
        tls: { }
      },
     headers: {
       request: {
         set: {
           Host: [
             "{http.reverse_proxy.upstream.host}"
            ],
            X-Served-For: [
             "{http.request.host}"
            ],
            X-Forwarded-Proto: [
             "https"
            ],
            X-Forwarded-Host: [
              "{http.reverse_proxy.upstream.host}"
            ]
         }   
      }
   },
   rewrite: {
     uri: "/app"
   }
  }
 ]
}

3. The problem I’m having:

I am reverse proxying to an upstream, and I cannot change how the upstream works.

On that upstream, there is a web app running at upstream.com/app.

I need to proxy from fancy-app.com to the upstream. Visitor to fancy-app.com should see the same exact thing as if they visited upstream.com/app. Also, visiting fancy-app.com/page1 should show the same exact thing as upstream.com/app/page1 etc.

The above config (matcher/handler) works, but the design of the proxied page gets broken a bit.

The reason for that is that my proxy now rewrites the path of every request to fancy-app.com to be fancy-app.com/app. And now if there’s a relative path to an asset on that page, e.g. /public/image.png my proxy will change it to fancy-app.com/app/public/image.png, browser visits that URL and there’s nothing there so the asset is not served.

5. What I already tried:

I was thinking about rewriting the response so the relative URLs, e.g. /public/image.png are replaced by absolute URLs, e.g. upstream.com/public/image.png and that way maybe I can avoid the issue.

Does anyone have a better solution?

Please update to v2.6.2 :innocent:

That’s a pretty annoying problem to have, not gonna lie :sweat_smile:
I guess, ye, you could try replacing certain strings in the response. Just be aware of false positives. But you will get those with any solution, so ehh, whatever :woman_shrugging:

Another way could be to look for certain response codes, most notably http/404 and then proxy again without rewriting.

Case 1:

  1. /page1 will get rewritten to /app/page1
  2. Proxy to upstream
  3. Send back response to client, because http code is 200, hinting the file/page was found

Case 2:

  1. /public/image.png will get rewritten to /app/public/image.png
  2. Proxy to upstream
  3. Yields a http/404, so rewrite back to /public/image.png and proxy again
  4. Send back response to client, because http code is 200, hinting the file/page was found

Have a look at docs/caddyfile/directives/reverse_proxy#handle_response. I would recommend writing that as Caddyfile and then converting it to json using caddy adapt.

Or you could try only rewriting if the path does not start with /public/* (and possibly many other paths), I guess.

2 Likes