How to Access FastAPI SwaggerUI Docs Behind an NGINX proxy?
Asked Answered
M

2

7

Hope you can help me, here is the issue I have:

Both of my frontend and backend servers runs on the same AWS EC2 instance. Because of this I have created a NGINX config like this:

server {
        server_name NAME;
        listen 80 default_server;
        location / {
                proxy_pass http://127.0.0.1:5000;
                proxy_set_header Host $http_host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_http_version 1.1;
        }
        location /api/ {
                proxy_pass http://127.0.0.1:8000;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_http_version 1.1;
        }
}

So any request to the "http://public_ip/api/" routed to the FastAPI backend server while every other request to another endpoint routed to the frontend SPA.

This works quite good mostly. However there is an issue if I try to access FastAPI "/api/docs" or "/api/redoc" routes. When I call the "/api/docs" endpoint for instance, there is a request to the "http://public_ip/openapi.json" address. And this isn't an endpoint starting with "/api" obviously. So NGINX blocks it and raises a bad request.

https://fastapi.tiangolo.com/advanced/behind-a-proxy/#about-proxies-with-a-stripped-path-prefix

I found this guide but it seems like this isn't related to my problem at all. At least I understand it that way.

Any help is appreciated. Thanks in advance.

Maury answered 7/5, 2021 at 12:50 Comment(0)
M
5

Passing 'openapi_url' argument to the FastAPI() seems like good solution. Passed openapi_url= '/api/openapi.json' and it's fixed for both docs and redoc. Any other/better solution to handle all redirects that may occur is appreciated.

api = FastAPI(title="API_NAME",
              description="API_DESC",
              version="0.2.0",
              docs_url='/api/docs',
              redoc_url='/api/redoc',
              openapi_url='/api/openapi.json')
Maury answered 7/5, 2021 at 13:23 Comment(3)
Why don't you have to set the root_dir there to /api? When I do what you did above, I can reach the docs but the actual api gives me a 404. The "behind-a-proxy" instructions above don't mention needing to set the docs_url just the root_dir. There's too many options.Priester
It turns out I had to add api to each of the individual routes. I think the root_dir stuff is probably based on Traefik and this works for NGINX if you bring the 'api' over by not having a trailing slash.Priester
I was also able to fix this by using the root_path property of FastAPI: app = FastAPI( root_path="/my_endpoint" ) where my_end_point was the Proxy URL I setup in my web server's config (/api in your example").Covenant
H
3

For me it worked as follows:

The entrypoint script of my docker container:

echo "Running the FastAPI server"
uvicorn server:app --host 0.0.0.0 --port 8000 --app-dir dashboard/api/ --root-path /api/ --forwarded-allow-ips "*" --reload &

The FastAPI app:

import uvicorn 

from typing import Union
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware 

app = FastAPI() # root_path="/api/"

origins = ['*']

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/")
async def read_root():
    return {"message": "Hello from FastAPI"} 


@app.get("/test")
def read_main(request: Request):
    return {"message": "Hello World", "root_path": request.scope.get("root_path")}


# if __name__ == "__main__":
#     uvicorn.run(app, host='0.0.0.0', port=8000)

The NGINX configuration:

upstream dashboard {
  server localhost:8081;
}

upstream apiserver {
  server localhost:8000;
}

server {
    listen 8080 default_server;
    listen [::]:8080 default_server;
    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;
    server_name _;
    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
    }
    location /dashboard/ {
        proxy_pass http://dashboard;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    location /api/ {
        rewrite  ^/api/(.*)  /$1 break;
        proxy_set_header Host $http_host;
        proxy_pass http://apiserver;
    }
}

Now everything works great outside of the docker container:

curl http://0.0.0.0:28080/api/
curl http://0.0.0.0:28080/api/test
curl http://0.0.0.0:28080/api/openapi.json
curl http://0.0.0.0:28080/api/docs

Inside the docker container it works as well:

curl http://0.0.0.0:8000/
curl http://0.0.0.0:8000/test
curl http://0.0.0.0:8000/openapi.json
curl http://0.0.0.0:8000/docs
Hour answered 4/5, 2023 at 21:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.