Request header
> Host: www.example.comHost Header
Learn how the Host header specifies the target server domain name and port for HTTP requests. Essential for virtual hosting and routing on shared servers.
- Direction
- Request
- Category
- Routing
- Spec
- RFC 9110 §7.2
- JS can set it
- No: forbidden header name
On this page
TL;DR:
Hostcarries the target hostname and optional port, not a URL. HTTP/1.1 requires exactly one valid Host field; missing, duplicate, or invalid values require 400. HTTP/2 and HTTP/3 normally carry the authority in:authority.
Host identifies the target authority so servers can distinguish sites sharing an IP address. It is not the client’s address and does not change which socket the client connected to. RFC 9110 §7.2 defines the field.
How Host Works
This illustrative request targets the API site on an already-established connection:
GET /users HTTP/1.1
Host: api.example.com
The server uses the target authority for routing. In a request using an absolute URI, an HTTP/1.1 proxy must use the authority from that URI and replace the received Host value when forwarding. See RFC 9112 §3.2.2.
Host Header Format
Illustrative valid values:
Host: example.com
Host: example.com:8080
Host: 192.0.2.10:8080
Host: [2001:db8::1]:8080
Do not include a scheme, path, or userinfo. IPv6 literals require brackets. Port omission means the default for the scheme: 80 for http, 443 for https. An explicitly specified default port is allowed; it is not forbidden just because it is conventional.
Virtual Hosting
In nginx, use server_name for name-based routing. This example belongs inside http; the directories must exist:
server {
listen 80;
server_name blog.example.com;
root /var/www/blog;
}
server {
listen 80;
server_name shop.example.com;
root /var/www/shop;
}
An unmatched name goes to the default server for the listening address and port. default_server is a listen setting, not a special server name. See nginx request processing.
Apache provides the same name-based routing with separate virtual hosts. With Listen 80 already configured, these blocks route two names:
<VirtualHost *:80>
ServerName blog.example.com
DocumentRoot "/var/www/blog"
</VirtualHost>
<VirtualHost *:80>
ServerName shop.example.com
DocumentRoot "/var/www/shop"
</VirtualHost>
Create the document roots and configure access to them under your existing Apache policy. For an unmatched name, Apache uses the first matching address-and-port virtual host. ServerName does not create DNS records. Apache’s virtual-host examples explain matching and ordering.
Port Handling
Changing Host: localhost:8080 while connected to port 3000 does not reconnect to 8080. Whether the application rejects a mismatch depends on its routing and validation. The request authority should describe the target URI, not an arbitrary internal backend port.
Browser Behavior
Browser JavaScript cannot override Host through Fetch: it is a forbidden request header. HTTP/2 uses :authority for this role, so seeing that pseudo-header instead of a textual Host line is expected.
Server-Side Handling
Log the received value for diagnosis, but do not treat it as a configured public origin. Forwarding layers can rewrite it. In Express, proxy trust also affects whether req.hostname uses X-Forwarded-Host; see its proxy documentation.
For diagnosis, this route reports the raw authority separately from Express’s hostname interpretation. Add it to an existing Express app:
app.get('/request-authority', (req, res) => {
res.json({
host: req.get('host'),
hostname: req.hostname,
protocol: req.protocol
})
})
req.hostname omits the port; it can come from X-Forwarded-Host when proxy trust is enabled. Use the raw field to debug what arrived, and the configured proxy policy to decide what to trust. Neither value automatically becomes a safe public URL.
For tenant routing, prefer an explicit mapping of allowed names to tenant records. company.example.com can identify a tenant, but a successful hostname lookup is not authorization to access that tenant’s data. Reject unknown names rather than falling through to a privileged default.
Testing Host Headers
For HTTP, change only the routing header:
curl --http1.1 -i -H 'Host: blog.example.com' http://127.0.0.1/
For HTTPS, keep the hostname in the URL and override DNS with curl’s --resolve:
curl --http1.1 --resolve blog.example.com:443:127.0.0.1 https://blog.example.com/
That keeps the URL hostname for TLS SNI and certificate verification. Merely changing Host on https://127.0.0.1/ does not make TLS use blog.example.com.
To exercise both virtual hosts locally without editing /etc/hosts:
curl --resolve blog.example.com:80:127.0.0.1 http://blog.example.com/
curl --resolve shop.example.com:80:127.0.0.1 http://shop.example.com/
The port in each --resolve entry must match the URL’s port. For a development server on 3000, use --resolve blog.example.com:3000:127.0.0.1 and http://blog.example.com:3000/. This changes curl’s address resolution for the named target; the server still needs a matching virtual-host configuration.
Getting Host right
RFC 9112 §3.2 requires 400 for an HTTP/1.1 request with no Host field, multiple Host field lines, or an invalid field value. This is a protocol requirement, not optional application advice. It does not mean every syntactically valid hostname belongs to your site.
Security Implications
Host is request-controlled input. Validate allowed authorities before using them for application routing. Avoid host.split(':')[0] as a general hostname parser: IPv6 addresses themselves contain colons.
For a direct HTTP/1.1 Express deployment with a small known set of authorities, an exact allowlist avoids ad hoc IPv6 parsing:
const allowedAuthorities = new Set(['localhost:3000', '[::1]:3000'])
app.use((req, res, next) => {
const authority = req.get('host')
if (!authority || !allowedAuthorities.has(authority.toLowerCase())) {
return res.status(400).send('Unrecognized authority')
}
next()
})
Place the middleware before application routes. Include each authority you intend to serve, including its port form. This deliberately narrow policy rejects aliases and explicit default-port variants unless listed. At a proxy, validate the externally accepted authority there as well; validating only a rewritten internal Host loses the original routing context.
Host Header Injection Attacks
If password-reset URLs are built from an unchecked request Host, the request can determine where the emailed token goes. Use a configured public origin for those links:
const publicOrigin = 'https://accounts.example.com'
const resetUrl = new URL('/reset', publicOrigin)
resetUrl.searchParams.set('token', token)
This is a construction snippet; token must come from your reset flow. The trust issue is covered by RFC 9110 §17.1.
Related Headers
Frequently asked questions
What is the Host header?
Host specifies the domain name and port of the server being requested. It is required in HTTP/1.1 and enables virtual hosting where one IP serves multiple domains.
Why is Host required?
HTTP/1.1 requires Host to identify the target authority, including for name-based virtual hosting. A server may have a default site, but that does not waive the protocol requirement.
What happens if Host is missing?
An HTTP/1.1 server must return 400 for a missing Host field, duplicate Host field lines, or an invalid Host value, as specified in RFC 9112 section 3.2.
Does Host include the port?
Host can include a port. A non-default port must be represented when it is part of the target authority; omission implies the default port for the URI scheme.
Sources
Related
URI vs URL (and URN)
A URL is a URI that says where a resource lives; a URI only identifies it. See the parts of a URL, what the server never receives and parser pitfalls.
SNI (Server Name Indication)
SNI is a TLS extension that sends the hostname in the ClientHello so one IP can serve many certificates. Learn debugging with curl and nginx upstream pitfalls.
Accept Header
Learn how the Accept header tells servers which content types (JSON, HTML, XML) your client can handle. Master content negotiation and quality values.
Accept-Language Header
Learn how the Accept-Language header tells servers which languages your client prefers for localized content. Understand language tags and quality values.