@@ -93,6 +93,26 @@ async def close_access_log(app: FastAPI) -> None:
9393 await pool .close ()
9494
9595
96+ def _get_client_ip (request : Request ) -> Optional [str ]:
97+ """Extracts the client IP address from request headers or client info."""
98+ # Check X-Forwarded-For header (common for reverse proxies)
99+ forwarded_for = request .headers .get ("X-Forwarded-For" )
100+ if forwarded_for :
101+ # X-Forwarded-For can be a comma-separated list; the first one is the original client
102+ return forwarded_for .split ("," )[0 ].strip ()
103+
104+ # Check X-Real-IP header
105+ real_ip = request .headers .get ("X-Real-IP" )
106+ if real_ip :
107+ return real_ip
108+
109+ # Fallback to request.client.host
110+ if request .client :
111+ return request .client .host
112+
113+ return None
114+
115+
96116async def log_request (
97117 request : Request ,
98118 response : Optional [Response ],
@@ -108,7 +128,8 @@ async def log_request(
108128 if request .url .path in config .exclude_paths :
109129 return
110130
111- ip_hash = _hash_ip (getattr (request .client , "host" , None ), config .ip_salt )
131+ client_ip = _get_client_ip (request )
132+ ip_hash = _hash_ip (client_ip , config .ip_salt )
112133 user_agent = request .headers .get ("user-agent" )
113134 referrer = request .headers .get ("referer" ) or request .headers .get ("referrer" )
114135
@@ -141,7 +162,8 @@ async def log_page_view(request: Request, page_path: str) -> None:
141162 if not config or not pool :
142163 return
143164
144- ip_hash = _hash_ip (getattr (request .client , "host" , None ), config .ip_salt )
165+ client_ip = _get_client_ip (request )
166+ ip_hash = _hash_ip (client_ip , config .ip_salt )
145167 user_agent = request .headers .get ("user-agent" )
146168 referrer = request .headers .get ("referer" ) or request .headers .get ("referrer" )
147169
0 commit comments