Reverse_proxy and replace_response

1. Caddy version (caddy version):

2.3.0 h1:fnrqJLa3G5vfxcxmOH/+kJOcunPLhSBnjgIvjXV/QTA=

Built with xcaddy build --with github.com/caddyserver/replace-response

2. How I run Caddy:

Locally. I just run the built binary on my dev machine.

a. System environment:

macOS Big Sur

b. Command:

./caddy run -config ./Caddyfile

c. Service/unit/compose file:

None.

d. My complete Caddyfile or JSON config:

{
    debug
}

(to_staging) {
    reverse_proxy {
        to https://a-private-staging-server.com
        header_up Referer "localhost:9443" "a-private-staging-server.com"
        header_up Host "localhost:9443" "a-private-staging-server.com"
    }

    replace {
        stream
        "a-private-staging-server.com" "localhost:9443"
    }
}

https://localhost:9443 {
    tls ./proxy-cert.pem ./proxy-pkey.pem

    log {
        output stdout
    }

    route /api/* {
        import to_staging
    }

    route {
        reverse_proxy http://localhost:5000
    }
}

3. The problem I’m having:

After reverse proxying a request, I want to modify some text in the upstream response body before sending the result to the client. The logs show that the replace_response handler isn’t being invoked at all.

4. Error messages and/or full log output:

5. What I already tried:

I have tried different plugins. I have tried replacing something very simple like “html” to check that I am not making a typo in the config. I have checked the parsed config through curl -L "http://localhost:2019/config" | jq and can confirm that the handler is correctly registered.

6. Links to relevant resources:

You can change these to use this placeholder. A bit simpler:

header_up Host {http.reverse_proxy.upstream.hostport}

The problem you’re seeing is due to directive ordering. The fix is to move replace above reverse_proxy in your snippet.

The way Caddy works is via a middleware-type system. Each handler is chained together by calling the next one in the chain, until one returns without calling the next in the chain, or the last handler is reached. This is how reverse_proxy works, because it’s a “terminal” handler, i.e. it’s a handler that acts as the end of the chain. The replace handler, like encode and a few others like header (if you use the defer option) run on the way back up the chain of handlers, i.e. after having called the next handler in the chain. So if you use route to set the order and have replace after reverse_proxy, then it never gets called because reverse_proxy doesn’t call it.

This is why the docs for replace recommend setting the order for replace like this:

{
	order replace after encode
}

i.e. set its order to be after encode’s, which is another handler with the property of running on the way back up the chain. If you use the order global option, then you won’t need to wrap it in a route.

You can change these to use this placeholder. A bit simpler:

Thank you for the tip!

The problem you’re seeing is due to directive ordering. The fix is to move replace above reverse_proxy in your snippet.

I tried what you suggested, it did not work.

I have tried:

{
    debug
    order replace after encode
}

(to_staging) {
    replace {
        stream
        "a-private-staging-server.com" "localhost:9443"
    }

    reverse_proxy {
        to https://a-private-staging-server.com
        header_up Referer {http.reverse_proxy.upstream.hostport}
        header_up Host {http.reverse_proxy.upstream.hostport}
    }
}

https://localhost:9443 {
    tls ./proxy-cert.pem ./proxy-pkey.pem

    log {
        output stdout
    }

    route /api/* {
        import to_staging
    }

    route {
        reverse_proxy http://localhost:5000
    }
}

and

{
    debug
    order replace after encode
}

https://localhost:9443 {
    tls ./proxy-cert.pem ./proxy-pkey.pem

    log {
        output stdout
    }

    handle /api/* {
        replace {
            stream
            "a-private-staging-server.com" "localhost:9443"
        }

        reverse_proxy {
            to https://a-private-staging-server.com
            header_up Referer {http.reverse_proxy.upstream.hostport}
            header_up Host {http.reverse_proxy.upstream.hostport}
        }
    }

    route {
        reverse_proxy http://localhost:5000
    }
}

I even tried adding replace before and after reverse_proxy, but did not get any replacement happening.

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