Reputation: 4545
Is it possible to get the URL that a request came from in FastAPI?
For example, if I have an endpoint that is requested at api.mysite.com/endpoint
and a request is made to this endpoint from www.othersite.com
, is there a way that I can retrieve the string "www.othersite.com" in my endpoint function?
Upvotes: 5
Views: 15041
Reputation: 34109
As mentioned by @jub0bs, HTTP requests usually carry the Referer
header that contains the address from which a resource has been requested—even though, you should always be aware that the Referer
header, like every other header, could easily be modified/spoofed on client side, in order to prevent the server from obtaining accurate data on the identity of the website visited by the user. As per MDN's documentation:
The
Referer
HTTP request header contains the absolute or partial address from which a resource has been requested. TheReferer
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, theReferer
contains the address of the page that uses the requested resource.
In FastAPI, you could retrieve the request headers, as demonstrated here. Hence, you could 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
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 (see the relevant implementation) 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/DisableX-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 theforwarded-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])
Upvotes: 1
Reputation: 66244
The premise of the question, which could be formulated as
a server can identify the URL from where a request came
is misguided. True, some HTTP requests (especially some of the requests issued by browsers) carry an Origin
header and/or a Referer
[sic] header. Also, the Forwarded
header, if present, contains information about where the request was issued. However, nothing in the HTTP specification requires that requests in general advertise where they came from.
Therefore, whether with FastAPI or some other server technology, there's no definite way of knowing where a request came from.
Upvotes: 5