HTTP Headers

Fundamentals Security Notes Jan 9, 2026 HTTP
http headers metadata request response

Definition

HTTP headers are key-value pairs included in HTTP requests and responses to provide metadata about the message. They control behavior, enable features, and convey information about content, authentication, caching, encoding, and more.

Headers are divided into four categories:

  1. Request Headers - Sent by clients (e.g., Authorization, Accept, User-Agent)
  2. Response Headers - Sent by servers (e.g., Content-Type, Cache-Control, ETag)
  3. Representation Headers - Describe the body data (e.g., Content-Length, Content-Encoding)
  4. Custom Headers - Application-specific (e.g., X-RateLimit-Remaining, X-Request-ID)

HTTP headers are defined in RFC 7231 and are fundamental to REST APIs, web security, and performance optimization.

Example

Request Headers:

GET /api/users/123 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Accept: application/json
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Cache-Control: no-cache

Response Headers:

HTTP/1.1 200 OK
Date: Thu, 09 Jan 2026 10:30:00 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 142
Cache-Control: max-age=3600, public
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1736421000
Access-Control-Allow-Origin: https://example.com
Strict-Transport-Security: max-age=31536000; includeSubDomains

{
  "id": 123,
  "name": "Alice Smith"
}

Code Example

JavaScript (Fetch API):

// Setting Request Headers
const fetchUser = async (userId) => {
  const response = await fetch(`https://api.example.com/users/${userId}`, {
    method: 'GET',
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN',
      'Accept': 'application/json',
      'Accept-Language': 'en-US',
      'X-Request-ID': crypto.randomUUID(),
      'If-None-Match': '"33a64df551425fcc55e4d42a148795d9f25f89d4"'
    }
  });
  
  // Reading Response Headers
  console.log('Content-Type:', response.headers.get('Content-Type'));
  console.log('ETag:', response.headers.get('ETag'));
  console.log('Rate Limit:', response.headers.get('X-RateLimit-Remaining'));
  
  // Iterate all headers
  for (const [key, value] of response.headers) {
    console.log(`${key}: ${value}`);
  }
  
  // Check if cached (304 Not Modified)
  if (response.status === 304) {
    console.log('Using cached version');
    return null;
  }
  
  return await response.json();
};

// Custom Headers in POST Request
const createUser = async (userData) => {
  const response = await fetch('https://api.example.com/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer YOUR_TOKEN',
      'X-Idempotency-Key': crypto.randomUUID(), // Prevent duplicate requests
      'X-Client-Version': '1.2.3'
    },
    body: JSON.stringify(userData)
  });
  
  return await response.json();
};

Python (requests library):

import requests
import uuid

# Setting Request Headers
def fetch_user(user_id):
    headers = {
        'Authorization': 'Bearer YOUR_TOKEN',
        'Accept': 'application/json',
        'Accept-Language': 'en-US',
        'X-Request-ID': str(uuid.uuid4()),
        'If-None-Match': '"33a64df551425fcc55e4d42a148795d9f25f89d4"'
    }
    
    response = requests.get(
        f'https://api.example.com/users/{user_id}',
        headers=headers
    )
    
    # Reading Response Headers
    print(f"Content-Type: {response.headers.get('Content-Type')}")
    print(f"ETag: {response.headers.get('ETag')}")
    print(f"Rate Limit: {response.headers.get('X-RateLimit-Remaining')}")
    
    # Iterate all headers
    for key, value in response.headers.items():
        print(f"{key}: {value}")
    
    # Check if cached (304 Not Modified)
    if response.status_code == 304:
        print('Using cached version')
        return None
    
    response.raise_for_status()
    return response.json()

# Custom Headers in POST Request
def create_user(user_data):
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer YOUR_TOKEN',
        'X-Idempotency-Key': str(uuid.uuid4()),
        'X-Client-Version': '1.2.3'
    }
    
    response = requests.post(
        'https://api.example.com/users',
        json=user_data,
        headers=headers
    )
    
    response.raise_for_status()
    return response.json()

Diagram

