# Fix Nginx proxy_pass Not Forwarding Host Header to Backend

Your Nginx reverse proxy forwards requests to a backend application, but the application generates URLs with the wrong hostname. Links point to http://localhost:8080 instead of https://app.example.com. The issue is the Host header.

When Nginx uses proxy_pass, the default behavior depends on your configuration. If you do not explicitly set the Host header, Nginx sends the upstream server the address specified in the proxy_pass directive, not the original client Host header.

The Problem

```nginx server { server_name app.example.com;

location / { proxy_pass http://127.0.0.1:8080; } } ```

With this configuration, the backend receives:

bash
GET / HTTP/1.0
Host: 127.0.0.1:8080
Connection: close

The backend then generates redirects and links using 127.0.0.1:8080, which breaks for external clients.

The Fix

```nginx server { server_name app.example.com;

location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } ```

Each header serves a purpose: - Host: The original hostname from the client request - X-Real-IP: The actual client IP address - X-Forwarded-For: The full chain of proxy IPs - X-Forwarded-Proto: The original protocol (http or https)

Variable Options for Host

The choice of variable matters:

nginx
proxy_set_header Host $host;           # Original Host header from client
proxy_set_header Host $http_host;      # Same as $host but includes port if present
proxy_set_header Host $server_name;    # The server_name from this server block
proxy_set_header Host "app.example.com"; # Hardcoded literal value

Use $host in most cases. It contains the value from the client's Host header, or if that header is missing, the server name from the matching server block.

When proxy_pass Changes the Host Automatically

If your proxy_pass uses a variable, Nginx does not modify the Host header:

nginx
proxy_pass http://$upstream;

But if proxy_pass uses a literal address, Nginx sets the Host header to that address:

nginx
proxy_pass http://127.0.0.1:8080;

This is the source of the confusion. Many administrators assume the original Host header passes through by default, but it does not when using literal addresses.

Backend-Specific Considerations

Node.js/Express

```javascript const express = require('express'); const app = express(); app.set('trust proxy', 1);

app.get('/', (req, res) => { console.log(req.hostname); // Now shows "app.example.com" res.send(Hello from ${req.hostname}); }); ```

Django

python
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
ALLOWED_HOSTS = ['app.example.com', 'localhost']

Verifying the Fix

Create a debug endpoint in your backend:

javascript
app.get('/debug/headers', (req, res) => {
    res.json({
        host: req.headers.host,
        xRealIp: req.headers['x-real-ip'],
        xForwardedFor: req.headers['x-forwarded-for'],
        xForwardedProto: req.headers['x-forwarded-proto']
    });
});

Request https://app.example.com/debug/headers through the proxy and verify the host field shows app.example.com, not 127.0.0.1:8080.