When working with Nginx configuration, these three variables often cause confusion:
# The server_name defined in your nginx.conf
$server_name = configured server name in server block
# The 'Host' header from HTTP request
$http_host = raw HTTP Host header (including port if present)
# Processed hostname (no port, lowercase, from $http_host or $server_name)
$host = normalized hostname (no port)
Let's examine how each variable behaves in different scenarios:
# Request: GET / HTTP/1.1 Host: example.com:8080
$http_host → "example.com:8080" # Raw header exactly as received
$host → "example.com" # Strips port automatically
$server_name → "example.com" # From server configuration
When the Host header is missing, Nginx will:
1. Use $server_name for $host
2. Leave $http_host empty
For your specific rewrite case, I recommend:
location = /vb/showthread.php {
if ($arg_p) {
# Best practice for canonical URLs
return 301 $scheme://$host/forum/index.php?posts/$arg_p/;
}
}
Why $host is preferred here:
- Automatically handles port normalization
- More secure than raw $http_host
- Falls back to $server_name when needed
Important differences in security context:
# Potential XSS risk with raw $http_host:
add_header X-Host $http_host; # Unsafe if header contains malicious chars
# Safer alternative:
add_header X-Host $host; # Normalized value
When working with non-standard ports:
# For applications that need the original port
set $full_host $host:$server_port;
# Versus:
set $full_host $http_host; # Might be empty if no Host header
To inspect these values during development:
location /debug {
add_header X-Debug-Host $host;
add_header X-Debug-Http_Host $http_host;
add_header X-Debug-Server_Name $server_name;
return 200 "Debug information sent in headers";
}
When working with Nginx rewrite rules, these three variables often cause confusion:
$host
: Contains the hostname from the request line or the "Host" header (when present). It's normalized to lowercase and excludes port numbers.$http_host
: Exactly matches the "Host" HTTP header as sent by the client, including port if specified.$server_name
: Represents the server_name directive matched for the current request.
Consider these real-world scenarios:
# Client request: GET / HTTP/1.1
# Host: example.com:8080
# Configuration:
server {
server_name example.com;
listen 80;
location /test {
return 200 "$host|$http_host|$server_name";
}
}
The response would be: example.com|example.com:8080|example.com
For your specific rewrite case:
location = /vb/showthread.php {
if ($arg_p) {
return 301 $scheme://$host/forum/index.php?posts/$arg_p/;
}
}
Why $host is optimal here:
- It's safer than $http_host as it's normalized (no port issues)
- It's more dynamic than $server_name when dealing with multiple domains
- Works correctly even if Host header is missing (falls back to server_name)
Important behaviors to note:
- When Host header is missing:
- $host falls back to server_name
- $http_host becomes empty
- With non-standard ports:
- $host strips the port
- $http_host preserves it
- With invalid Host headers:
- $host uses the valid portion
- $http_host shows the raw (possibly invalid) value
For complex multi-domain setups:
server {
server_name primary.com secondary.com;
if ($host != $server_name) {
return 301 $scheme://$server_name$request_uri;
}
}
For port-agnostic redirects:
location /secure {
return 301 https://$host$request_uri;
}