A FastAPI middleware for validating Host headers against allowed hosts, inspired by Django's ALLOWED_HOSTS
Project description
FastAPI Allowed Hosts
A FastAPI middleware that validates the Host header against a list of allowed hosts — inspired by Django's ALLOWED_HOSTS setting. Protects your application from HTTP Host header attacks and provides easy access to the client's IP address.
Features
- 🛡️ Host Header Validation — Reject requests with invalid or spoofed
Hostheaders - 🌐 Proxy Support — Handles
X-Forwarded-Host,X-Forwarded-For, andX-Real-IPheaders - 🎯 Wildcard Matching — Support for subdomain wildcards (e.g.,
.example.com) - 🔄 WWW Redirect — Optional automatic redirect from
example.comtowww.example.com - 📍 Client IP Extraction — Automatically extracts and exposes the real client IP via
request.state.client_ip - ⚡ Zero Dependencies — Only requires FastAPI/Starlette (which you already have)
Installation
pip install fastapi-allowed-hosts
Or with Poetry:
poetry add fastapi-allowed-hosts
Quick Start
from fastapi import FastAPI, Request
from fastapi_allowed_hosts import AllowedHostsMiddleware
app = FastAPI()
# Add the middleware with your allowed hosts
app.add_middleware(
AllowedHostsMiddleware,
allowed_hosts=["example.com", "www.example.com", "localhost"],
)
@app.get("/")
async def root(request: Request):
# Access the client IP extracted by the middleware
client_ip = request.state.client_ip
return {"message": "Hello World", "your_ip": client_ip}
Configuration
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
allowed_hosts |
Sequence[str] |
("*",) |
List of allowed host patterns |
www_redirect |
bool |
True |
Redirect non-www to www if www variant is in allowed_hosts |
on_error |
Callable |
None |
Custom error handler (request, host) -> Response |
Host Patterns
The middleware supports several host pattern formats:
| Pattern | Description | Example Matches |
|---|---|---|
"example.com" |
Exact match | example.com |
".example.com" |
Subdomain wildcard | example.com, api.example.com, sub.api.example.com |
"*" |
Match all hosts | Any host (use only in development!) |
Usage Examples
Production Setup
from fastapi import FastAPI
from fastapi_allowed_hosts import AllowedHostsMiddleware
app = FastAPI()
app.add_middleware(
AllowedHostsMiddleware,
allowed_hosts=[
"myapp.com",
"www.myapp.com",
"api.myapp.com",
],
)
Allow All Subdomains
app.add_middleware(
AllowedHostsMiddleware,
allowed_hosts=[".myapp.com"], # Matches myapp.com and all subdomains
)
Development Setup
import os
from fastapi import FastAPI
from fastapi_allowed_hosts import AllowedHostsMiddleware
app = FastAPI()
# Use wildcard in development, strict hosts in production
allowed = ["*"] if os.getenv("DEBUG") else ["myapp.com", "www.myapp.com"]
app.add_middleware(
AllowedHostsMiddleware,
allowed_hosts=allowed,
)
Disable WWW Redirect
app.add_middleware(
AllowedHostsMiddleware,
allowed_hosts=["example.com", "www.example.com"],
www_redirect=False, # Don't auto-redirect to www
)
Accessing Client IP
The middleware automatically extracts the client IP and makes it available on request.state.client_ip:
from fastapi import FastAPI, Request
from fastapi_allowed_hosts import AllowedHostsMiddleware
app = FastAPI()
app.add_middleware(
AllowedHostsMiddleware,
allowed_hosts=["*"],
)
@app.get("/whoami")
async def whoami(request: Request):
return {
"ip": request.state.client_ip,
"host": request.headers.get("host"),
}
@app.post("/log-action")
async def log_action(request: Request):
client_ip = request.state.client_ip
# Log the action with the real client IP
print(f"Action performed by {client_ip}")
return {"status": "logged"}
How It Works
Host Extraction
The middleware extracts the host following the same algorithm as Django's HttpRequest.get_host():
- Check
X-Forwarded-Hostheader (set by reverse proxies) - Fall back to
Hostheader - Strip port number (handles both IPv4 and IPv6)
- Normalize to lowercase
Client IP Extraction
The client IP is determined using the following priority:
X-Forwarded-Forheader (first IP in the comma-separated list)X-Real-IPheader (commonly used by Nginx)- Direct client connection (
request.client.host)
This ensures the real client IP is captured even when behind load balancers or reverse proxies.
Security Note
When using X-Forwarded-For or X-Forwarded-Host, ensure your reverse proxy is configured to set these headers correctly and that direct access to your application is blocked. Trusting these headers without proper proxy configuration can lead to IP spoofing.
Custom Error Handling
By default, when a request comes from a disallowed host, the middleware returns a 400 Bad Request with the text "Invalid host header". You can customize this behavior using the on_error parameter:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from starlette.responses import Response
from fastapi_allowed_hosts import AllowedHostsMiddleware
app = FastAPI()
def custom_error_handler(request: Request, host: str) -> Response:
"""Custom handler for disallowed hosts."""
return JSONResponse(
status_code=403,
content={
"error": "Access denied",
"message": f"Host '{host}' is not allowed",
"path": str(request.url.path),
},
)
app.add_middleware(
AllowedHostsMiddleware,
allowed_hosts=["example.com"],
on_error=custom_error_handler,
)
The on_error callback receives:
request: The StarletteRequestobjecthost: The invalid host that was rejected
It must return a Starlette Response object.
Requirements
- Python 3.14+
- FastAPI 0.128.0+
License
MIT License — see LICENSE for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Acknowledgments
Inspired by Django's ALLOWED_HOSTS setting and host validation implementation. See the Django documentation for more details on the security benefits of host validation.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_allowed_hosts-0.1.0.tar.gz.
File metadata
- Download URL: fastapi_allowed_hosts-0.1.0.tar.gz
- Upload date:
- Size: 6.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92ef00485861e33e439055f6d761e8655e91632f51335a77e12c574802d5b344
|
|
| MD5 |
049b732119b1145d11546afd6877d869
|
|
| BLAKE2b-256 |
a13d6f48b575edeadd1715c2f0e907c0be59b81c9b5a3029e0a00cbd166f8fae
|
File details
Details for the file fastapi_allowed_hosts-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fastapi_allowed_hosts-0.1.0-py3-none-any.whl
- Upload date:
- Size: 7.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a542c4ab6d774e4d022c6d495e8067d4b50e63fee045c4ed5507e8f488a6aaf
|
|
| MD5 |
e6bdfc0c429f39ae97bc433da720520b
|
|
| BLAKE2b-256 |
18a2643933c787331a9152b365f47a0c4785e6ab27a45ff15dd7beb313cb6cb8
|