When working with Nginx as a reverse proxy, a common challenge arises when you need to preserve the original hostname of upstream servers rather than passing the upstream group name. This becomes particularly important in containerized environments where applications rely on specific hostnames for routing or configuration.
By default, Nginx passes the upstream group name in the Host header when proxying requests. For example, with this configuration:
upstream main {
server web1.local:80;
server web2.local:80;
server web3.local:80;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://main;
}
}
The Host header received by your upstream servers will be "main" rather than the individual server names (web1.local, web2.local, etc.).
The most straightforward solution is to explicitly set the Host header using Nginx variables:
location / {
proxy_pass http://main;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
If you need to pass the actual upstream server's hostname, you can use:
location / {
proxy_pass http://main;
proxy_set_header Host $proxy_host;
}
In Docker environments, you might need additional configuration to ensure proper hostname resolution:
resolver 127.0.0.11 valid=30s;
upstream main {
server web1.local:80 resolve;
server web2.local:80 resolve;
server web3.local:80 resolve;
}
Here's a complete example that preserves upstream hostnames while handling various proxy headers:
http {
resolver 127.0.0.11 valid=30s;
upstream backend {
server web1.local:80 resolve;
server web2.local:80 resolve;
server web3.local:80 resolve;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $proxy_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;
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
}
}
}
After making changes, always test your configuration:
nginx -t
systemctl reload nginx
You can verify the Host header being sent using curl:
curl -v http://example.com
When using Nginx as a reverse proxy for multiple upstream servers (especially in Docker environments), you might encounter situations where the upstream servers receive incorrect Host
header information. By default, Nginx passes the upstream group name (e.g., "main") rather than the actual server hostname (e.g., "web1.local").
Many web applications rely on the Host
header for:
- Virtual host routing
- Generating correct URLs
- Security validations
- Session management
The key is to modify the Host
header before forwarding requests to upstream servers. Here's the proper Nginx configuration:
upstream main {
server web1.local:80;
server web2.local:80;
server web3.local:80;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://main;
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;
}
}
For more complex scenarios, consider these variations:
# Option 1: Use the original request's host
proxy_set_header Host $http_host;
# Option 2: Use the upstream server name (requires Nginx Plus)
proxy_set_header Host $upstream_addr;
# Option 3: Custom host header for specific needs
proxy_set_header Host web1.local;
When working with Docker containers, you might need additional configuration:
location / {
proxy_pass http://main;
proxy_set_header Host $host;
proxy_set_header X-Original-Host $http_host;
proxy_set_header X-Forwarded-Host $host;
proxy_redirect off;
}
- Check headers with
curl -v http://example.com
- Inspect Nginx logs with
log_format
including$host
- Verify Docker container receives correct headers
While these header modifications are lightweight, consider:
- Connection pooling with
keepalive
- Proper buffer sizing with
proxy_buffer_size
- Caching strategies for static content