diff --git a/fastapi/applications.py b/fastapi/applications.py
index ed05a1ff9e..e7e816c2e9 100644
--- a/fastapi/applications.py
+++ b/fastapi/applications.py
@@ -1101,16 +1101,18 @@ class FastAPI(Starlette):
def setup(self) -> None:
if self.openapi_url:
- urls = (server_data.get("url") for server_data in self.servers)
- server_urls = {url for url in urls if url}
async def openapi(req: Request) -> JSONResponse:
root_path = req.scope.get("root_path", "").rstrip("/")
- if root_path not in server_urls:
- if root_path and self.root_path_in_servers:
- self.servers.insert(0, {"url": root_path})
- server_urls.add(root_path)
- return JSONResponse(self.openapi())
+ schema = self.openapi()
+ if root_path and self.root_path_in_servers:
+ server_urls = {s.get("url") for s in schema.get("servers", [])}
+ if root_path not in server_urls:
+ schema = dict(schema)
+ schema["servers"] = [{"url": root_path}] + schema.get(
+ "servers", []
+ )
+ return JSONResponse(schema)
self.add_route(self.openapi_url, openapi, include_in_schema=False)
if self.openapi_url and self.docs_url:
diff --git a/fastapi/openapi/docs.py b/fastapi/openapi/docs.py
index b845f87c1c..0d9242f9fa 100644
--- a/fastapi/openapi/docs.py
+++ b/fastapi/openapi/docs.py
@@ -5,6 +5,20 @@ from annotated_doc import Doc
from fastapi.encoders import jsonable_encoder
from starlette.responses import HTMLResponse
+
+def _html_safe_json(value: Any) -> str:
+ """Serialize a value to JSON with HTML special characters escaped.
+
+ This prevents injection when the JSON is embedded inside a "
+ html = get_swagger_ui_html(
+ openapi_url="/openapi.json",
+ title="Test",
+ init_oauth={"appName": xss_payload},
+ )
+ body = html.body.decode()
+
+ assert "