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?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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