HTTP

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.

5 min read beginner Try in Playground

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()
})

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.

Keep Learning