Update
As mentioned by @jub0bs, HTTP requests usually carry the Referer
header that contains the address from which a resource has been requested. As per MDN's documentation:
The Referer
HTTP request header contains the absolute or partial
address from which a resource has been requested. The Referer
header
allows a server to identify referring pages that people are
visiting from or where requested resources are being used. This data
can be used for analytics, logging, optimized caching, and more.
When you click a link, the Referer
contains the address of the page
that includes the link. When you make resource requests to another
domain, the Referer
contains the address of the page that uses the
requested resource.
In FastAPI, you can retrieve the request headers, as demonstrated here. Hence, you can obtain the Referer
URL in the following way:
from fastapi import FastAPI, Request
app = FastAPI()
@app.get('/')
def main(request: Request):
referer = request.headers.get('referer')
return referer
Original Answer
As per FastAPI documentation, and hence Starlette's:
Let's imagine you want to get the client's IP address/host inside of
your path operation function.
@app.get("/items/{item_id}")
def read_root(item_id: str, request: Request):
client_host = request.client.host
return {"client_host": client_host, "item_id": item_id}
Please note that if you are running behind a reverse proxy server such as nginx, you would need to run Uvicorn with --proxy-headers
flag to accept such headers (it is already enabled by default, but is restricted to only trusting connecting IPs in the forwarded-allow-ips
configuration), as well as with --forwarded-allow-ips='*'
flag to ensure that the domain socket is trusted as a source from which to proxy headers (instead of trusting headers from all IPs using the '*'
wildcard, it would be more safe to trust only proxy headers from the IP of your reverse proxy server). As per Uvicorn's docs:
--proxy-headers / --no-proxy-headers
- Enable/Disable X-Forwarded-Proto
, X-Forwarded-For
, X-Forwarded-Port
to populate remote address info. Defaults to enabled, but is restricted to only trusting connecting IPs in the forwarded-allow-ips
configuration.
--forwarded-allow-ips
- Comma separated list of IPs to trust with proxy headers. Defaults to the $FORWARDED_ALLOW_IPS
environment variable if available, or '127.0.0.1'
. A wildcard '*'
means always trust.
To ensure that the proxy forwards them, you should make sure that the X-Forwarded-For
and X-Forwarded-Proto
headers are set by the proxy (see Uvicorn's documentation, as well as this post for more details).
Assuming that requests to your API are handled in the backend of the website that you are trying to retrieve its URL/domain, and not by allowing users to issue requests to your API directly from their frontend—and hence, in that case, the client's IP address would be the website's (server/backend) IP address, and not the user's IP address, who is browsing the website—once you obtain the website's IP address (using request.client.host
, as described earlier), you can perform a reverse DNS lookup to get the website's hostname (doesn't always exist though, or does not have a meaningful name), as shown in the example below. From there, you can look up for information on the hostname or the IP address itself online. You could also create a database with every IP address (or better, hostname) you resolve for future reference.
import socket
#address = '2001:4860:4860::8888' # Google's Public DNS IPv6 address
address = '216.58.214.14' # a Google's IP address
print(socket.gethostbyaddr(address)[0])
origin_url = dict(request.scope["headers"]).get(b"referer", b"").decode()
. My requirement for this is to know whether the request is coming from our dev or prod frontend server, and, from testing, it appears that this header will always be there in our case. – Conglomerate