I have a FastAPI application that is behind a NextJS reverse proxy. I'm using NextJS rewrites, which sets the x-forwarded-for
header to the externally-visible hostname and port. The rewrite looks like this:
rewrites: async () => [
{
source: "/api/:slug*",
destination: "http://backend:8000/:slug*"
}
]
The whole lot is running in a docker stack (which is where hostnames like backend
come from) and I end up with headers like this:
host: backend:8000
x-forwarded-host: localhost:3000
I'm then emailing a link from the FastAPI application. I construct the URL like this:
@app.get("/verify")
async def verify(token: str):
...
@app.post("/signup")
async def signup(body: SignupRequest, request: Request) -> str:
user = add_user(body.username, body.email)
token = user.get_signup_token()
url = request.url_for("verify").include_query_params(token=token)
email_verification(body.email, url)
return ""
I've set up FastAPI with root_path="/api"
so that the path is rewritten correctly.
The resulting url is http://backend:8000/api/verify
. I want it to be http://localhost:3000/api/verify
(ie to have the actual hosted URL rather than the intra-docker-stack URL).
I've tried adding a middleware like this:
@app.middleware("http")
async def rewrite_host_header(request: Request, call_next):
if "x-forwarded-host" in request.headers:
request.headers["host"] = request.headers["x-forwarded-host"]
return await call_next(request)
but this doesn't seem to make a difference. I've also tried adding request.url = request.url.replace(host=request.headers["x-forwarded-for"])
but this also doesn't change the output of request.url_for(...)
.
How am I meant to configure this so that URLs are emitted with the right scheme, hostname and port?
Edit to add: I've tried also setting X-Forwarded-Proto
, X-Forwarded-Port
, X-Forwarded-Prefix
and X-Forwarded-For
, making requests directly to FastAPI using curl
and so cutting out the NextJS step. None of it makes any difference to the URLs that url_for()
emits.
I have a FastAPI application that is behind a NextJS reverse proxy. I'm using NextJS rewrites, which sets the x-forwarded-for
header to the externally-visible hostname and port. The rewrite looks like this:
rewrites: async () => [
{
source: "/api/:slug*",
destination: "http://backend:8000/:slug*"
}
]
The whole lot is running in a docker stack (which is where hostnames like backend
come from) and I end up with headers like this:
host: backend:8000
x-forwarded-host: localhost:3000
I'm then emailing a link from the FastAPI application. I construct the URL like this:
@app.get("/verify")
async def verify(token: str):
...
@app.post("/signup")
async def signup(body: SignupRequest, request: Request) -> str:
user = add_user(body.username, body.email)
token = user.get_signup_token()
url = request.url_for("verify").include_query_params(token=token)
email_verification(body.email, url)
return ""
I've set up FastAPI with root_path="/api"
so that the path is rewritten correctly.
The resulting url is http://backend:8000/api/verify
. I want it to be http://localhost:3000/api/verify
(ie to have the actual hosted URL rather than the intra-docker-stack URL).
I've tried adding a middleware like this:
@app.middleware("http")
async def rewrite_host_header(request: Request, call_next):
if "x-forwarded-host" in request.headers:
request.headers["host"] = request.headers["x-forwarded-host"]
return await call_next(request)
but this doesn't seem to make a difference. I've also tried adding request.url = request.url.replace(host=request.headers["x-forwarded-for"])
but this also doesn't change the output of request.url_for(...)
.
How am I meant to configure this so that URLs are emitted with the right scheme, hostname and port?
Edit to add: I've tried also setting X-Forwarded-Proto
, X-Forwarded-Port
, X-Forwarded-Prefix
and X-Forwarded-For
, making requests directly to FastAPI using curl
and so cutting out the NextJS step. None of it makes any difference to the URLs that url_for()
emits.
1 Answer
Reset to default 0I think the issue is that Uvicorn (if you're using it) does not trust proxy headers by default unless specified.
According to the FastAPI documentation about deploying FastAPI on Docker behind a proxy, you need to enable proxy headers.
Additionally, Starlette provides information about Uvicorn middleware for handling proxy header. The Uvicorn GitHub code also provides insight into how proxy headers are processed.
host
fromx-forwarded-for
. – Tom Commented Feb 3 at 20:43