Un proxy frente a tu aplicación normalmente establecería algunos headers al vuelo antes de enviar las requests a tu servidor para informarle que la request fue reenviada por el proxy, informándole la URL original (pública), incluyendo el dominio, que está usando HTTPS, etc.
El programa servidor (por ejemplo Uvicorn vía FastAPI CLI) es capaz de interpretar estos headers, y luego pasar esa información a tu aplicación.
Pero por seguridad, como el servidor no sabe que está detrás de un proxy de confianza, no interpretará esos headers.
Puedes iniciar FastAPI CLI con la CLI Option--forwarded-allow-ips y pasar las direcciones IP que deberían ser de confianza para leer esos headers reenviados.
Si lo estableces a --forwarded-allow-ips="*" confiaría en todas las IPs entrantes.
Si tu servidor está detrás de un proxy de confianza y solo el proxy se comunica con él, esto haría que acepte cualquier IP que tenga ese proxy.
fastapi run --forwarded-allow-ips="*"
fast →fastapi run --forwarded-allow-ips="*" INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Aquí hay una representación visual de cómo el proxy añade headers reenviados entre el cliente y el servidor de la aplicación:
El proxy intercepta la request original del cliente y añade los headers especiales reenviados (X-Forwarded-*) antes de pasar la request al servidor de la aplicación.
Estos headers preservan información sobre la request original que de otra manera se perdería:
X-Forwarded-For: La dirección IP original del cliente
X-Forwarded-Proto: El protocolo original (https)
X-Forwarded-Host: El host original (mysuperapp.com)
Cuando FastAPI CLI está configurado con --forwarded-allow-ips, confía en estos headers y los usa, por ejemplo para generar las URLs correctas en las redirecciones.
Podrías tener un proxy que añade un prefijo de ruta a tu aplicación.
En estos casos puedes usar root_path para configurar tu aplicación.
El root_path es un mecanismo proporcionado por la especificación ASGI (sobre la que FastAPI está construido, a través de Starlette).
El root_path se usa para manejar estos casos específicos.
Y también se usa internamente al montar sub-aplicaciones.
Tener un proxy con un prefijo de ruta eliminado, en este caso, significa que podrías declarar una ruta en /app en tu código, pero luego, añades una capa encima (el proxy) que pondría tu aplicación FastAPI bajo una ruta como /api/v1.
En este caso, la ruta original /app realmente se serviría en /api/v1/app.
A pesar de que todo tu código está escrito asumiendo que solo hay /app.
Y el proxy estaría "eliminando" el prefijo de ruta al vuelo antes de transmitir la request al servidor de la app (probablemente Uvicorn vía FastAPI CLI), manteniendo a tu aplicación convencida de que se está sirviendo en /app, para que no tengas que actualizar todo tu código para incluir el prefijo /api/v1.
Hasta aquí, todo funcionaría como de costumbre.
Pero luego, cuando abres la docs UI integrada (el frontend), esperaría obtener el esquema de OpenAPI en /openapi.json, en lugar de /api/v1/openapi.json.
Así que, el frontend (que se ejecuta en el navegador) intentaría alcanzar /openapi.json y no podría obtener el esquema de OpenAPI.
Porque tenemos un proxy con un prefijo de ruta de /api/v1 para nuestra app, el frontend necesita obtener el esquema de OpenAPI en /api/v1/openapi.json.
Consejo
La IP 0.0.0.0 se usa comúnmente para indicar que el programa escucha en todas las IPs disponibles en esa máquina/servidor.
La docs UI también necesitaría que el esquema de OpenAPI declare que este servidor de la API está ubicado en /api/v1 (detrás del proxy). Por ejemplo:
{"openapi":"3.1.0",// More stuff here"servers":[{"url":"/api/v1"}],"paths":{// More stuff here}}
En este ejemplo, el "Proxy" podría ser algo como Traefik. Y el servidor sería algo como FastAPI CLI con Uvicorn, ejecutando tu aplicación FastAPI.
Alternativamente, si no tienes forma de proporcionar una opción de línea de comandos como --root-path o equivalente, puedes establecer el parámetro root_path al crear tu app FastAPI:
Así que, no esperará ser accedido en http://127.0.0.1:8000/api/v1/app.
Uvicorn esperará que el proxy acceda a Uvicorn en http://127.0.0.1:8000/app, y luego sería responsabilidad del proxy añadir el prefijo extra /api/v1 encima.
Acerca de proxies con un prefijo de ruta eliminado¶
Ten en cuenta que un proxy con prefijo de ruta eliminado es solo una de las formas de configurarlo.
Probablemente en muchos casos el valor por defecto será que el proxy no tenga un prefijo de ruta eliminado.
En un caso así (sin un prefijo de ruta eliminado), el proxy escucharía en algo como https://myawesomeapp.com, y luego si el navegador va a https://myawesomeapp.com/api/v1/app y tu servidor (ej. Uvicorn) escucha en http://127.0.0.1:8000 el proxy (sin un prefijo de ruta eliminado) accedería a Uvicorn en la misma ruta: http://127.0.0.1:8000/api/v1/app.
pero esta vez en la URL con el prefijo de ruta proporcionado por el proxy: /api/v1.
Por supuesto, la idea aquí es que todos accederían a la app a través del proxy, así que la versión con el prefijo de ruta /api/v1 es la "correcta".
Y la versión sin el prefijo de ruta (http://127.0.0.1:8000/app), proporcionada por Uvicorn directamente, sería exclusivamente para que el proxy (Traefik) acceda a ella.
Eso demuestra cómo el Proxy (Traefik) usa el prefijo de ruta y cómo el servidor (Uvicorn) usa el root_path de la opción --root-path.
La forma "oficial" de acceder a la app sería a través del proxy con el prefijo de ruta que definimos. Así que, como esperaríamos, si pruebas la docs UI servida por Uvicorn directamente, sin el prefijo de ruta en la URL, no funcionará, porque espera ser accedida a través del proxy.
Este es un caso de uso más avanzado. Siéntete libre de saltarlo.
Por defecto, FastAPI creará un servidor en el esquema de OpenAPI con la URL del root_path.
Pero también puedes proporcionar otros servidores alternativos, por ejemplo si quieres que la misma docs UI interactúe tanto con un entorno de staging como con uno de producción.
Si pasas una lista personalizada de servidores y hay un root_path (porque tu API vive detrás de un proxy), FastAPI insertará un "servidor" con este root_path al principio de la lista.
{"openapi":"3.1.0",// More stuff here"servers":[{"url":"/api/v1"},{"url":"https://stag.example.com","description":"Staging environment"},{"url":"https://prod.example.com","description":"Production environment"}],"paths":{// More stuff here}}
Consejo
Notar el servidor auto-generado con un valor de url de /api/v1, tomado del root_path.
La docs UI interactuará con el servidor que selecciones.
Detalles Técnicos
La propiedad servers en la especificación de OpenAPI es opcional.
Si no especificas el parámetro servers y el root_path es igual a /, la propiedad servers en el esquema de OpenAPI generado se omitirá completamente por defecto, lo cual es el equivalente a un solo servidor con un valor de url de /.
Si necesitas montar una sub-aplicación (como se describe en Sub Applications - Mounts) mientras también usas un proxy con root_path, puedes hacerlo normalmente, como esperarías.
FastAPI usará internamente el root_path de forma inteligente, así que simplemente funcionará. ✨