解决 FastAPI /docs 文档白屏或加载时间过长的问题
一、问题现象
在使用 FastAPI 开发接口服务时,默认会提供一个非常方便的接口文档页面:
text
http://127.0.0.1:8000/docs
正常情况下,访问该地址会打开 Swagger UI 文档页面,可以直接查看接口、调试接口、查看请求参数和响应结构。
但是在实际部署环境中,经常会遇到以下问题:
/docs页面打开后一直白屏;- 页面长时间停留在加载状态;
- 浏览器控制台提示 JS 或 CSS 加载失败;
- 内网环境、服务器环境、无外网环境下
/docs无法正常显示; - 页面 HTML 能返回,但
swagger-ui-bundle.js加载非常慢; - 线上服务器访问
/docs比本地慢很多。
这些问题通常不是 FastAPI 接口本身异常,而是 Swagger UI 所依赖的前端静态资源加载失败或加载过慢导致的。
二、问题原因分析
FastAPI 的 /docs 页面本质上是一个 Swagger UI 前端页面。
这个页面并不是只有一段 HTML,它还需要加载一些前端资源,例如:
text
swagger-ui-bundle.js
swagger-ui.css
如果这些资源无法正常加载,页面就可能出现白屏。
常见原因包括:
1. CDN 资源访问慢或无法访问
FastAPI 默认文档页面通常会引用外部的 Swagger UI 静态资源。
如果服务器或浏览器访问外部 CDN 较慢,就会导致 /docs 页面长时间等待。
在部分内网环境、企业服务器、国内网络环境中,CDN 资源可能会出现:
text
pending
timeout
ERR_CONNECTION_TIMED_OUT
ERR_CONNECTION_RESET
net::ERR_FAILED
这时 /docs 页面虽然已经打开,但核心 JS 文件没有加载完成,所以页面会一直白屏。
2. 服务器环境不能访问外网
有些 FastAPI 服务部署在内网服务器、Docker 容器、政企内网或无公网环境中。
这种情况下,接口服务本身可以正常访问,但是 /docs 页面依赖的外部 JS 和 CSS 文件无法加载。
最终表现就是:
text
接口正常
/openapi.json 正常
/docs 白屏
3. swagger-ui-bundle.js 文件过大,下载时间过长
swagger-ui-bundle.js 是 Swagger UI 的核心 JS 文件,体积相对较大。
如果服务器没有开启缓存、压缩,或者网络质量较差,每次打开 /docs 都重新下载这个 JS 文件,就会造成页面打开缓慢。
4. 静态文件路径配置错误
如果已经改成了本地静态资源,但是路径配置错误,也会导致白屏。
例如代码中配置了:
python
swagger_js_url="/static/swagger-ui/swagger-ui-bundle.js"
swagger_css_url="/static/swagger-ui/swagger-ui.css"
但是项目目录中没有对应文件,或者 FastAPI 没有正确挂载 /static 静态目录,就会导致 JS/CSS 返回 404。
三、排查方法
遇到 /docs 白屏时,建议先打开浏览器开发者工具。
快捷键:
text
F12
然后切换到:
text
Network
刷新 /docs 页面,重点检查以下资源:
text
swagger-ui-bundle.js
swagger-ui.css
/openapi.json
重点看这几个字段:
text
Status
Type
Size
Time
如果出现下面情况,就基本可以确定是静态资源问题:
text
swagger-ui-bundle.js 404
swagger-ui-bundle.js pending
swagger-ui-bundle.js timeout
swagger-ui.css 404
/openapi.json 正常,但页面白屏
四、解决思路
最稳定的解决方案是:
text
不要让 /docs 页面依赖外部 CDN
而是把 swagger-ui-bundle.js 和 swagger-ui.css 下载到项目本地
然后通过 FastAPI 的 StaticFiles 提供本地静态资源
这样 /docs 页面加载时,JS 和 CSS 都从自己的服务中读取,不再依赖外部网络。
五、项目目录结构
可以在项目中创建如下目录:
text
project/
├── main.py
└── static/
└── swagger-ui/
├── swagger-ui-bundle.js
├── swagger-ui.css
└── favicon.png
其中核心文件是:
text
swagger-ui-bundle.js
swagger-ui.css
favicon.png 不是必须的,只是用于设置文档页面图标。
六、FastAPI 代码配置
示例代码如下:
python
from fastapi import FastAPI
from fastapi.openapi.docs import (
get_swagger_ui_html,
get_swagger_ui_oauth2_redirect_html,
)
from fastapi.staticfiles import StaticFiles
app = FastAPI(
title="My API",
docs_url=None,
redoc_url=None,
)
app.mount(
"/static",
StaticFiles(directory="static"),
name="static",
)
@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
return get_swagger_ui_html(
openapi_url=app.openapi_url,
title=f"{app.title} - Swagger UI",
swagger_js_url="/static/swagger-ui/swagger-ui-bundle.js",
swagger_css_url="/static/swagger-ui/swagger-ui.css",
swagger_favicon_url="/static/swagger-ui/favicon.png",
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
swagger_ui_parameters={
"docExpansion": "none",
"defaultModelsExpandDepth": -1,
},
)
@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
async def swagger_ui_redirect():
return get_swagger_ui_oauth2_redirect_html()
@app.get("/test")
async def test():
return {"message": "ok"}
核心配置是:
python
swagger_js_url="/static/swagger-ui/swagger-ui-bundle.js"
swagger_css_url="/static/swagger-ui/swagger-ui.css"
这两个参数表示 /docs 页面不再从外部 CDN 加载 Swagger UI 资源,而是从当前 FastAPI 服务的 /static 路径加载。
七、为什么要设置 docs_url=None
代码中使用了:
python
app = FastAPI(
docs_url=None,
redoc_url=None,
)
这是为了关闭 FastAPI 默认生成的 /docs 页面。
然后我们自己重新注册一个 /docs 路由:
python
@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
...
这样可以完全控制 Swagger UI 的 JS、CSS、favicon、页面标题等资源地址。
八、验证是否配置成功
启动服务:
bash
uvicorn main:app --host 0.0.0.0 --port 8000
访问:
text
http://127.0.0.1:8000/docs
然后再访问静态资源地址:
text
http://127.0.0.1:8000/static/swagger-ui/swagger-ui-bundle.js
http://127.0.0.1:8000/static/swagger-ui/swagger-ui.css
如果这两个地址都能正常返回内容,说明静态资源配置成功。
再打开浏览器开发者工具,查看 Network。
正常情况下应该能看到:
text
swagger-ui-bundle.js 200
swagger-ui.css 200
/openapi.json 200
如果都是 200,/docs 页面一般就可以正常显示。
九、如果仍然加载慢,可以继续优化
如果本地化静态资源后,/docs 仍然加载慢,可以继续检查以下几点。
1. 开启 gzip 压缩
swagger-ui-bundle.js 文件体积较大,建议在 Nginx 中开启 gzip 压缩。
示例:
nginx
gzip on;
gzip_types text/plain text/css application/javascript application/json;
gzip_min_length 1k;
如果没有使用 Nginx,也可以在 FastAPI 中使用 GZipMiddleware:
python
from fastapi.middleware.gzip import GZipMiddleware
app.add_middleware(GZipMiddleware, minimum_size=1000)
2. 开启静态资源缓存
如果每次打开 /docs 都重新下载 JS 文件,也会影响速度。
可以在 Nginx 中给静态资源设置缓存:
nginx
location /static/ {
expires 7d;
add_header Cache-Control "public";
}
这样浏览器第二次打开 /docs 时,就可以直接使用缓存资源。
3. 检查 /openapi.json 是否过大
有时候白屏不是 JS 加载慢,而是 /openapi.json 太大。
如果接口数量很多、Pydantic 模型很复杂,Swagger UI 在渲染大量接口时也会变慢。
可以访问:
text
http://127.0.0.1:8000/openapi.json
检查这个文件是否非常大。
如果 /openapi.json 很大,可以考虑:
- 减少无用接口暴露;
- 内部接口设置
include_in_schema=False; - 拆分多个 FastAPI 应用;
- 不把所有后台任务接口都放到同一个 Swagger 文档中;
- 关闭默认展开模型。
例如:
python
@app.get("/internal/task", include_in_schema=False)
async def internal_task():
return {"status": "ok"}
4. 不要把静态目录路径写错
如果代码中写的是:
python
StaticFiles(directory="static")
那么项目运行目录下必须存在:
text
static/
如果你是在其他目录执行 uvicorn,可能会因为相对路径不同导致找不到静态文件。
更稳妥的写法是使用绝对路径:
python
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
app.mount(
"/static",
StaticFiles(directory=BASE_DIR / "static"),
name="static",
)
这样不管从哪个目录启动项目,都能正确找到静态资源。
十、完整推荐版代码
python
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.openapi.docs import (
get_swagger_ui_html,
get_swagger_ui_oauth2_redirect_html,
)
from fastapi.staticfiles import StaticFiles
BASE_DIR = Path(__file__).resolve().parent
app = FastAPI(
title="My API",
docs_url=None,
redoc_url=None,
)
app.add_middleware(GZipMiddleware, minimum_size=1000)
app.mount(
"/static",
StaticFiles(directory=BASE_DIR / "static"),
name="static",
)
@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
return get_swagger_ui_html(
openapi_url=app.openapi_url,
title=f"{app.title} - Swagger UI",
swagger_js_url="/static/swagger-ui/swagger-ui-bundle.js",
swagger_css_url="/static/swagger-ui/swagger-ui.css",
swagger_favicon_url="/static/swagger-ui/favicon.png",
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
swagger_ui_parameters={
"docExpansion": "none",
"defaultModelsExpandDepth": -1,
"displayRequestDuration": True,
},
)
@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
async def swagger_ui_redirect():
return get_swagger_ui_oauth2_redirect_html()
@app.get("/test")
async def test():
return {"message": "ok"}
十一、最终效果
修改后,/docs 页面加载流程变成:
text
浏览器访问 /docs
↓
FastAPI 返回 Swagger UI HTML
↓
浏览器从 /static/swagger-ui/swagger-ui-bundle.js 加载 JS
↓
浏览器从 /static/swagger-ui/swagger-ui.css 加载 CSS
↓
浏览器请求 /openapi.json
↓
Swagger UI 正常渲染接口文档
这样就避免了外部 CDN 访问慢、网络阻断、JS 文件超时等问题。
十二、总结
FastAPI /docs 页面白屏或加载时间过长,大多数情况下不是接口服务本身的问题,而是 Swagger UI 所依赖的前端资源加载失败或加载过慢。
推荐解决方式是:
text
将 swagger-ui-bundle.js 和 swagger-ui.css 下载到本地
使用 StaticFiles 挂载静态目录
通过 get_swagger_ui_html 自定义 swagger_js_url 和 swagger_css_url
核心代码如下:
python
def swagger_monkey_patch(*args, **kwargs):
"""
Wrap the function which is generating the HTML for the /docs endpoint and overwrite the default values for the swagger js and css.
"""
return get_swagger_ui_html(
*args, **kwargs,
swagger_js_url="/static/swagger-ui/swagger-ui-bundle.js",
swagger_css_url="/static/swagger-ui/swagger-ui.css",
)
这种方式适合:
- 内网部署;
- 服务器无法访问外网;
- CDN 访问慢;
/docs页面白屏;- Swagger UI JS 文件 pending;
- 线上接口文档打开很慢。
配置完成后,再配合 gzip 压缩和静态资源缓存,可以明显提升 FastAPI 文档页面的打开速度。