CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-waitress

A production-quality pure-Python WSGI server with robust HTTP protocol support and comprehensive configuration options

Pending
Overview
Eval results
Files

proxy-headers.mddocs/

Proxy Headers

Support for parsing trusted proxy headers (X-Forwarded-For, X-Forwarded-Proto, etc.) when running behind reverse proxies or load balancers.

Capabilities

Proxy Headers Middleware

WSGI middleware for handling proxy headers from trusted sources, modifying the WSGI environ to reflect the original client request information.

def proxy_headers_middleware(app, trusted_proxy=None, trusted_proxy_count=1, trusted_proxy_headers=None, clear_untrusted=True, log_untrusted=False, logger=logger):
    """
    WSGI middleware for processing trusted proxy headers.
    
    Parameters:
    - app: WSGI application to wrap
    - trusted_proxy (str): IP address of trusted proxy (e.g., '127.0.0.1')
    - trusted_proxy_count (int): Number of trusted proxy hops to process
    - trusted_proxy_headers (set): Set of header names to trust and process
    - clear_untrusted (bool): Remove untrusted proxy headers (default: True)
    - log_untrusted (bool): Log when untrusted headers are encountered (default: False)
    - logger: Logger for warnings and errors (default: waitress logger)
    
    Returns:
    WSGI application: Wrapped application with proxy header processing
    
    Notes:
    - Modifies WSGI environ based on trusted proxy headers
    - Updates REMOTE_ADDR, HTTP_HOST, wsgi.url_scheme as appropriate
    - Only processes headers from configured trusted sources
    """

Proxy Header Parsing

Low-level function for parsing and validating proxy headers from trusted sources.

def parse_proxy_headers(environ, trusted_proxy_count, trusted_proxy_headers, logger=logger):
    """
    Parse trusted proxy headers and update WSGI environ.
    
    Parameters:
    - environ (dict): WSGI environ dictionary to modify
    - trusted_proxy_count (int): Number of proxy hops to trust
    - trusted_proxy_headers (set): Headers to process
    - logger: Logger for warnings and errors (default: waitress logger)
    
    Returns:
    set: Set of untrusted header keys that were processed
    
    Raises:
    MalformedProxyHeader: When proxy headers contain invalid data
    """

Proxy Header Data Structures

Classes and constants for handling proxy header information.

class MalformedProxyHeader(Exception):
    """
    Raised when proxy headers contain invalid or malformed data.
    
    This can occur when:
    - Forwarded headers have invalid syntax
    - X-Forwarded-* headers contain non-IP addresses
    - Header values exceed expected formats
    """

class Forwarded:
    """
    NamedTuple for parsed RFC 7239 Forwarded header data.
    
    Attributes:
    - by (str): Proxy identifier that forwarded the request
    - for_ (str): Client identifier (usually IP address)  
    - host (str): Host header value from original request
    - proto (str): Protocol scheme (http/https) from original request
    """
    
    by: str
    for_: str  
    host: str
    proto: str

PROXY_HEADERS = frozenset([
    'X_FORWARDED_FOR',
    'X_FORWARDED_HOST', 
    'X_FORWARDED_PROTO',
    'X_FORWARDED_PORT',
    'X_FORWARDED_BY',
    'FORWARDED'
])

Trusted Proxy Configuration

Configure waitress to trust specific proxy sources and process their headers.

Single Proxy Setup

from waitress import serve

# Trust single reverse proxy (e.g., nginx on same server)
serve(app,
    trusted_proxy='127.0.0.1',
    trusted_proxy_headers={'x-forwarded-for', 'x-forwarded-proto'},
    host='127.0.0.1',
    port=8080
)

# This configuration will:
# - Only trust headers from 127.0.0.1
# - Process X-Forwarded-For and X-Forwarded-Proto headers
# - Ignore proxy headers from other sources

Multiple Proxy Layers

# Trust multiple proxy hops (e.g., Cloudflare + nginx)
serve(app,
    trusted_proxy_count=2,  # Trust 2 levels of proxies
    trusted_proxy_headers={
        'x-forwarded-for',
        'x-forwarded-proto', 
        'x-forwarded-host',
        'x-real-ip'
    }
)

# This processes headers through 2 proxy layers:
# Client -> Cloudflare -> nginx -> waitress

Comprehensive Proxy Support

# Support all common proxy headers
serve(app,
    trusted_proxy='10.0.0.1',  # Load balancer IP
    trusted_proxy_headers={
        'x-forwarded-for',      # Client IP chain
        'x-forwarded-proto',    # Original protocol (http/https)
        'x-forwarded-host',     # Original Host header
        'x-forwarded-port',     # Original port
        'x-real-ip',           # Direct client IP (nginx)
        'forwarded'            # RFC 7239 standard header
    },
    clear_untrusted=True,      # Remove headers from untrusted sources
    log_untrusted=True         # Log untrusted header attempts
)

WSGI Middleware Usage

Use proxy headers middleware for fine-grained control in WSGI applications.

from waitress.proxy_headers import proxy_headers_middleware
from waitress import serve

def my_app(environ, start_response):
    # Application sees corrected environ values
    client_ip = environ['REMOTE_ADDR']        # Real client IP
    original_host = environ['HTTP_HOST']      # Original host header
    scheme = environ['wsgi.url_scheme']       # Original scheme (http/https)
    
    status = '200 OK'
    headers = [('Content-Type', 'text/plain')]
    start_response(status, headers)
    
    response = f"Client IP: {client_ip}\nHost: {original_host}\nScheme: {scheme}"
    return [response.encode('utf-8')]