graph TB
    subgraph "Request Headers"
        RH1[Authorization]
        RH2[Accept]
        RH3[Content-Type]
        RH4[User-Agent]
        RH5[If-None-Match]
    end
    
    subgraph "Response Headers"
        RES1[Content-Type]
        RES2[Cache-Control]
        RES3[ETag]
        RES4[X-RateLimit-*]
        RES5[Access-Control-*]
    end
    
    subgraph "Security Headers"
        SEC1[Strict-Transport-Security]
        SEC2[Content-Security-Policy]
        SEC3[X-Frame-Options]
        SEC4[X-Content-Type-Options]
    end
    
    Client -->|Send Request| RH1
    Client -->|Send Request| RH2
    Client -->|Send Request| RH3
    Client -->|Send Request| RH4
    Client -->|Send Request| RH5
    
    Server -->|Send Response| RES1
    Server -->|Send Response| RES2
    Server -->|Send Response| RES3
    Server -->|Send Response| RES4
    Server -->|Send Response| RES5
    
    Server -->|Security| SEC1
    Server -->|Security| SEC2
    Server -->|Security| SEC3
    Server -->|Security| SEC4

Security Notes

SECURITY NOTES

CRITICAL: Custom headers are logged and can leak sensitive information. Never trust client-provided headers.

Sensitive Data in Headers:

  • Never expose secrets: Don’t put API keys, tokens, or passwords in custom headers that are logged
  • Mind header logging: Headers are logged in servers, proxies, WAFs, and monitoring systems
  • Authorization headers: Be careful exposing Authorization headers; use short-lived tokens
  • Avoid PII in headers: Don’t include user IDs, email addresses, or other PII

Header Injection Attacks:

  • Validate all headers: Sanitize input headers to prevent header injection
  • Prevent log injection: Sanitize headers to prevent injection of newlines/carriage returns
  • No CRLF injection: Don’t allow \r\n in header values (header smuggling attacks)

Security Headers:

  • Set Strict-Transport-Security: Force HTTPS; prevent downgrade attacks
  • Set Content-Security-Policy: Prevent XSS attacks via inline scripts
  • Set X-Frame-Options: Prevent clickjacking (DENY or SAMEORIGIN)
  • Set X-Content-Type-Options: Prevent MIME sniffing (nosniff)
  • Set Referrer-Policy: Control referrer information leakage

Client-Provided Headers:

  • Never trust for security: Don’t make authorization decisions based on client headers
  • Validate format: Enforce strict format for custom headers
  • Use whitelisting: Only accept known, safe headers
  • Sanitize for logging: Remove sensitive values before logging

Transport Security:

  • HTTPS mandatory: Encrypt headers in transit to prevent interception
  • No HTTP fallback: Reject unencrypted connections

Analogy

Think of HTTP headers like shipping labels on a package:

  • Request Headers β†’ Shipping instructions (“Handle with care”, “Fragile”)
  • Response Headers β†’ Return receipt info (tracking number, delivery date)
  • Content-Type β†’ What’s inside the box (books, electronics, food)
  • Authorization β†’ Signature proving you’re allowed to send/receive

The package (body) is the actual content, but the labels (headers) control how it’s handled.

Best Practices

  1. Set Required Headers - Host, Content-Type, Authorization when needed
  2. Use Standard Headers - Follow RFC specifications for common headers
  3. Include Security Headers - HSTS, CSP, X-Frame-Options, X-Content-Type-Options
  4. Enable Caching - Use Cache-Control, ETag, Last-Modified for performance
  5. Add Rate Limit Info - Include X-RateLimit-* headers for API clients
  6. Set CORS Headers - Configure Access-Control-* for cross-origin requests
  7. Avoid Custom X- Prefix - Modern practice uses standard names without X- (RFC 6648)
  8. Validate Input Headers - Sanitize and validate all incoming headers
  9. Log Request IDs - Use X-Request-ID for tracing requests across services

Common Mistakes

  • Missing Content-Type - Not specifying the body format in requests/responses
  • Wrong Authorization Format - Using Token abc instead of Bearer abc
  • Exposing Secrets - Logging or caching headers containing tokens
  • No Security Headers - Not setting HSTS, CSP, or X-Frame-Options
  • Case Sensitivity - Treating headers as case-sensitive (they’re case-insensitive)
  • Header Injection - Not sanitizing user input that goes into headers
  • Caching Sensitive Data - Setting Cache-Control: public on authenticated endpoints
  • Trusting Forwarded Headers - Using X-Forwarded-For without validation

Standards & RFCs