The name contains the word redirect, so it gets pasted into a location block when a backend behind the VPN cannot be reached. Nothing changes, and the obvious conclusion is that the directive is broken or needs different syntax. What happened is simpler and less flattering: proxy_redirect operates on a response that has already come back, and in this deployment no request ever got that far.

Two directives, two moments in one request

An HTTP request through a reverse proxy passes through several decisions in order, and the two directives live at opposite ends of that sequence. One chooses where the request goes. The other edits certain headers in the answer that comes back.

DirectiveWhen it actsWhat it can change
proxy_passBefore any bytes leave the proxyWhich upstream receives the request
proxy_redirectAfter the upstream respondsMatching Location and Refresh text

That ordering is the whole answer. If the second row never executes because the first one failed, the second directive is not misconfigured, it is simply unreachable. No amount of rewriting the second line will create the response it was meant to edit. Diagnose from the first row outward, and the confusing behaviour resolves into a missing route or a refused connection.

Selecting the upstream belongs to proxy_pass

Suppose NGINX runs on a public entry host and can reach an application at 10.40.0.20:8080 over its VPN. The fragment that chooses that application looks like this, inside an existing server block:

location / {
    proxy_pass http://10.40.0.20:8080;
}

Use the real scheme, address and port, and treat this as a fragment rather than a deployment. The entry host also has to hold the VPN route, a permitted forwarding path and working return traffic to that service, because a proxy that can reach an address on paper still needs the kernel to agree.

The proxy module reference keeps upstream selection separate from response-header rewriting, and it notes that a proxy_pass value containing a URI changes how the matching location prefix is replaced. Adding or removing path components without checking the application expected URL is a reliable way to produce a 404 that looks like a routing failure.

proxy_redirect fixes answers that leak a private address

Once traffic flows, a backend may answer with a Location header naming its private address. A browser following that header leaves the public entry point and tries to reach something it cannot resolve. A deliberately configured response rewrite maps the private URL onto the public one:

proxy_redirect http://10.40.0.20:8080/ https://app.example.com/;

Replace both sides with real values. Understand the narrow scope as well: matching Location and Refresh header text gets rewritten, and URLs embedded in HTML, JavaScript or downloaded files do not.

If an application builds absolute links everywhere, the durable fix is its own external-URL configuration, not a growing list of rewrite rules on the proxy. Adding rules until the output looks right produces a configuration that fails on the first page nobody tested. One correct application setting replaces the whole collection.

What a header rewrite cannot repair

Several adjacent problems look like redirect problems and are not. The Host header sent upstream may need to match the backend virtual host. A TLS upstream may need a specific server name plus certificate verification, and rewriting a response header does nothing for either.

Cookie scope and domain attributes belong to the application and its configuration. A proxy can drop or rewrite headers all day and still produce a session that fails on the second request, because the cookie was issued for a host the browser never visited. That failure looks intermittent, which is why it survives so long in a ticket queue. Reading the cookie domain usually ends the investigation in a minute.

Keep authentication and backend selection constrained to the intended application as well. A public reverse proxy should expose one application through the server VPN rather than becoming a general-purpose gateway to every private address that host can reach. A single generic location that forwards anything the caller asks for turns one exposed service into a map of your private network. Name the upstream explicitly and keep it there.

Test in the order the request travels

Confirm three things in sequence: NGINX selected the intended location, it completed a request to the backend, and the browser followed any later redirect and loaded the sub-resources. Each step has its own log lines, and reading them in order turns a vague 502 into a specific finding.

The distinction you need is between a failed upstream connection and a browser sent to an unreachable private name. The first appears in the proxy error log; the second appears as a request that never arrives, with the browser complaining about a host it cannot resolve. Two different problems, two different files, and only one of them involves proxy_redirect at all.

Ingingo zihuye