- Home
- HTTP Headers
- Host Header
Header
Host 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.
TL;DR: Specifies which domain and port you’re requesting, enabling virtual hosting where multiple websites share one IP address. Required in HTTP/1.1.
What is Host?
The Host header tells the server which website or domain you’re trying to reach. It’s like addressing an envelope—even if multiple websites share the same server, the Host header ensures your request goes to the right one.
This header is mandatory in HTTP/1.1 and enables virtual hosting, where one server can host multiple websites.
How Host Works
Client specifies target domain:
GET /api/users HTTP/1.1
Host: api.example.com
Accept: application/json
```text
**Server routes to correct website:**
```http
HTTP/1.1 200 OK
Content-Type: application/json
{"users": [...]}
Host Header Format
Domain Only
Host: example.com
Host: www.example.com
Host: api.example.com
```http
### Domain with Port
```http
Host: example.com:8080
Host: localhost:3000
Host: api.example.com:443
IP Address
Host: 192.168.1.100
Host: 127.0.0.1:8080
Host: [::1]:3000
```text
## Real-World Examples
### Production Website
```http
GET / HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0...
API Endpoint
POST /api/v1/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer token123
```text
### Development Server
```http
GET /dashboard HTTP/1.1
Host: localhost:3000
Accept: text/html
Subdomain Service
GET /upload HTTP/1.1
Host: cdn.example.com
Accept: */*
```text
## Virtual Hosting
### Multiple Sites, One Server
A single server at IP 192.168.1.100 can host:
```http
# Request to blog
GET / HTTP/1.1
Host: blog.example.com
→ Serves blog content
# Request to shop
GET / HTTP/1.1
Host: shop.example.com
→ Serves e-commerce site
# Request to API
GET /users HTTP/1.1
Host: api.example.com
→ Serves API responses
Server Configuration
# Nginx virtual host example
server {
server_name blog.example.com;
root /var/www/blog;
}
server {
server_name shop.example.com;
root /var/www/shop;
}
server {
server_name api.example.com;
location / {
proxy_pass http://localhost:3000;
}
}
```text
## Port Handling
### Default Ports (Usually Omitted)
```http
# These are equivalent:
Host: example.com
Host: example.com:80
# These are equivalent:
Host: secure.example.com
Host: secure.example.com:443
Custom Ports (Must Include)
Host: example.com:8080
Host: api.example.com:3000
Host: localhost:5173
```javascript
### Port Matching Rules
```javascript
// Server checks both domain and port
app.listen(3000, () => {
console.log('Server on port 3000')
})
// Request must match:
// Host: localhost:3000 ✅
// Host: localhost:8080 ❌ (wrong port)
Security Implications
Host Header Injection
# Malicious request
GET /reset-password HTTP/1.1
Host: evil.com
```javascript
**Server protection:**
```javascript
app.use((req, res, next) => {
const allowedHosts = ['example.com', 'www.example.com', 'api.example.com']
if (!allowedHosts.includes(req.headers.host)) {
return res.status(400).json({ error: 'Invalid host header' })
}
next()
})
Password Reset Attacks
// ❌ Vulnerable: Uses Host header in email links
const resetLink = `https://${req.headers.host}/reset?token=${token}`
// ✅ Secure: Use configured domain
const resetLink = `https://${process.env.DOMAIN}/reset?token=${token}`
```text
## Browser Behavior
### Automatic Host Header
Browsers automatically set the Host header:
```javascript
// When you visit https://example.com/page
// Browser automatically sends:
// Host: example.com
fetch('https://api.example.com/data')
// Browser automatically sends:
// Host: api.example.com
CORS and Host
# Preflight request
OPTIONS /api/data HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
```javascript
## Server-Side Handling
### Reading Host Header
```javascript
// Express.js
app.get('/api/info', (req, res) => {
const host = req.headers.host
const hostname = req.hostname // Without port
const protocol = req.protocol
res.json({
host,
hostname,
fullUrl: `${protocol}://${host}${req.originalUrl}`
})
})
Virtual Host Routing
app.use((req, res, next) => {
const host = req.headers.host
if (host === 'api.example.com') {
// Route to API handlers
req.service = 'api'
} else if (host === 'admin.example.com') {
// Route to admin interface
req.service = 'admin'
} else if (host === 'www.example.com') {
// Route to main website
req.service = 'website'
}
next()
})
```javascript
### Subdomain Extraction
```javascript
function getSubdomain(host) {
const parts = host.split('.')
if (parts.length > 2) {
return parts[0] // Returns 'api' from 'api.example.com'
}
return null
}
app.use((req, res, next) => {
req.subdomain = getSubdomain(req.headers.host)
next()
})
Common Patterns
API Versioning by Subdomain
GET /users HTTP/1.1
Host: v1.api.example.com
GET /users HTTP/1.1
Host: v2.api.example.com
```text
### Environment-Based Routing
```http
# Development
Host: dev.example.com
# Staging
Host: staging.example.com
# Production
Host: www.example.com
Multi-Tenant Applications
# Tenant A
Host: companyA.saas.example.com
# Tenant B
Host: companyB.saas.example.com
```text
## Testing Host Headers
### Using curl
```bash
# Specify host explicitly
curl -H "Host: api.example.com" http://192.168.1.100/users
# Test virtual hosting
curl -H "Host: blog.example.com" http://server.com/
curl -H "Host: shop.example.com" http://server.com/
# Test with custom port
curl -H "Host: localhost:3000" http://127.0.0.1:3000/api
Local Development
// /etc/hosts file for local testing
127.0.0.1 local.example.com
127.0.0.1 api.local.example.com
// Then access:
// http://local.example.com:3000
// http://api.local.example.com:3000
```javascript
## Best Practices
### Validate Host Headers
```javascript
const ALLOWED_HOSTS = process.env.ALLOWED_HOSTS?.split(',') || ['localhost']
app.use((req, res, next) => {
const host = req.headers.host?.split(':')[0] // Remove port
if (!ALLOWED_HOSTS.includes(host)) {
return res.status(400).json({ error: 'Invalid host' })
}
next()
})
Use Environment Variables
// ❌ Hardcoded domains
const baseUrl = 'https://api.example.com'
// ✅ Environment-based
const baseUrl = `https://${process.env.API_HOST || 'localhost:3000'}`
```text
### Handle Missing Host
```javascript
app.use((req, res, next) => {
if (!req.headers.host) {
return res.status(400).json({ error: 'Host header required' })
}
next()
})
Related Headers
- Origin - Request origin for CORS
- Referer - Previous page URL
- User-Agent - Client identification
- Authorization - Authentication credentials
Host Header Injection Attacks
The Host header is a common target for injection attacks because many applications use it to construct URLs, generate links in emails, and set cookie domains. If an application blindly trusts the Host header without validation, an attacker can manipulate it to redirect users to malicious sites or poison password reset links.
Password reset poisoning is the most well-known attack. When a user requests a password reset, the application generates a reset link using the Host header: https://{req.headers.host}/reset?token=abc. An attacker who can control the Host header (for example, by sending a request directly to the server’s IP address with a custom Host header) can make the reset email contain a link to their own domain. When the victim clicks the link, the attacker captures the reset token.
The defense is straightforward: never use the Host header to construct URLs in security-sensitive contexts. Instead, use a configured BASE_URL environment variable or a hardcoded domain. Validate the Host header against an allowlist of known domains and return 400 for unrecognized values. Most web frameworks provide a way to configure trusted hosts, and enabling this validation is a security best practice regardless of whether you use the Host header in your application logic.
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?
Multiple websites can share one IP address. The Host header tells the server which site you want. Without it, the server cannot determine which virtual host to serve.
What happens if Host is missing?
HTTP/1.1 servers should return 400 Bad Request if Host is missing. HTTP/1.0 did not require it, but modern servers expect it for virtual hosting.
Does Host include the port?
Include port only if non-standard: Host: example.com for port 80/443, Host: example.com:8080 for other ports. Browsers handle this automatically.