"""Utilities for extracting client IP addresses from HTTP requests. Handles X-Forwarded-For and X-Real-IP headers when behind a reverse proxy (nginx). Only trusts these headers when the request comes from a known trusted proxy. """ from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from fastapi import Request def get_client_ip(request: Request, trusted_proxies: list[str] | None = None) -> str: """Extract the client IP address from a request. When the request comes from a trusted proxy, reads the real IP from X-Forwarded-For or X-Real-IP headers. Otherwise returns the immediate connection source (request.client.host). X-Forwarded-For can be spoofed by the client, so we only trust it if the request comes from a known proxy IP. Args: request: The incoming FastAPI request. trusted_proxies: Optional list of trusted proxy IP addresses. If None, only uses request.client.host. Returns: The best-guess client IP address suitable for rate limiting. """ if not request.client: return "0.0.0.0" immediate_ip = request.client.host trusted_proxies = trusted_proxies or [] # If the immediate connection is not from a trusted proxy, use it directly. if immediate_ip not in trusted_proxies: return immediate_ip # Proxy is trusted, check for forwarded headers. # X-Forwarded-For can contain multiple IPs (client, proxy1, proxy2). # We use the leftmost (the original client). forwarded_for = request.headers.get("X-Forwarded-For", "").strip() if forwarded_for: # Take the first IP in the list client_ip = forwarded_for.split(",")[0].strip() if client_ip: return client_ip # Fall back to X-Real-IP real_ip = request.headers.get("X-Real-IP", "").strip() if real_ip: return real_ip # No forwarded headers found, use immediate connection return immediate_ip