# Wrap application with proxy middleware
proxied_app = proxy_headers_middleware(
    my_app,
    trusted_proxy='192.168.1.100',
    trusted_proxy_headers={'x-forwarded-for', 'x-forwarded-proto'}
)

# Serve wrapped application
serve(proxied_app, host='127.0.0.1', port=8080)

Header Processing Examples

Understanding how different proxy headers are processed.

X-Forwarded-For Processing

# Original request through proxy chain:
# Client (203.0.113.1) -> Proxy1 (10.0.0.1) -> Proxy2 (127.0.0.1) -> Waitress

# X-Forwarded-For: 203.0.113.1, 10.0.0.1
# With trusted_proxy_count=2:

environ_before = {
    'REMOTE_ADDR': '127.0.0.1',  # Direct connection from Proxy2
    'HTTP_X_FORWARDED_FOR': '203.0.113.1, 10.0.0.1'
}

environ_after = {
    'REMOTE_ADDR': '203.0.113.1',  # Original client IP
    'HTTP_X_FORWARDED_FOR': '203.0.113.1, 10.0.0.1'  # Preserved
}

X-Forwarded-Proto Processing

# HTTPS request through HTTP proxy:
# Client (HTTPS) -> nginx (HTTP) -> Waitress

# X-Forwarded-Proto: https
# With trusted proxy headers:

environ_before = {
    'wsgi.url_scheme': 'http',  # Direct HTTP connection
    'HTTP_X_FORWARDED_PROTO': 'https'
}

environ_after = {
    'wsgi.url_scheme': 'https',  # Original client scheme
    'HTTP_X_FORWARDED_PROTO': 'https'  # Preserved
}

RFC 7239 Forwarded Header

# Standard Forwarded header processing:
# Forwarded: for=203.0.113.1;proto=https;host=example.com

environ_before = {
    'REMOTE_ADDR': '127.0.0.1',
    'HTTP_HOST': 'localhost:8080',
    'wsgi.url_scheme': 'http',
    'HTTP_FORWARDED': 'for=203.0.113.1;proto=https;host=example.com'
}

environ_after = {
    'REMOTE_ADDR': '203.0.113.1',      # From for= parameter
    'HTTP_HOST': 'example.com',        # From host= parameter  
    'wsgi.url_scheme': 'https',        # From proto= parameter
    'HTTP_FORWARDED': '...'            # Preserved
}

Security Considerations

Important security aspects when handling proxy headers.

# Security best practices:
TRUST_CONFIGURATION = "Only trust known proxy IPs"
HEADER_VALIDATION = "Validate all header values"
CLEAR_UNTRUSTED = "Remove headers from untrusted sources"
LOG_ATTEMPTS = "Log untrusted header attempts"

# Common security issues:
HEADER_SPOOFING = "Clients can set X-Forwarded-* headers"
IP_SPOOFING = "Untrusted proxies can forge client IPs" 
PROTOCOL_CONFUSION = "HTTP/HTTPS confusion without validation"

# Mitigation strategies:
WHITELIST_PROXIES = "trusted_proxy configuration"
VALIDATE_HEADERS = "Automatic validation in parse_proxy_headers"
REMOVE_UNTRUSTED = "clear_untrusted=True (default)"
MONITOR_ATTEMPTS = "log_untrusted=True for monitoring"

Secure Configuration Examples

# Production security configuration
serve(app,
    # Only trust specific load balancer
    trusted_proxy='10.0.1.100',
    
    # Limit to essential headers
    trusted_proxy_headers={'x-forwarded-for', 'x-forwarded-proto'},
    
    # Security defaults
    clear_untrusted=True,   # Remove untrusted headers
    log_untrusted=True,     # Log security attempts
    
    # Bind only to internal interface
    host='127.0.0.1',
    port=8080
)

# High-security configuration with validation
def validate_proxy_headers(app):
    def wrapper(environ, start_response):
        # Additional header validation
        xff = environ.get('HTTP_X_FORWARDED_FOR', '')
        if xff and not is_valid_ip_list(xff):
            # Reject request with invalid IPs
            start_response('400 Bad Request', [])
            return [b'Invalid proxy headers']
        return app(environ, start_response)
    return wrapper

secure_app = validate_proxy_headers(
    proxy_headers_middleware(app, trusted_proxy='192.168.1.1')
)

Deployment Integration

Common deployment patterns with proxy header handling.

nginx Reverse Proxy

# nginx configuration
server {
    listen 80;
    server_name example.com;
    
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header Host $host;
    }
}
# Corresponding waitress configuration
serve(app,
    trusted_proxy='127.0.0.1',
    trusted_proxy_headers={
        'x-forwarded-for',
        'x-forwarded-proto', 
        'x-forwarded-host'
    },
    host='127.0.0.1',
    port=8080
)

HAProxy Load Balancer

# HAProxy configuration
frontend web_frontend
    bind *:80
    option forwardfor
    http-request set-header X-Forwarded-Proto http
    default_backend web_servers

backend web_servers
    server web1 127.0.0.1:8080
# Waitress configuration for HAProxy
serve(app,
    trusted_proxy='127.0.0.1',
    trusted_proxy_headers={'x-forwarded-for', 'x-forwarded-proto'},
    host='127.0.0.1', 
    port=8080
)

Install with Tessl CLI

npx tessl i tessl/pypi-waitress

docs

buffer-management.md

command-line.md

configuration.md

error-handling.md

http-processing.md

index.md

proxy-headers.md

server-management.md

task-management.md

tile.json