How to Fix Nginx Proxy Pass Losing Port Number in Redirects


2 views

When working with Nginx as a reverse proxy for Node.js applications, a common issue arises with HTTP redirects losing their port numbers. Here's what typically happens:

// Original request
GET http://localhost:8080/app/login

// Node.js Express response
res.redirect('/app/dashboard');

// Actual redirect goes to (missing port 8080)
Location: http://localhost/app/dashboard

The issue stems from how Nginx handles the proxy_redirect directive and the Host header. By default, Nginx doesn't preserve the original port number in redirect responses from the proxied server.[ux]Nginx Proxy Pass Redirects Losing Port Number: Fix for Express.js and Node.js Applications[/ux]

When configuring Nginx as a reverse proxy for Node.js applications, developers often encounter an issue where redirects lose the original port number. The problem typically manifests when:

  • Your Express.js app runs on a non-standard port (e.g., 3000)
  • Nginx listens on a different port (e.g., 8080)
  • Express sends a redirect response (res.redirect('/app'))

The core issue stems from how Nginx handles the Host header and the proxy_redirect directive. By default, when you have:

proxy_pass http://localhost:3000;
proxy_redirect off;
proxy_set_header Host $host;

Nginx will:

  1. Strip the original port from redirect responses
  2. Use only the domain part from the Host header
  3. Not preserve the original port in Location headers

Here's the proper Nginx configuration that preserves ports in redirects:

location /app {
  rewrite /app/(.*) /$1 break;
  proxy_pass http://localhost:3000;
  
  # Critical for port preservation
  proxy_set_header Host $host:$server_port;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-Forwarded-Port $server_port;
  
  # Handle redirects properly
  proxy_redirect http://localhost:3000/ http://$host:$server_port/app/;
  proxy_redirect http://localhost/ http://$host:$server_port/app/;
}

For optimal results, configure your Express app to understand it's behind a proxy:

const express = require('express');
const app = express();

// Trust the X-Forwarded headers
app.enable('trust proxy');

app.get('/login', (req, res) => {
  // This will now redirect properly with port
  res.redirect('/app/dashboard');
});

app.listen(3000);

Verify the solution works with these test cases:

  1. Access http://localhost:8080/app/login - should redirect to http://localhost:8080/app/dashboard
  2. Check response headers with curl -v http://localhost:8080/app/login
  3. Test with different ports (80, 443, 8080, etc.)

If you're still facing issues, consider these alternatives:

# Option 1: Use absolute URLs in Express
res.redirect('http://' + req.get('host') + '/app/dashboard');

# Option 2: Modify Nginx rewrite rules
location /app {
  proxy_pass http://localhost:3000/;  # Note trailing slash
  proxy_set_header Host $host:$server_port;
  proxy_redirect ~^(http://[^:]+)(?::d+)?/(.*)$ http://$1:$server_port/app/$2;
}

When setting up Nginx as a reverse proxy for Node.js applications, a common issue occurs with HTTP redirects losing their port numbers. Consider this typical Nginx configuration:

location /app {
  rewrite /app/(.*) /$1 break;
  proxy_pass http://localhost:3000;
  proxy_redirect off;
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

While this configuration works for regular requests, redirects from your Express application will drop the port number. For example, a redirect to '/app' from Node.js might send users to http://localhost/app instead of http://localhost:8080/app.

The root cause lies in how Nginx handles the Host header and redirect responses. When proxy_redirect is set to off, Nginx doesn't modify the Location header in redirect responses. Meanwhile, Express generates redirects based on the Host header it receives.

Here's the proper configuration that preserves port numbers in redirects:

location /app {
  rewrite /app/(.*) /$1 break;
  proxy_pass http://localhost:3000;
  proxy_redirect default;
  proxy_set_header Host $host:$server_port;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-Forwarded-Port $server_port;
}

The solution involves several important changes:

1. proxy_redirect default - Allows Nginx to modify Location headers
2. Host $host:$server_port - Includes the port in the Host header
3. Additional X-Forwarded headers - Provides complete request information

For Express applications, you should configure the trust proxy setting:

const express = require('express');
const app = express();

// Trust the X-Forwarded headers from Nginx
app.set('trust proxy', true);

// Your routes and middleware here
app.get('/redirect', (req, res) => {
  res.redirect('/app');
});

After implementing these changes:

  1. Requests to http://localhost:8080/app/redirect will properly redirect to http://localhost:8080/app
  2. The port number will be preserved in all redirect responses
  3. Your application will have complete information about the original request

For more complex setups with SSL termination or multiple proxies:

proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;

These additional headers ensure your application has all necessary information to generate correct URLs, even in complex deployment scenarios.