How to get the origin URL in FastAPI?
Question:
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?
Answers:
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.
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])
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?
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.
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. 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 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/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])