# fastapi-ipware
A FastAPI/Starlette-native wrapper for [python-ipware](https://github.com/un33k/python-ipware) that eliminates the need for WSGI-style header conversion.
## Features
- **Zero conversion overhead** - Headers converted once at initialization, not on every request
- **FastAPI-native API** - Works directly with FastAPI/Starlette `Request` objects
- **Customizable precedence** - Easy to configure header priority for your infrastructure
- **Proxy validation** - Supports trusted proxy lists and proxy count validation
- **Thin wrapper** - Leverages all the battle-tested logic from python-ipware
## Installation
```bash
pip install fastapi-ipware
```
Or with uv:
```bash
uv add fastapi-ipware
```
## Quick Start
```python
from fastapi import FastAPI, Request
from fastapi_ipware import FastAPIIpWare
app = FastAPI()
ipware = FastAPIIpWare()
@app.get("/")
async def get_ip(request: Request):
ip, trusted = ipware.get_client_ip_from_request(request)
if ip:
return {
"ip": str(ip),
"trusted": trusted,
"is_public": ip.is_global,
"is_private": ip.is_private,
}
return {"error": "Could not determine IP"}
```
## Usage
### Basic Usage
```python
from fastapi_ipware import FastAPIIpWare
# Use default configuration (optimized for FastAPI/cloud deployments)
ipware = FastAPIIpWare()
ip, trusted = ipware.get_client_ip_from_request(request)
```
### Custom Header Precedence
Customize which headers are checked and in what order:
```python
# Prioritize Cloudflare headers
ipware = FastAPIIpWare(
precedence=(
"CF-Connecting-IP",
"X-Forwarded-For",
"X-Real-IP",
)
)
# NGINX configuration
ipware = FastAPIIpWare(
precedence=(
"X-Real-IP",
"X-Forwarded-For",
)
)
```
### Proxy Count Validation
Validate that requests pass through the expected number of proxies:
```python
# Expect exactly 1 proxy (e.g., AWS ALB)
ipware = FastAPIIpWare(proxy_count=1)
# In strict mode, must be exactly 1 proxy
ip, trusted = ipware.get_client_ip_from_request(request, strict=True)
# In non-strict mode, allow 1 or more proxies
ip, trusted = ipware.get_client_ip_from_request(request, strict=False)
```
### Trusted Proxy List
Validate that requests pass through specific trusted proxies:
```python
# Trust specific proxy IP prefixes
ipware = FastAPIIpWare(
proxy_list=["10.0.", "10.1."] # AWS internal IPs
)
ip, trusted = ipware.get_client_ip_from_request(request)
# trusted=True only if request came through specified proxies
```
### Combined Validation
Use both proxy count and trusted proxy list:
```python
# Expect 1 proxy from a specific IP range
ipware = FastAPIIpWare(
proxy_count=1,
proxy_list=["10.0."]
)
```
## Real-World Examples
### AWS Application Load Balancer
```python
ipware = FastAPIIpWare(
proxy_count=1,
proxy_list=["10.0."] # Your VPC CIDR
)
```
### Cloudflare
```python
ipware = FastAPIIpWare(
precedence=("CF-Connecting-IP",)
)
```
### Multiple Proxies (CDN + Load Balancer)
```python
ipware = FastAPIIpWare(
proxy_count=2,
proxy_list=["10.1.", "10.2."] # CDN and LB IPs
)
```
### NGINX Reverse Proxy
```python
ipware = FastAPIIpWare(
precedence=("X-Real-IP", "X-Forwarded-For"),
proxy_count=1
)
```
## IP Address Types
The returned IP address object has useful properties:
```python
ip, _ = ipware.get_client_ip_from_request(request)
if ip:
print(f"Is public: {ip.is_global}")
print(f"Is private: {ip.is_private}")
print(f"Is loopback: {ip.is_loopback}")
print(f"Is multicast: {ip.is_multicast}")
```
python-ipware automatically prefers:
1. Public (global) IPs first
2. Private IPs second
3. Loopback IPs last
## Default Header Precedence
The default precedence order is optimized for modern cloud deployments. See the [default precedence configuration](https://github.com/iloveitaly/fastapi-ipware/blob/main/fastapi_ipware/__init__.py#L48-L58) in the source code.
## Why fastapi-ipware?
`python-ipware` expects WSGI-style headers (`HTTP_X_FORWARDED_FOR`), but FastAPI uses natural header names (`X-Forwarded-For`). This wrapper handles the conversion automatically so you don't have to.
## Contributing
Contributions welcome! This is a thin wrapper around python-ipware, so most IP detection logic lives there.
## License
[MIT License](LICENSE.md)
## Credits
Built on top of [python-ipware](https://github.com/un33k/python-ipware) by un33k.
Raw data
{
"_id": null,
"home_page": null,
"name": "fastapi-ipware",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "fastapi, starlette, ipware, ip-address, client-ip, proxy, x-forwarded-for",
"author": "Michael Bianco",
"author_email": "Michael Bianco <mike@mikebian.co>",
"download_url": "https://files.pythonhosted.org/packages/24/4e/4bf916385541ec40c150ba920098f467d6187f9bf7f7e15460a0488b5447/fastapi_ipware-0.1.1.tar.gz",
"platform": null,
"description": "# fastapi-ipware\n\nA FastAPI/Starlette-native wrapper for [python-ipware](https://github.com/un33k/python-ipware) that eliminates the need for WSGI-style header conversion.\n\n## Features\n\n- **Zero conversion overhead** - Headers converted once at initialization, not on every request\n- **FastAPI-native API** - Works directly with FastAPI/Starlette `Request` objects\n- **Customizable precedence** - Easy to configure header priority for your infrastructure\n- **Proxy validation** - Supports trusted proxy lists and proxy count validation\n- **Thin wrapper** - Leverages all the battle-tested logic from python-ipware\n\n## Installation\n\n```bash\npip install fastapi-ipware\n```\n\nOr with uv:\n\n```bash\nuv add fastapi-ipware\n```\n\n## Quick Start\n\n```python\nfrom fastapi import FastAPI, Request\nfrom fastapi_ipware import FastAPIIpWare\n\napp = FastAPI()\nipware = FastAPIIpWare()\n\n@app.get(\"/\")\nasync def get_ip(request: Request):\n ip, trusted = ipware.get_client_ip_from_request(request)\n \n if ip:\n return {\n \"ip\": str(ip),\n \"trusted\": trusted,\n \"is_public\": ip.is_global,\n \"is_private\": ip.is_private,\n }\n \n return {\"error\": \"Could not determine IP\"}\n```\n\n## Usage\n\n### Basic Usage\n\n```python\nfrom fastapi_ipware import FastAPIIpWare\n\n# Use default configuration (optimized for FastAPI/cloud deployments)\nipware = FastAPIIpWare()\n\nip, trusted = ipware.get_client_ip_from_request(request)\n```\n\n### Custom Header Precedence\n\nCustomize which headers are checked and in what order:\n\n```python\n# Prioritize Cloudflare headers\nipware = FastAPIIpWare(\n precedence=(\n \"CF-Connecting-IP\",\n \"X-Forwarded-For\",\n \"X-Real-IP\",\n )\n)\n\n# NGINX configuration\nipware = FastAPIIpWare(\n precedence=(\n \"X-Real-IP\",\n \"X-Forwarded-For\",\n )\n)\n```\n\n### Proxy Count Validation\n\nValidate that requests pass through the expected number of proxies:\n\n```python\n# Expect exactly 1 proxy (e.g., AWS ALB)\nipware = FastAPIIpWare(proxy_count=1)\n\n# In strict mode, must be exactly 1 proxy\nip, trusted = ipware.get_client_ip_from_request(request, strict=True)\n\n# In non-strict mode, allow 1 or more proxies\nip, trusted = ipware.get_client_ip_from_request(request, strict=False)\n```\n\n### Trusted Proxy List\n\nValidate that requests pass through specific trusted proxies:\n\n```python\n# Trust specific proxy IP prefixes\nipware = FastAPIIpWare(\n proxy_list=[\"10.0.\", \"10.1.\"] # AWS internal IPs\n)\n\nip, trusted = ipware.get_client_ip_from_request(request)\n\n# trusted=True only if request came through specified proxies\n```\n\n### Combined Validation\n\nUse both proxy count and trusted proxy list:\n\n```python\n# Expect 1 proxy from a specific IP range\nipware = FastAPIIpWare(\n proxy_count=1,\n proxy_list=[\"10.0.\"]\n)\n```\n\n## Real-World Examples\n\n### AWS Application Load Balancer\n\n```python\nipware = FastAPIIpWare(\n proxy_count=1,\n proxy_list=[\"10.0.\"] # Your VPC CIDR\n)\n```\n\n### Cloudflare\n\n```python\nipware = FastAPIIpWare(\n precedence=(\"CF-Connecting-IP\",)\n)\n```\n\n### Multiple Proxies (CDN + Load Balancer)\n\n```python\nipware = FastAPIIpWare(\n proxy_count=2,\n proxy_list=[\"10.1.\", \"10.2.\"] # CDN and LB IPs\n)\n```\n\n### NGINX Reverse Proxy\n\n```python\nipware = FastAPIIpWare(\n precedence=(\"X-Real-IP\", \"X-Forwarded-For\"),\n proxy_count=1\n)\n```\n\n## IP Address Types\n\nThe returned IP address object has useful properties:\n\n```python\nip, _ = ipware.get_client_ip_from_request(request)\n\nif ip:\n print(f\"Is public: {ip.is_global}\")\n print(f\"Is private: {ip.is_private}\")\n print(f\"Is loopback: {ip.is_loopback}\")\n print(f\"Is multicast: {ip.is_multicast}\")\n```\n\npython-ipware automatically prefers:\n1. Public (global) IPs first\n2. Private IPs second\n3. Loopback IPs last\n\n## Default Header Precedence\n\nThe default precedence order is optimized for modern cloud deployments. See the [default precedence configuration](https://github.com/iloveitaly/fastapi-ipware/blob/main/fastapi_ipware/__init__.py#L48-L58) in the source code.\n\n## Why fastapi-ipware?\n\n`python-ipware` expects WSGI-style headers (`HTTP_X_FORWARDED_FOR`), but FastAPI uses natural header names (`X-Forwarded-For`). This wrapper handles the conversion automatically so you don't have to.\n\n## Contributing\n\nContributions welcome! This is a thin wrapper around python-ipware, so most IP detection logic lives there.\n\n## License\n\n[MIT License](LICENSE.md)\n\n## Credits\n\nBuilt on top of [python-ipware](https://github.com/un33k/python-ipware) by un33k.\n",
"bugtrack_url": null,
"license": null,
"summary": "FastAPI/Starlette-native wrapper for python-ipware to get client IP addresses",
"version": "0.1.1",
"project_urls": {
"Repository": "https://github.com/iloveitaly/fastapi-ipware"
},
"split_keywords": [
"fastapi",
" starlette",
" ipware",
" ip-address",
" client-ip",
" proxy",
" x-forwarded-for"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "e657a5bb5d4407a11467b5cd5dd46803133a5f7e213ae25d5939827f426e45c3",
"md5": "8fd94f5b3b5fe6189bd201db5d2b06c6",
"sha256": "eb47c9dfd3b7962aac81b1fb603c4f5cda5450c905b47773d2ee625b1ecb2277"
},
"downloads": -1,
"filename": "fastapi_ipware-0.1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "8fd94f5b3b5fe6189bd201db5d2b06c6",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 4877,
"upload_time": "2025-11-01T18:33:59",
"upload_time_iso_8601": "2025-11-01T18:33:59.714080Z",
"url": "https://files.pythonhosted.org/packages/e6/57/a5bb5d4407a11467b5cd5dd46803133a5f7e213ae25d5939827f426e45c3/fastapi_ipware-0.1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "244e4bf916385541ec40c150ba920098f467d6187f9bf7f7e15460a0488b5447",
"md5": "594afd7a6fca2d57f07c85536d4d61af",
"sha256": "2e2e638e57b8e0f3c08cbea32fca96713302cdcec380b6ea9eb216eae949eb3f"
},
"downloads": -1,
"filename": "fastapi_ipware-0.1.1.tar.gz",
"has_sig": false,
"md5_digest": "594afd7a6fca2d57f07c85536d4d61af",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 4054,
"upload_time": "2025-11-01T18:34:00",
"upload_time_iso_8601": "2025-11-01T18:34:00.856206Z",
"url": "https://files.pythonhosted.org/packages/24/4e/4bf916385541ec40c150ba920098f467d6187f9bf7f7e15460a0488b5447/fastapi_ipware-0.1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-11-01 18:34:00",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "iloveitaly",
"github_project": "fastapi-ipware",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "fastapi-ipware"
}