Reputation: 1256
When we call an endpoint and a redirect occurs due to a missing trailing slash. As you can see in the image below, when a request is made to https://.../notifications
, the FastAPI server responds with a redirect to http://.../notifications/
I suspect that it's an app configuration issue rather than a server configuration issue. Does anyone have an idea of how to resolve this issue?
Upvotes: 59
Views: 22604
Reputation: 3867
The older answers helped me to isolate the issue, but I was not happy to solve the challenge at the infrastucture level. I wanted to be able to cover the expected behaviour by automated tests within my software project. Read my blog post for more details, why I prefer the solution described below.
Since FastAPI version 0.98.0 the framework provides a way to disable the redirect behaviour by setting the redirect_slashes
parameter to False
, which is True
by default. This works for the whole application as well as for individual routers.
from fastapi import FastAPI, APIRouter
healthcheck_router = APIRouter(redirect_slashes=False)
@healthcheck_router.get('/')
async def healtcheck() -> dict:
return {'state': 'healthy'}
def create_app() -> FastAPI:
app = FastAPI(redirect_slashes=False)
return app.include_router(healthcheck_router, prefix='/api/health')
This change can also be tested easily:
import pytest
from starlette.testclient import TestClient
from app import create_app
@pytest.fixture
def test_client() -> TestClient:
app = create_app()
return TestClient(app=app)
def test_healthcheck_returns_200(test_client: TestClient):
response = test_client.get('/api/health/')
assert response.status_code == 200
def test_healthcheck_without_trailing_slash_returns_404(test_client: TestClient):
response = test_client.get('/api/health')
assert response.status_code == 404
Upvotes: 2
Reputation: 1
I modified Karol Zlot's frontend workaround so that it's only applied in production. I added this to my main.ts file and it works great!
import { environment } from './environments/environment';
if (environment.production) {
const meta = document.createElement('meta');
meta.httpEquiv = "Content-Security-Policy";
meta.content="upgrade-insecure-requests";
document.head.appendChild(meta);
}
Upvotes: 0
Reputation: 4035
I experienced this issue when using FastAPI with react-admin.
One workaround is to change FastAPI app so it doesn't make redirects, but treats both URLs as valid API endpoints (with and without slash).
You can use this snippet wrote by malthunayan to change behaviour of APIRouter
:
from typing import Any, Callable
from fastapi import APIRouter as FastAPIRouter
from fastapi.types import DecoratedCallable
class APIRouter(FastAPIRouter):
def api_route(
self, path: str, *, include_in_schema: bool = True, **kwargs: Any
) -> Callable[[DecoratedCallable], DecoratedCallable]:
if path.endswith("/"):
path = path[:-1]
add_path = super().api_route(
path, include_in_schema=include_in_schema, **kwargs
)
alternate_path = path + "/"
add_alternate_path = super().api_route(
alternate_path, include_in_schema=False, **kwargs
)
def decorator(func: DecoratedCallable) -> DecoratedCallable:
add_alternate_path(func)
return add_path(func)
return decorator
source: https://github.com/tiangolo/fastapi/issues/2060#issuecomment-834868906
(you can also see other similar solutions in this GitHub issue)
Another workaround is to add:
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
to index.html
file in frontend. It will upgrade all requests from http
to https
(also when run locally, so it may not be the best workaround)
Upvotes: 8
Reputation: 3067
This is because your application isn't trusting the reverse proxy's headers overriding the scheme (the X-Forwarded-Proto
header that's passed when it handles a TLS request).
There's a few ways we can fix that:
If you're running the application straight from uvicorn
server, try using the flag --forwarded-allow-ips '*'
.
If you're running gunicorn
you can set as well the flag --forwarded-allow-ips="*"
.
In either application, you can additionally use the FORWARDED_ALLOW_IPS
environment variable.
Important: the *
should be used only as a test, as it'll lead your application to trust the X-Forwarded-*
headers from any source. I suggest you read uvicorn's docs and gunicorn's docs for a deeper knowledge of what to set in this flag and why.
Upvotes: 42