Heroku drops x-rapidapi-proxy-secret header during request forwarding

I built a REST API and deployed it to Heroku. Then I published it on RapidAPI marketplace. RapidAPI sends a special header called x-rapidapi-proxy-secret that I use to make sure requests only come through their platform.

I added middleware to check this header:

api = FastAPI()
proxy_secret = os.environ.get("SECRET_KEY", None)

@api.middleware("http")
async def validate_rapidapi_header(req: Request, next_call):
    if proxy_secret:
        request_headers = req.headers
        print(request_headers)
        if (
                "X-RapidAPI-Proxy-Secret" not in request_headers
                or request_headers["X-RapidAPI-Proxy-Secret"] != proxy_secret
        ):
            return PlainTextResponse(
                "API access denied", status_code=403
            )

    result = await next_call(req)
    return result

The problem is that when RapidAPI sends a request to my Heroku app, the first request has the x-rapidapi-proxy-secret header. But then Heroku does an internal redirect and the second request is missing this header completely.

Here’s what I see in my logs:

2022-05-13T09:49:58.117345+00:00 app[web.1]: Headers({'host': 'myapi.herokuapp.com', 'x-rapidapi-proxy-secret': '**hidden**', 'x-rapidapi-user': 'testuser'})
2022-05-13T09:49:58.117942+00:00 app[web.1]: 176.12.151.31:0 - "GET /data?query=test" 307

2022-05-13T09:49:59.163686+00:00 app[web.1]: Headers({'host': 'myapi.herokuapp.com', 'x-rapidapi-key': '**hidden**'})
2022-05-13T09:49:59.163820+00:00 app[web.1]: 176.12.151.31:0 - "GET /data/?query=test HTTP/1.1" 403

The first request has the proxy secret header but gets a 307 redirect. The second request after redirect is missing the header so my middleware blocks it with 403.

Is there any way to make Heroku keep all headers when it redirects requests? Or should I handle this proxy secret validation differently?

Yeah, this is a known issue with Heroku’s HTTP stack. HTTP clients strip custom headers during redirects for security reasons, and Heroku’s router does the same thing. I’ve hit this exact problem when deploying APIs behind proxy services.

Easiest fix is to modify your FastAPI app so it handles both trailing slash scenarios without redirects. Set redirect_slashes=False when you create your FastAPI instance, then explicitly define routes for both /data and /data/ endpoints. This kills the 307 redirect completely.

You could also implement token-based auth where the first successful request with the proxy secret generates a short-lived JWT token for subsequent requests. But that adds complexity and might not play nice with RapidAPI’s expected flow.

Heroku’s redirect behavior can’t be configured, so avoiding redirects altogether is your best bet.

This happens because you’re hitting FastAPI’s automatic trailing slash redirect. When RapidAPI calls /data?query=test, FastAPI redirects to /data/?query=test with a 307 status. Most HTTP clients (including Heroku’s routing layer) drop non-standard headers during redirects - that’s just how HTTP works. I hit this exact issue last year with a different API gateway. What worked for me was adding a custom route handler that catches both URL patterns before the redirect happens. You can create @api.get("/data") and @api.get("/data/") pointing to the same function, or use route parameters for a more generic fix. You could also move your validation logic from middleware to individual route handlers. That way you’re only checking auth on actual endpoint calls, not every request including redirects.

I feel you on the Heroku redirect issue! Setting redirect_slashes=False is smart - it kills that annoying 307 redirect. Just make sure you set routes for both /data and /data/ or you’ll hit the same problem again.