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:
- Strip the original port from redirect responses
- Use only the domain part from the
Host
header - 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:
- Access
http://localhost:8080/app/login
- should redirect tohttp://localhost:8080/app/dashboard
- Check response headers with
curl -v http://localhost:8080/app/login
- 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:
- Requests to
http://localhost:8080/app/redirect
will properly redirect tohttp://localhost:8080/app
- The port number will be preserved in all redirect responses
- 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.