Python & Backend
Upgrading to FastAPI 0.146: root_path deprecation and CLI changes
FastAPI 0.146 deprecates FastAPI(root_path=...). Move subpath configuration to the CLI or ASGI server and update your proxy settings and tests.
By Tanvir Ahamed, published . 7 min read.
Researched and written with AI assistance; sources are linked below.
Applications running behind a path-stripping reverse proxy such as Nginx, Traefik, or an AWS Application Load Balancer often pass root_path directly into the FastAPI() constructor so that interactive OpenAPI documentation and URL generation know about the external prefix. In FastAPI 0.146.0, released on October 18, 2026, passing root_path to FastAPI() is officially deprecated and emits a FastAPIDeprecationWarning at startup. Combined with the removal of openapi_prefix and **extra keyword arguments in FastAPI 0.145.0, the framework now enforces a cleaner boundary between Python application definitions and ASGI server deployment parameters. This guide walks through everything that changed between FastAPI 0.143 and 0.146, how to move root_path out of application factories into fastapi run or Uvicorn, and how to update OpenAPI proxy configurations and test suites without breaking subpath routing.
Why FastAPI 0.146.0 deprecates the application root_path parameter
In the ASGI specification, root_path exists to solve a specific problem: an external reverse proxy listens on a subpath like /api/v1/app, strips the /api/v1 prefix, and forwards the request as /app to the backend server listening on a local port. It also supports mounting sub-applications inside a parent ASGI application. When the backend application generates relative URLs or builds an OpenAPI schema for /docs, it needs to know that external callers reach it through /api/v1.
Historically, FastAPI allowed you to configure this prefix in two places: on the ASGI server command line (uvicorn --root-path /api/v1 or fastapi run --root-path /api/v1) or in Python code via FastAPI(root_path="/api/v1"). As documented in the updated Behind a Proxy guide, the FastAPI(root_path=...) parameter is now deprecated because its behavior was not consistent with the server configuration.
When root_path was only passed to FastAPI(), Starlette and the underlying ASGI server did not populate scope["root_path"] on incoming requests the same way an ASGI server does when configured with --root-path. That mismatch caused subtle discrepancies in how routes, mounted sub-applications, and request scopes behaved depending on whether the setting lived in Python or in the server startup command.
In FastAPI 0.146.0 (via PR #16520), the root_path parameter on FastAPI.__init__ is marked with @typing_extensions.deprecated in the FastAPI class reference and triggers a runtime warning whenever a non-empty string is passed. Static type checkers that support PEP 702 (deprecated()) will flag the parameter in your editor or CI pipeline, and runtime initialization executes the following check:
if root_path:
warnings.warn(
"The `root_path` parameter is deprecated. Configure the "
"server's root path instead, for example with "
"`fastapi run --root-path /api/v1`.",
category=FastAPIDeprecationWarning,
stacklevel=2,
)
self.root_path = root_pathKeeping root_path out of FastAPI() also improves portability. An application instance in Python should not need to know whether it is deployed at / in a local container, /staging/api in a preview environment, or /api/v1 in production. That topology belongs to the process runner and reverse proxy.
What else changed between FastAPI 0.143 and 0.146
If you are upgrading from FastAPI 0.143.x or earlier directly to 0.146.0, several other changes landed in the FastAPI release notes during the October 2026 release window that can affect startup, routing, and testing.
Breaking removals in 0.145.0: openapi_prefix and extra kwargs
FastAPI 0.145.0 (PR #16518) removes two long-deprecated parameters from FastAPI.__init__: openapi_prefix and arbitrary extra keyword arguments (**extra).
In older versions, passing unrecognized keyword arguments to FastAPI(custom_setting="value") or using the legacy openapi_prefix="/api/v1" parameter would either be absorbed by **extra or emit a deprecation warning. Starting in 0.145.0, passing either one raises an immediate TypeError at import or application factory execution time. If your application attaches custom metadata or settings to the FastAPI instance during initialization, store those attributes on app.state after creating the FastAPI() object rather than passing them as constructor keyword arguments.
Template URL path updates in 0.145.0
Also in FastAPI 0.145.0 (PR #16517), the Templates documentation was updated to clarify how Jinja2 templates should generate URLs when running behind a proxy. Using url_for('read_item', id=id).path inside a template produces a site-root-relative path that begins with /, automatically includes any server-configured root_path, and omits the scheme, host, query parameters, and fragment. When full URLs including the scheme and domain are required behind a proxy, you should configure both --forwarded-allow-ips and --root-path on the server.
WebSocket support in security dependencies in 0.144.0
In the 0.144.0 release (PR #10147), FastAPI added WebSocket support to standard security dependencies in fastapi.security (such as HTTPBearer, APIKeyHeader, APIKeyCookie, APIKeyQuery, and OAuth2PasswordBearer). Previously, injecting these dependencies into @app.websocket routes failed with TypeError: __call__() missing 1 required positional argument: 'request' because the dependencies expected a Request instance rather than Starlette's shared HTTPConnection or WebSocket object. You can now reuse the same security dependency classes across HTTP and WebSocket endpoints.
Notable changes across the 0.143.x series
Rounding out the recent releases in the FastAPI release notes:
- 0.143.5: Fixed handling of empty values for byte file list parameters (
list[bytes] = File(), PR#9383). - 0.143.4: Added
httpx2to optional dependencies (PR#16512) following the Starlette 1.7.0 bump in 0.143.0. - 0.143.3: Fixed non-finite float serialization (
NaN,Infinity,-Infinity) in JSONL and Server-Sent Events (SSE) streams (PR#16509). - 0.143.2: Fixed blocking frontend fallback file checks (PR
#16503). - 0.143.1: Fixed included route metadata in
iter_route_contexts()(PR#16484). - 0.143.0: Made automatic OpenTelemetry exporter setup opt-in. If you previously relied on FastAPI automatically configuring OpenTelemetry exporters from
OTEL_*environment variables, you must now setFASTAPI_OTEL_AUTO_CONFIGURE=truein your environment or passFastAPI(telemetry={"auto_configure": True}).
Moving root_path from FastAPI() to the CLI and ASGI server
To upgrade cleanly to FastAPI 0.146.0, remove root_path, openapi_prefix, and any unrecognized keyword arguments from your FastAPI() constructor calls. Let's look at a typical application factory before the upgrade and how to refactor it.
Here is a legacy setup that reads root_path from environment settings and passes an arbitrary keyword argument into FastAPI(). On 0.145.0 and 0.146.0, this code either raises a TypeError (for environment) or emits FastAPIDeprecationWarning (for root_path):
# main_legacy.py - Avoid in FastAPI 0.145+
import os
from fastapi import FastAPI, Request
# FastAPI 0.145.0 raises TypeError on unknown kwargs like 'environment'.
# FastAPI 0.146.0 emits FastAPIDeprecationWarning when 'root_path' is non-empty.
app = FastAPI(
title="Inventory Service",
root_path=os.getenv("ROOT_PATH", "/api/v1"),
)
@app.get("/app")
def read_main(request: Request) -> dict[str, str | None]:
return {
"message": "Hello World",
"root_path": request.scope.get("root_path"),
}To update this code for FastAPI 0.146.0, strip root_path from the FastAPI() constructor and move any custom application attributes onto app.state:
# main.py - Compatible with FastAPI 0.146.0
import os
from fastapi import FastAPI, Request
app = FastAPI(title="Inventory Service")
app.state.environment = os.getenv("ENVIRONMENT", "production")
@app.get("/app")
def read_main(request: Request) -> dict[str, str | None]:
return {
"message": "Hello World",
"root_path": request.scope.get("root_path"),
}With the Python code decoupled from the proxy subpath, configure --root-path where the ASGI server starts.
Running with the FastAPI CLI
The official Behind a Proxy documentation recommends passing --root-path directly to fastapi run. Under the hood, fastapi run invokes Uvicorn and forwards the root path option to the ASGI server:
fastapi run main.py --root-path /api/v1If your application runs behind a trusted reverse proxy that terminates TLS and sets X-Forwarded-For and X-Forwarded-Proto headers, combine --root-path with --forwarded-allow-ips so that generated URLs (including template url_for() calls) reflect the external scheme and host:
fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1Running with Uvicorn directly
If your container entrypoint or process manager calls uvicorn directly instead of the FastAPI CLI, pass the same --root-path flag to Uvicorn:
uvicorn main:app --host 0.0.0.0 --port 8000 --root-path /api/v1Or, if you start Uvicorn programmatically from a Python entrypoint script, pass root_path to uvicorn.run(). This sets scope["root_path"] at the ASGI server layer without triggering the FastAPI constructor deprecation:
# run.py
import os
import uvicorn
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
root_path=os.getenv("ROOT_PATH", "/api/v1"),
)Handling OpenAPI servers and docs behind a reverse proxy
A common misconception when configuring --root-path /api/v1 is assuming that Uvicorn itself will now listen at http://127.0.0.1:8000/api/v1/app. As explained in the Behind a Proxy guide, Uvicorn does not expect the proxy prefix to be present on requests arriving at its socket. The reverse proxy strips /api/v1 and sends /app to http://127.0.0.1:8000/app. Uvicorn only uses --root-path /api/v1 to populate request.scope["root_path"] = "/api/v1" and hand it to FastAPI.
How OpenAPI schema generation uses scope["root_path"]
When a browser or client requests /openapi.json, FastAPI inspects request.scope.get("root_path", "").rstrip("/"). By default, FastAPI() sets root_path_in_servers=True. When a non-empty root_path is present in the ASGI request scope and is not already listed in self.servers, FastAPI automatically prepends {"url": root_path} to the servers array in the generated OpenAPI JSON:
{
"openapi": "3.1.0",
"info": {
"title": "Inventory Service",
"version": "0.1.0"
},
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
"/app": {
"get": {
"summary": "Read Main",
"operationId": "read_main_app_get",
"responses": {
"200": {
"description": "Successful Response"
}
}
}
}
}
}When you open Swagger UI through the proxy at https://example.com/api/v1/docs, the browser fetches the OpenAPI schema, sees the /api/v1 server entry, and sends interactive "Try it out" requests to https://example.com/api/v1/app. Because Uvicorn expects the proxy to strip /api/v1, opening http://127.0.0.1:8000/docs directly on the local port without going through the proxy will fail when you click "Execute", because Swagger UI will request http://127.0.0.1:8000/api/v1/app directly from Uvicorn.
Controlling servers and root_path_in_servers
While root_path on FastAPI() is deprecated in 0.146.0, the servers and root_path_in_servers parameters on FastAPI() remain fully supported in the FastAPI class reference.
If you specify explicit upstream servers (for example, multiple regional proxy URLs) and do not want FastAPI to automatically inject the local ASGI root_path into the OpenAPI servers list, pass root_path_in_servers=False:
# main_custom_servers.py
from fastapi import FastAPI
app = FastAPI(
title="Inventory Service",
servers=[
{"url": "https://stg.example.com/api/v1", "description": "Staging"},
{"url": "https://prod.example.com/api/v1", "description": "Production"},
],
root_path_in_servers=False,
)
@app.get("/app")
def read_main() -> dict[str, str]:
return {"message": "Hello World"}When servers is omitted and root_path is empty or /, FastAPI omits the servers field from the OpenAPI schema entirely.
Updating tests and CI when upgrading to 0.146.0
If your test suite previously tested proxy subpath behavior or OpenAPI servers generation by instantiating FastAPI(root_path="/api/v1") inside test fixtures, those tests will emit FastAPIDeprecationWarning on 0.146.0.
Instead of configuring root_path on the application, pass root_path to TestClient. As shown in the FastAPI TestClient reference, TestClient.__init__ accepts a root_path: str = "" argument and populates the ASGI scope on every simulated request just like Uvicorn does:
# test_main.py
from fastapi import FastAPI, Request
from fastapi.testclient import TestClient
app = FastAPI(title="Inventory Service")
@app.get("/app")
def read_main(request: Request) -> dict[str, str | None]:
return {
"message": "Hello World",
"root_path": request.scope.get("root_path"),
}
def test_root_path_scope_and_openapi() -> None:
client = TestClient(app, root_path="/api/v1")
response = client.get("/app")
assert response.status_code == 200
assert response.json() == {
"message": "Hello World",
"root_path": "/api/v1",
}
openapi_response = client.get("/openapi.json")
assert openapi_response.status_code == 200
assert openapi_response.json()["servers"] == [{"url": "/api/v1"}]Notice that the test requests client.get("/app"), not client.get("/api/v1/app"). This mirrors how an ASGI server behind a path-stripping reverse proxy receives the stripped path while reading /api/v1 from scope["root_path"].
Turning FastAPIDeprecationWarning into a CI error
To prevent new FastAPI(root_path=...) calls or other deprecated FastAPI patterns from creeping back into your codebase, configure pytest in your pyproject.toml to treat FastAPIDeprecationWarning as an error:
# pyproject.toml
[tool.pytest.ini_options]
filterwarnings = [
"error::fastapi.exceptions.FastAPIDeprecationWarning",
]With this filter enabled in CI, any test or application factory that initializes FastAPI(root_path="/api/v1") fails immediately with a clear traceback pointing to the caller (stacklevel=2).
As a next step after upgrading to FastAPI 0.146.0, audit your Dockerfile entrypoints, Kubernetes manifests, or systemd unit files to verify that --root-path and --forwarded-allow-ips are defined alongside your fastapi run or uvicorn commands, and run pytest with FastAPIDeprecationWarning set to error to confirm no application factories still configure proxy paths in Python.
Sources
- FastAPI
- Python
- ASGI
- Backend