SE
STACKENGINE
Zero-Trust Client IDE/ Performance

Nginx Proxy Redirect Header Matcher

nginx proxy_redirect rewrites Location headers to the wrong domain when upstream returns redirects, breaking client navigation and OAuth flows.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(1L)
AI Audit Console
> Waiting for pre-flight audit...
AirLock HYBRID KERNEL: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPECUpdated 2026

How to Fix nginx proxy_redirect Replacing the Wrong Domain in Location Headers

Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 10 mins

TL;DR

  • What broke: proxy_redirect is matching and replacing the upstream Location header with the wrong domain — either clobbering your public hostname with an internal upstream address, or vice versa, depending on directive order.
  • How to fix it: Explicitly scope proxy_redirect with the exact upstream URL as the first argument and your public-facing URL as the second, or disable it entirely and control redirects via proxy_set_header Host.
  • Use our Client-Side Sandbox above to paste your failing nginx location {} block and auto-refactor the proxy_redirect directive.

The Incident (What does the error mean?)

Your upstream app returns:

HTTP/1.1 302 Found
Location: http://internal-app:8080/dashboard

nginx is supposed to rewrite that to https://app.example.com/dashboard. Instead, clients receive a Location pointing to a mangled URL — wrong scheme, wrong host, or a partially-substituted path. The browser follows it and hits a dead endpoint.

Common raw symptom in browser devtools:

Location: http://app.example.cominternal-app:8080/dashboard
# or
Location: http://internal-app:8080/dashboard   ← rewrite never fired
# or
Location: https://wrong-vhost.internal/dashboard  ← wrong server_name matched

This silently breaks OAuth2 redirect_uri callbacks, POST-redirect-GET flows, and SAML ACS responses — all without a 5xx. Logs look clean. Users just can't log in.


The Attack Vector / Blast Radius

proxy_redirect applies globally across all location blocks if placed in the server {} context. A single misconfigured directive bleeds into every proxied endpoint on that vhost.

Cascading failure chain:

  1. OAuth provider POSTs to /callback → upstream issues 302 /dashboard → nginx mangles Location → token exchange fails → users locked out.
  2. If proxy_redirect default is active, nginx uses proxy_pass URL as the match pattern. Any upstream that returns a Location not matching that exact base URL gets silently passed through unrewritten — internal hostnames leak to clients.
  3. In multi-upstream configs (upstream {} blocks with multiple servers), the mangled hostname can expose your internal service mesh topology to end users via response headers — an unintentional information disclosure.

How to Fix It (The Solution)

Basic Fix — Explicit `proxy_redirect` mapping

 location /app/ {
     proxy_pass http://internal-app:8080/;
 
-    proxy_redirect default;
+    proxy_redirect http://internal-app:8080/ https://app.example.com/app/;
 }

default expands to proxy_redirect <proxy_pass_url> <scheme>://<host>/ using the current request's Host header — which is unreliable behind CDNs or when proxy_set_header Host is overridden downstream.

Enterprise Best Practice — Disable `proxy_redirect`, control via headers

For most production setups, the upstream app should never be issuing redirects to its own internal address. Fix the root cause: make the upstream redirect-aware via X-Forwarded-* headers, then disable nginx's rewriting entirely.

 server {
     server_name app.example.com;
 
     location /app/ {
         proxy_pass         http://internal-app:8080/;
 
+        proxy_set_header   Host              $host;
+        proxy_set_header   X-Forwarded-Proto $scheme;
+        proxy_set_header   X-Forwarded-Host  $host;
+        proxy_set_header   X-Forwarded-Port  $server_port;
 
-        proxy_redirect     default;
+        proxy_redirect     off;
     }
 }

With proxy_redirect off, nginx touches nothing. The upstream app reads X-Forwarded-Host and X-Forwarded-Proto to construct correct absolute redirect URLs itself. This is the only approach that survives blue/green deploys, canary routing, and CDN layer changes without nginx config edits.

If you cannot modify the upstream app, use an explicit map:

 location /app/ {
     proxy_pass       http://internal-app:8080/;
-    proxy_redirect   http://internal-app:8080/ /;
+    proxy_redirect   ~^http://internal-app:8080(/.*)$  https://app.example.com$1;
 }

The regex form (~ prefix) handles path variations that the literal form misses when upstream returns sub-paths not anchored to /.


💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure browser-native utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.


Prevention in CI/CD

1. nginx config linting in CI — nginx -t is not enough.

Use gixy, Yandex's static analyzer for nginx:

# In your pipeline
pip install gixy
gixy /etc/nginx/nginx.conf
# Catches: proxy_redirect misuse, SSRF vectors, header injection risks

2. Integration test for Location headers — catch this before deploy:

# Assert the Location header on a known redirect endpoint
RESPONSE=$(curl -sI -o /dev/null -w "%{redirect_url}" http://staging.app.example.com/app/login)
if [[ "$RESPONSE" != https://app.example.com/* ]]; then
  echo "FAIL: Location header domain mismatch: $RESPONSE"
  exit 1
fi

Plug this into your GitHub Actions or GitLab CI test stage. It runs in under 2 seconds and blocks any merge that breaks redirect behavior.

3. OPA policy for nginx ConfigMaps (Kubernetes):

If you're managing nginx config via ConfigMaps in-cluster, enforce via OPA Gatekeeper:

# Deny any nginx ConfigMap containing 'proxy_redirect default'
violation[{"msg": msg}] {
  input.request.object.data[_] == v
  contains(v, "proxy_redirect default")
  msg := "proxy_redirect default is banned. Use explicit URL mapping or proxy_redirect off."
}

4. Renovate/Dependabot for nginx base image pinning — upstream behavior of proxy_redirect default has shifted across nginx minor versions. Pin your Docker base image digest, not just the tag.

Further Exploration / Matrix Routing