What's the difference of $host and $http_host in Nginx
Asked Answered
D

4

233

In Nginx, what's the difference between variables $host and $http_host.

Devonna answered 14/3, 2013 at 16:25 Comment(0)
D
281

$host is a variable of the Core module.

$host

This variable is equal to line Host in the header of request or name of the server processing the request if the Host header is not available.

This variable may have a different value from $http_host in such cases: 1) when the Host input header is absent or has an empty value, $host equals to the value of server_name directive; 2)when the value of Host contains port number, $host doesn't include that port number. $host's value is always lowercase since 0.8.17.

$http_host is also a variable of the same module but you won't find it with that name because it is defined generically as $http_HEADER (ref).

$http_HEADER

The value of the HTTP request header HEADER when converted to lowercase and with 'dashes' converted to 'underscores', e.g. $http_user_agent, $http_referer...;


Summarizing:

  • $http_host equals always the HTTP_HOST request header.
  • $host equals $http_host, lowercase and without the port number (if present), except when HTTP_HOST is absent or is an empty value. In that case, $host equals the value of the server_name directive of the server which processed the request.
Devonna answered 14/3, 2013 at 16:25 Comment(6)
$host is specifically the first server_name that is defined in the current server block. if you have multiple server_names, only the first one will appear.Booking
True. In fact, it is quite typical to define: server_name example.com www.example.com;Devonna
you can have multiple server_name directives too. if you happen to have a regex in the first one, that becomes the $host , and all sorts of ugly stuff can happen during rewrite rules.Booking
Does the $server_name variable equal the server_name directive's value or the actual server name that was selected if there were multiple server_name directives?Discriminating
@Discriminating $server_name is always equal to the first value specified with the server_name directive. For example, with server_name example.com one.example.com two.example.com;, $server_name will always be "example.com", regardless of which host the user has specified. In fact, if you don't have a default_server, the host might be something completely different (like example.org).Kellda
Links are kinda 404 now.Goyette
E
21

The accepted answer and its comments don't seem to be correct (anymore). The docs (http://nginx.org/en/docs/http/ngx_http_core_module.html#var_host) say that $host is

in this order of precedence: host name from the request line, or host name from the “Host” request header field, or the server name matching a request

So $http_host is always the value of the Host header field. They might differ if the host in the request line (if specified) differs from the Host header field. Or if the Host header is not set.

server_name matches only the Host header field (http://nginx.org/en/docs/http/request_processing.html), so that $host may differ from the matched server_name.

Ephram answered 23/4, 2021 at 8:50 Comment(1)
yes, though it is important to note that $http_host has still the port included, while $host doesn't seem so. Some peculiar settings may need the port to behave well, otherwise they fall on standard settings (port 80/443).Relator
S
16

$http_host

$http_host always equals Host request header field

Host: example.org

$host

$host is in this order of precedence (from high to low):

  • Host name from the request line
    GET http://example.org/test/ HTTP/1.1
    
  • Host request header field
  • The server_name (in Nginx config) matching a request, even if server_name is wildcard (Ex: server_name *.example.org;)

Host name from the request line

When open URL http://example.org/test/ ...

Most browser send the request like this

GET /test/ HTTP/1.1
Host: example.org

Most browser doesn't send the request like this (but this is valid request)

GET http://example.org/test/ HTTP/1.1

Validation

Nginx testing config

server {
    listen       80;
    server_name  *.example.org;

    location / {
        default_type "text/plain";
        return 200 "[host] = $host";
    }
}

When all exist ...

$host = host name from the request line

curl http://127.0.0.1 -v \
  --request-target http://request.example.org/test/ \
  --path-as-is \
  -H "Host: host.example.org"

This command will

  • Connect to 127.0.0.1
  • Send request path as GET http://request.example.org/test/ HTTP/1.1
  • Set Host header to Host: host.example.org
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET http://request.example.org/test/ HTTP/1.1
> Host: host.example.org
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.23.1
< Date: Fri, 21 Oct 2022 02:00:56 GMT
< Content-Type: text/plain
< Content-Length: 28
< Connection: keep-alive
<
* Connection #0 to host 127.0.0.1 left intact
[host] = request.example.org

When only Host header exist ...

$host = Host header

curl http://127.0.0.1/test/ -v \
  -H "Host: host.example.org"
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET /test/ HTTP/1.1
> Host: host.example.org
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.23.1
< Date: Fri, 21 Oct 2022 02:01:37 GMT
< Content-Type: text/plain
< Content-Length: 25
< Connection: keep-alive
<
* Connection #0 to host 127.0.0.1 left intact
[host] = host.example.org

When none exist ...

$host = server_name (in Nginx config)

# HTTP 1.1 must have Host header, so use HTTP 1.0
curl http://127.0.0.1/test/ -v -H "Host:" -0
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET /test/ HTTP/1.0
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.23.1
< Date: Fri, 21 Oct 2022 02:02:20 GMT
< Content-Type: text/plain
< Content-Length: 22
< Connection: close
<
* Closing connection 0
[host] = *.example.org

Ref: ngx_http_core_module, Nginx $host validation

Seline answered 19/8, 2022 at 10:41 Comment(1)
Excellent answer. The problem is, that "Host name from the request line" has the highest priority and will route the request to a server (vhost) even if the request has a bogus Host header and does not match server_name. This is an issue because applications behind nginx tend to use the passed on Host header. It's a pity that server_name does not check the Host header (if exists in the request) if there was a hostname in "the request line" -- a rare and mostly malicious case.Weinman
E
0

Both $http_host and $host are variables used in Nginx configuration files, but they have slight differences in their usage and behavior.

  1. $http_host: This variable represents the value of the "Host" header in the HTTP request. It includes the port if specified by the client. For example, if the client sends a request to "example.com:8080", then $http_host will be set to "example.com:8080". This variable is useful when you need to capture the exact host value from the client request.

  2. $host: This variable represents the name of the server requested by the client, without the port number. It only contains the hostname or IP address. For example, if the client sends a request to "example.com:8080", then $host will be set to "example.com". This variable is commonly used for server configuration, especially when defining server names or redirects.

To summarize, the main difference between $http_host and $host is that $http_host includes the port number specified by the client, while $host does not. In most cases, you would use $host when you need to capture the hostname for server-related configuration, and $http_host when you need to capture the complete "Host" header value with the port included.

Ernaldus answered 10/8, 2023 at 12:25 Comment(1)

© 2022 - 2024 — McMap. All rights reserved.