fastapi-ipware 0.1.0__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fastapi-ipware
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: FastAPI/Starlette-native wrapper for python-ipware to get client IP addresses
5
5
  Keywords: fastapi,starlette,ipware,ip-address,client-ip,proxy,x-forwarded-for
6
6
  Author: Michael Bianco
@@ -15,28 +15,28 @@ Description-Content-Type: text/markdown
15
15
 
16
16
  A FastAPI/Starlette-native wrapper for [python-ipware](https://github.com/un33k/python-ipware) that eliminates the need for WSGI-style header conversion.
17
17
 
18
+ `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.
19
+
20
+ Also, 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. This is different from the
21
+ default ordering ipware which deprioritizes platform-specific headers, which is often the wrong ordering if you are using something like CloudFlare.
22
+
18
23
  ## Features
19
24
 
20
25
  - **Zero conversion overhead** - Headers converted once at initialization, not on every request
21
26
  - **FastAPI-native API** - Works directly with FastAPI/Starlette `Request` objects
22
27
  - **Customizable precedence** - Easy to configure header priority for your infrastructure
23
28
  - **Proxy validation** - Supports trusted proxy lists and proxy count validation
24
- - **Thin wrapper** - Leverages all the battle-tested logic from python-ipware
25
29
 
26
30
  ## Installation
27
31
 
28
- ```bash
29
- pip install fastapi-ipware
30
- ```
31
-
32
- Or with uv:
33
-
34
32
  ```bash
35
33
  uv add fastapi-ipware
36
34
  ```
37
35
 
38
36
  ## Quick Start
39
37
 
38
+ [Take a look at this example usage.](https://github.com/iloveitaly/structlog-config/blob/1e32f7afd206485b1356b322596cc19cb0bef8ba/structlog_config/fastapi_access_logger.py#L57-L72)
39
+
40
40
  ```python
41
41
  from fastapi import FastAPI, Request
42
42
  from fastapi_ipware import FastAPIIpWare
@@ -137,43 +137,6 @@ ipware = FastAPIIpWare(
137
137
  )
138
138
  ```
139
139
 
140
- ## Real-World Examples
141
-
142
- ### AWS Application Load Balancer
143
-
144
- ```python
145
- ipware = FastAPIIpWare(
146
- proxy_count=1,
147
- proxy_list=["10.0."] # Your VPC CIDR
148
- )
149
- ```
150
-
151
- ### Cloudflare
152
-
153
- ```python
154
- ipware = FastAPIIpWare(
155
- precedence=("CF-Connecting-IP",)
156
- )
157
- ```
158
-
159
- ### Multiple Proxies (CDN + Load Balancer)
160
-
161
- ```python
162
- ipware = FastAPIIpWare(
163
- proxy_count=2,
164
- proxy_list=["10.1.", "10.2."] # CDN and LB IPs
165
- )
166
- ```
167
-
168
- ### NGINX Reverse Proxy
169
-
170
- ```python
171
- ipware = FastAPIIpWare(
172
- precedence=("X-Real-IP", "X-Forwarded-For"),
173
- proxy_count=1
174
- )
175
- ```
176
-
177
140
  ## IP Address Types
178
141
 
179
142
  The returned IP address object has useful properties:
@@ -189,26 +152,16 @@ if ip:
189
152
  ```
190
153
 
191
154
  python-ipware automatically prefers:
155
+
192
156
  1. Public (global) IPs first
193
157
  2. Private IPs second
194
158
  3. Loopback IPs last
195
159
 
196
- ## Default Header Precedence
197
-
198
- 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.
199
-
200
- ## Why fastapi-ipware?
201
-
202
- `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.
203
-
204
- ## Contributing
205
-
206
- Contributions welcome! This is a thin wrapper around python-ipware, so most IP detection logic lives there.
207
-
208
160
  ## License
209
161
 
210
162
  [MIT License](LICENSE.md)
211
163
 
212
164
  ## Credits
213
165
 
214
- Built on top of [python-ipware](https://github.com/un33k/python-ipware) by un33k.
166
+ - Built on top of [python-ipware](https://github.com/un33k/python-ipware) by un33k.
167
+ - https://github.com/long2ice/fastapi-limiter/blob/8d179c058fa2aaf98f3450c9026a7300ae2b3bdd/fastapi_limiter/__init__.py#L11
@@ -2,28 +2,28 @@
2
2
 
3
3
  A FastAPI/Starlette-native wrapper for [python-ipware](https://github.com/un33k/python-ipware) that eliminates the need for WSGI-style header conversion.
4
4
 
5
+ `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.
6
+
7
+ Also, 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. This is different from the
8
+ default ordering ipware which deprioritizes platform-specific headers, which is often the wrong ordering if you are using something like CloudFlare.
9
+
5
10
  ## Features
6
11
 
7
12
  - **Zero conversion overhead** - Headers converted once at initialization, not on every request
8
13
  - **FastAPI-native API** - Works directly with FastAPI/Starlette `Request` objects
9
14
  - **Customizable precedence** - Easy to configure header priority for your infrastructure
10
15
  - **Proxy validation** - Supports trusted proxy lists and proxy count validation
11
- - **Thin wrapper** - Leverages all the battle-tested logic from python-ipware
12
16
 
13
17
  ## Installation
14
18
 
15
- ```bash
16
- pip install fastapi-ipware
17
- ```
18
-
19
- Or with uv:
20
-
21
19
  ```bash
22
20
  uv add fastapi-ipware
23
21
  ```
24
22
 
25
23
  ## Quick Start
26
24
 
25
+ [Take a look at this example usage.](https://github.com/iloveitaly/structlog-config/blob/1e32f7afd206485b1356b322596cc19cb0bef8ba/structlog_config/fastapi_access_logger.py#L57-L72)
26
+
27
27
  ```python
28
28
  from fastapi import FastAPI, Request
29
29
  from fastapi_ipware import FastAPIIpWare
@@ -124,43 +124,6 @@ ipware = FastAPIIpWare(
124
124
  )
125
125
  ```
126
126
 
127
- ## Real-World Examples
128
-
129
- ### AWS Application Load Balancer
130
-
131
- ```python
132
- ipware = FastAPIIpWare(
133
- proxy_count=1,
134
- proxy_list=["10.0."] # Your VPC CIDR
135
- )
136
- ```
137
-
138
- ### Cloudflare
139
-
140
- ```python
141
- ipware = FastAPIIpWare(
142
- precedence=("CF-Connecting-IP",)
143
- )
144
- ```
145
-
146
- ### Multiple Proxies (CDN + Load Balancer)
147
-
148
- ```python
149
- ipware = FastAPIIpWare(
150
- proxy_count=2,
151
- proxy_list=["10.1.", "10.2."] # CDN and LB IPs
152
- )
153
- ```
154
-
155
- ### NGINX Reverse Proxy
156
-
157
- ```python
158
- ipware = FastAPIIpWare(
159
- precedence=("X-Real-IP", "X-Forwarded-For"),
160
- proxy_count=1
161
- )
162
- ```
163
-
164
127
  ## IP Address Types
165
128
 
166
129
  The returned IP address object has useful properties:
@@ -176,26 +139,16 @@ if ip:
176
139
  ```
177
140
 
178
141
  python-ipware automatically prefers:
142
+
179
143
  1. Public (global) IPs first
180
144
  2. Private IPs second
181
145
  3. Loopback IPs last
182
146
 
183
- ## Default Header Precedence
184
-
185
- 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.
186
-
187
- ## Why fastapi-ipware?
188
-
189
- `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.
190
-
191
- ## Contributing
192
-
193
- Contributions welcome! This is a thin wrapper around python-ipware, so most IP detection logic lives there.
194
-
195
147
  ## License
196
148
 
197
149
  [MIT License](LICENSE.md)
198
150
 
199
151
  ## Credits
200
152
 
201
- Built on top of [python-ipware](https://github.com/un33k/python-ipware) by un33k.
153
+ - Built on top of [python-ipware](https://github.com/un33k/python-ipware) by un33k.
154
+ - https://github.com/long2ice/fastapi-limiter/blob/8d179c058fa2aaf98f3450c9026a7300ae2b3bdd/fastapi_limiter/__init__.py#L11
@@ -44,28 +44,46 @@ class FastAPIIpWare(IpWare):
44
44
  Used to validate and extract the correct client IP.
45
45
  proxy_list: List of trusted proxy IP prefixes (e.g., ["10.1.", "10.2.3"]).
46
46
  """
47
- # FastAPI-native precedence using actual header names with dashes
47
+ # Header precedence order: Provider-specific headers before generic ones
48
+ #
49
+ # We prioritize provider-specific headers (CF-Connecting-IP, True-Client-IP, etc.)
50
+ # over generic headers (X-Forwarded-For, X-Real-IP) because:
51
+ # 1. Provider headers are set by trusted CDN/proxy infrastructure
52
+ # 2. They cannot be spoofed by clients
53
+ # 3. They represent the most reliable source of client IP information
54
+ #
55
+ # Generic headers like X-Forwarded-For can be set by anyone and are easier
56
+ # to manipulate, so they should only be used as fallbacks.
48
57
  if precedence is None:
49
58
  precedence = (
50
- "X-Forwarded-For", # Most common, used by AWS ELB, nginx, etc.
51
- "X-Real-IP", # NGINX
59
+ # Provider-specific headers (highest reliability)
52
60
  "CF-Connecting-IP", # Cloudflare
53
61
  "True-Client-IP", # Cloudflare Enterprise
54
62
  "Fastly-Client-IP", # Fastly, Firebase
55
63
  "X-Client-IP", # Microsoft Azure
56
64
  "X-Cluster-Client-IP", # Rackspace Cloud Load Balancers
57
- "Forwarded-For", # RFC 7239
58
- "Forwarded", # RFC 7239
65
+ # Generic headers (fallback)
66
+ "X-Forwarded-For", # Generic, used by AWS ELB, nginx, etc.
67
+ "X-Real-IP", # NGINX
68
+ # NOTE: Upstream python-ipware treats Forwarded headers as plain IP lists.
69
+ # RFC 7239 `Forwarded` parameters (for=) are not parsed.
70
+ "Forwarded-For", # RFC 7239 (plain IP list only)
71
+ "Forwarded", # RFC 7239 (plain IP list only)
59
72
  "Client-IP", # Akamai, Cloudflare
73
+ "REMOTE_ADDR", # Direct connection fallback
60
74
  )
61
75
 
62
76
  # Store FastAPI-style precedence for reference
63
77
  self._fastapi_precedence = precedence
64
78
 
65
- # Convert user-friendly header names (with dashes) to WSGI format once
66
- # This happens only at initialization, not on every request
79
+ # Convert user-friendly header names (with dashes) to WSGI format once.
80
+ # REMOTE_ADDR is a WSGI/ASGI convention, not an HTTP header, so it must
81
+ # not be prefixed with HTTP_.
67
82
  wsgi_precedence = tuple(
68
- f"HTTP_{header.upper().replace('-', '_')}" for header in precedence
83
+ "REMOTE_ADDR"
84
+ if header == "REMOTE_ADDR"
85
+ else f"HTTP_{header.upper().replace('-', '_')}"
86
+ for header in precedence
69
87
  )
70
88
 
71
89
  # Initialize parent class with WSGI-style headers
@@ -104,6 +122,11 @@ class FastAPIIpWare(IpWare):
104
122
  for name, value in request.headers.items()
105
123
  }
106
124
 
125
+ if request.client:
126
+ # NOTE: python-ipware falls back to REMOTE_ADDR when no headers match.
127
+ # Map Starlette's connection info to that expected key.
128
+ meta["REMOTE_ADDR"] = request.client.host
129
+
107
130
  return self.get_client_ip(meta, strict=strict)
108
131
 
109
132
 
@@ -1,21 +1,26 @@
1
1
  [project]
2
2
  name = "fastapi-ipware"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "FastAPI/Starlette-native wrapper for python-ipware to get client IP addresses"
5
- keywords = ["fastapi", "starlette", "ipware", "ip-address", "client-ip", "proxy", "x-forwarded-for"]
5
+ keywords = [
6
+ "fastapi",
7
+ "starlette",
8
+ "ipware",
9
+ "ip-address",
10
+ "client-ip",
11
+ "proxy",
12
+ "x-forwarded-for",
13
+ ]
6
14
  readme = "README.md"
7
15
  requires-python = ">=3.10"
8
- dependencies = [
9
- "python-ipware>=3.0.0",
10
- "starlette>=0.49.1",
11
- ]
16
+ dependencies = ["python-ipware>=3.0.0", "starlette>=0.49.1"]
12
17
  authors = [{ name = "Michael Bianco", email = "mike@mikebian.co" }]
13
18
  urls = { "Repository" = "https://github.com/iloveitaly/fastapi-ipware" }
14
19
 
15
20
  # additional packaging information: https://packaging.python.org/en/latest/specifications/core-metadata/#license
16
21
 
17
22
  [build-system]
18
- requires = ["uv_build>=0.8.11,<0.9.0"]
23
+ requires = ["uv_build>=0.9.0,<0.10.0"]
19
24
  build-backend = "uv_build"
20
25
 
21
26
  [tool.uv.build-backend]
@@ -23,8 +28,16 @@ build-backend = "uv_build"
23
28
  module-root = ""
24
29
 
25
30
  [dependency-groups]
26
- dev = ["pytest>=8.3.3","pyright>=1.1.380","ruff>=0.8.0"]
31
+ dev = [
32
+ "pytest>=8.3.3",
33
+ "pyright[nodejs]>=1.1.380",
34
+ "ruff>=0.8.0",
35
+ "fastapi>=0.128.0",
36
+ "httpx>=0.28.1",
37
+ ]
27
38
 
28
39
  [tool.pyright]
29
- exclude = ["example.py", ".venv", "**/__pycache__"]
40
+ exclude = ["examples/", "playground/", "tmp/", ".venv/", "tests/"]
30
41
 
42
+ [tool.ruff]
43
+ extend-exclude = ["playground.py", "playground/"]