Why do relative mod_rewrite substitutions redirect differently behind a production reverse proxy than on localhost?
0 reputation · 06 Apr 2020, 03:52 UTC
0 reputation · 06 Apr 2020, 03:52 UTC
I have a set of mod_rewrite rules in a VirtualHost on Apache httpd 2.4 that use relative substitutions, for example a pattern like ^(.*)$ /app/$1. On my local machine, where the site is served directly from its DocumentRoot, the rewritten URLs resolve exactly as expected.
In production the same configuration sits behind a reverse proxy, and the rewritten targets appear to be evaluated against a different base. Depending on the request, I see redirects pointing at the wrong path segment, and in some cases what looks like a rewrite loop. The rules themselves are identical in both environments; only the DocumentRoot layout and the proxy layer differ.
My understanding is that per-server and per-directory rewrite contexts resolve relative substitutions differently, and that RewriteBase interacts with this, but I am not sure how the proxy changes which context applies or whether I should be forcing absolute substitutions with the [R] flag or a leading scheme and host.
Which context (server, VirtualHost, directory, or .htaccess) determines how a relative substitution is resolved, and does a fronting reverse proxy alter that resolution? Should relative substitutions be avoided entirely in proxied deployments, or is setting an explicit RewriteBase sufficient to make behavior identical across environments?
35275 reputation · 06 Apr 2020, 07:33 UTC
When you use a relative substitution in a VirtualHost (e.g. RewriteRule ^(.*)$ /app/$1), Apache resolves that substitution against the request URI that the backend sees. On a local setup the backend receives the client’s original URI, so the rewrite works as expected. In production the reverse proxy (typically ProxyPass/ProxyPassReverse) rewrites the path before forwarding the request to the backend, so the backend receives a different URI. The relative substitution is then evaluated against that altered URI, producing wrong redirects or loops.
Directory, Location) context – the substitution is resolved relative to the directory or location prefix. Here RewriteBase can override the implicit base.Yes. The proxy may add a path prefix (e.g. /app) or strip one before sending the request to the backend. Because the backend sees a different initial URI, a relative substitution that was correct for the original URI will now target a different path. The proxy’s ProxyPassReverse rewrites Location headers in responses, but it does not affect the internal rewrite that the backend performs.
ProxyPass and ProxyPassReverse directives. What prefix does the proxy add to the request that reaches the backend?
/app, the backend should see that path. Either:
RewriteRule ^(.*)$ /app/$1 [L,R=302].
Directory or Location block that matches the proxy prefix and set RewriteBase /app/.
LogLevel rewrite:trace6 for the VirtualHost, make a request, and compare the logs for localhost and proxied requests. Look for the RewriteRule that is applied and the resulting Location header.
Location header contains the correct path and that no redirect loop occurs.
RewriteBase or the substitution can affect other rules. Verify that unrelated rewrites still function.
In environments where a reverse proxy modifies the path, it is safest to use absolute substitutions (starting with /) or to include the full scheme and host when a client‑visible redirect is required. If you must use a relative substitution, ensure that the backend’s request URI is identical to what the client sent, which usually means disabling path changes in the proxy.
Could you share the ProxyPass and ProxyPassReverse directives that are used in production? Knowing the exact path prefix added by the proxy will help fine‑tune the RewriteBase or substitution.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.