Skip to content

ProxyFixMiddleware doesn't fix Starlette/FastAPI #367

Description

@MajorDallas

I wrapped my FastAPI app according to the docs:

fast_app = FastAPI(...)
# add routers, mounts, other middlewares...
# The final middleware is ProxyFixMiddleware
app = ProxyFixMiddleware(fast_app, mode='legacy')

# app is targeted by the hypercorn cli or started via trio when the module is executed

(The docs should probably make it clear it must be done this way: I lost almost two hours wondering why it wasn't doing anything with FastAPI.add_middleware before coming across #179)

I have a view function:

@router.get(
    "/signin",
    status_code=HTTPStatus.TEMPORARY_REDIRECT,
)
async def sign_in(req: Request):
    logger.debug(req.scope)
    async with AsyncOAuth2Client(
        b.config.OAUTH["client_id"],
        redirect_uri=str(req.url_for(authorize.__name__)),
        scope=["openid", "email", "profile"],
        **b.config.OAUTH["httpxkw"],
    ) as client:
        uri, state = client.create_authorization_url(
            str(b.config.OAUTH["authorization_endpoint"])
        )
    return RedirectResponse(uri)

My NGINX location block has these settings:

    proxy_pass http://unix:/run/hypercorn.sock;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_http_version 1.1;

The problem started with req.url_for: it keeps rendering a URL with http:// and breaking OAuth.

Hypercorn correctly logs that X-Forwarded-Proto is https. However, when I log req.scope within the app, pretty much everything I expected the middleware to fix is set as if the middleware weren't present at all:

{
  'type': 'http',
  'http_version': '1.1', 
  'asgi': {'spec_version': '2.1', 'version': '3.0'},
  'method': 'GET',
  'scheme': 'http',  # WRONG
  ...,
  'client': None,  # WRONG -- should equal X-Forwarded-For
  'server': None,
  'headers': [
    (b'host', b'<server_name in nginx and hypercorn.toml>),  # Correct
    (b'x-forwarded-proto', b'https'),
    (b'x-forwarded-for', b'<my ip>'), ...
  ],
  ...
}

So Hypercorn itself clearly sees the proxy headers and is consuming them itself more or less correctly, but it's not adding that information to the Scope in a way that Starlette can see it.

I hope this is just my error, because that would be easy to fix. Maybe I need to wrap the Router instead of the FastAPI? Or maybe it should be the first instead of the last middleware?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions