本文 AI 產出,尚未審核

FastAPI 教學:在 Production 環境關閉 /docs/redoc(OpenAPI 文件)

簡介

在開發階段,FastAPI 內建的 Swagger UI/docs)與 ReDoc/redoc)讓 API 使用者能即時檢視與測試介面,極大提升開發效率與除錯便利性。
然而,當應用程式部署到 Production 環境時,這兩個自動產生的文件頁面往往不該對外開放:

  • 資訊安全 – 文件中會揭露所有路由、請求參數、回傳結構,攻擊者可藉此搜尋弱點。
  • 效能考量 – 產生 OpenAPI schema 需要額外的計算與 I/O,對高流量服務會產生不必要的開銷。
  • 品牌與使用者體驗 – 真正的前端或 API 客戶端通常會有自訂的文件或 API Portal,內建的 UI 可能不符合企業形象。

本篇文章將說明 為什麼、什麼時候以及如何在 Production 中安全、乾淨地關閉 /docs/redoc,並提供多種實作範例與最佳實踐,幫助你在部署 FastAPI 時保持彈性與安全。


核心概念

1. FastAPI 產生文件的機制

FastAPI 會在應用程式啟動時自動建立一個 OpenAPI schemaopenapi.json),並根據設定掛載兩個路由:

路由 預設 UI 產生的檔案
/docs Swagger UI swagger-ui.html
/redoc ReDoc redoc.html

這兩個路由本質上是 普通的 FastAPI 路由,因此可以使用標準的路由註冊與移除方式加以管理。

2. 何時關閉文件

  • 正式上線(Production):所有外部使用者僅能透過正式的 API 文件或客戶端存取。
  • 測試環境(Staging):若測試環境與 Production 完全相同,建議同樣關閉,或使用環境變數做細部控制。
  • 內部開發環境:保留 /docs/redoc 以利開發與除錯。

3. 使用環境變數控制

最常見的做法是 透過環境變數(如 ENV=production)在程式啟動時決定是否掛載文件路由。這樣可以在同一套程式碼中同時支援多種部署模式。


程式碼範例

以下示範 五種 常見且實用的關閉方式,均以 Python(FastAPI)為例,並附上完整註解。

範例 1️⃣:最簡單的 docs_url=None / redoc_url=None

在建立 FastAPI 實例時直接傳入 None,即可完全不產生對應路由。

# main.py
from fastapi import FastAPI

# 在 Production 中直接關閉文件
app = FastAPI(
    title="My Production API",
    docs_url=None,      # 關閉 /docs (Swagger UI)
    redoc_url=None,     # 關閉 /redoc (ReDoc)
    openapi_url="/openapi.json"  # 若仍需要 OpenAPI JSON,可自行保留
)

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    """示範端點"""
    return {"item_id": item_id}

重點:此方式在程式啟動即決定,無法在不同環境動態切換。適合「只在 Production」的單一部署情境。


範例 2️⃣:利用環境變數動態設定

透過 os.getenv 判斷當前環境,決定是否保留文件路由。

# main.py
import os
from fastapi import FastAPI

ENV = os.getenv("ENV", "development")   # 預設為 development

if ENV == "production":
    # Production:關閉文件
    docs_url = None
    redoc_url = None
else:
    # Development / Staging:保留文件
    docs_url = "/docs"
    redoc_url = "/redoc"

app = FastAPI(
    title="Dynamic Docs API",
    docs_url=docs_url,
    redoc_url=redoc_url,
    openapi_url="/openapi.json"
)

@app.get("/ping")
async def ping():
    return {"msg": "pong"}

技巧:將環境變數寫入 Docker Compose、Kubernetes ConfigMap 或 CI/CD pipeline 中,保持部署與程式碼的分離。


範例 3️⃣:在 startup 事件中手動移除路由

如果你已在 FastAPI() 時啟用了文件,但想在 startup 階段根據條件移除它們。

# main.py
import os
from fastapi import FastAPI

app = FastAPI()

@app.get("/hello")
async def hello():
    return {"msg": "Hello World"}

@app.on_event("startup")
async def disable_docs():
    """根據環境在啟動時移除 /docs 與 /redoc"""
    if os.getenv("ENV") == "production":
        # 移除 swagger UI
        app.router.routes = [
            route for route in app.router.routes
            if route.path not in ("/docs", "/redoc")
        ]
        # 同時關閉 OpenAPI schema(若不需要)
        app.openapi_url = None

說明:此方法不需要在建立 FastAPI 時傳入 None,可在同一程式碼庫中保留完整文件,僅在需要時「動態」關閉。


範例 4️⃣:自訂 OpenAPI 產生函式,僅在非 Production 時回傳 schema

如果你仍想保留 /docs 介面但不希望外部取得 openapi.json,可以自訂 openapi 方法。

# main.py
import os
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

app = FastAPI()

def custom_openapi():
    """只在非 production 環境產生 OpenAPI schema"""
    if os.getenv("ENV") == "production":
        return None   # 直接回傳 None,Swagger UI 會顯示無法載入
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = get_openapi(
        title="Conditional OpenAPI",
        version="1.0.0",
        routes=app.routes,
    )
    app.openapi_schema = openapi_schema
    return app.openapi_schema

app.openapi = custom_openapi

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    return {"user_id": user_id}

優點/docs 仍可在開發環境正常使用,而 Production 中會自動顯示「OpenAPI schema not found」的訊息,避免資訊外洩。


範例 5️⃣:使用中介層(Middleware)攔截文件請求

若你想在 所有環境 保留路由,但僅對特定 IP 或認證通過者開放,可透過 Middleware 進行授權控制。

# main.py
from fastapi import FastAPI, Request, HTTPException
from starlette.status import HTTP_403_FORBIDDEN

app = FastAPI()

ALLOWED_IPS = {"127.0.0.1"}   # 僅允許本機存取文件

@app.middleware("http")
async def block_docs(request: Request, call_next):
    path = request.url.path
    if path in ("/docs", "/redoc", "/openapi.json"):
        client_ip = request.client.host
        if client_ip not in ALLOWED_IPS:
            raise HTTPException(status_code=HTTP_403_FORBIDDEN,
                                detail="Access to API docs is forbidden")
    response = await call_next(request)
    return response

@app.get("/status")
async def status():
    return {"status": "ok"}

應用:在內部測試環境僅允許公司內部 IP 存取 /docs,外部使用者則會得到 403 Forbidden


常見陷阱與最佳實踐

常見問題 為什麼會發生 解決方案或最佳做法
忘記在 Production 中關閉 /docs 多數開發者只在本地測試時注意文件,部署腳本未加以檢查。 在 CI/CD pipeline 加入 環境變數檢查,或使用 Dockerfile 中的 ENV 變數強制設定。
關閉 /docs 後,Swagger UI 仍能讀取 openapi.json 只關閉 UI,卻忘記同時將 openapi_url 設為 None 同時設定 docs_url=None, redoc_url=None, openapi_url=None,或自訂 app.openapi = lambda: None
使用 docs_url=None,但仍在程式碼中 app.include_router() 時產生警告 某些第三方套件會在啟動時自動掛載文件路由。 include_router 時使用 include_in_schema=False,或在套件的設定檔中關閉自動文件。
中介層(Middleware)攔截失敗,仍回傳文件 中介層寫在錯誤位置或未正確返回 call_next 確認 Middleware 在 FastAPI() 建立後、路由註冊前 加入,且 raise HTTPException 正確拋出。
開發人員忘記切換環境變數,導致 Production 暴露文件 部署腳本與程式碼分離不佳。 使用 .env 檔案或 Kubernetes Secret,並在程式碼中明確 assert os.getenv("ENV") 以防止預設為 development

建議的最佳實踐

  1. 環境變數化:在 Dockerfiledocker-compose.yml、或 CI/CD 中明確設定 ENV=production,並在程式碼裡僅根據此變數決定文件路由。
  2. 最小化公開資訊:在 Production 中同時關閉 openapi_url,即使有人猜到 /docs 位置也無法取得 schema。
  3. 自動化測試:在測試階段加入 安全檢查,確保 /docs/redoc/openapi.json 在 Production 環境返回 404403
  4. 文件管理分離:若公司需要正式的 API 文件,建議使用 SwaggerHub、Redocly、或自建的 API Portal,而非 FastAPI 內建 UI。
  5. 日誌與監控:在關閉文件後,仍可在日誌中記錄對 /docs/redoc 的請求,以偵測潛在的掃描或攻擊行為。

實際應用場景

場景 為什麼要關閉文件 實作方式
金融業線上交易平台 法規要求隱藏所有 API 結構,避免資安風險。 Dockerfile 中設定 ENV=production,使用 範例 2 的環境變數控制,並將 openapi_url=None
大型電商的微服務 每個微服務都有內部測試文件,但外部只提供統一的 API Portal。 在每個服務的 startup 事件使用 範例 3 直接移除 /docs/redoc,同時保留 openapi.json 供內部 CI 使用。
內部工具平台(僅公司員工使用) 需要文件給開發者,但不希望公開給外部網路。 使用 範例 5 的 Middleware,僅允許公司內部 IP 存取文件。
多租戶 SaaS 每個租戶都有自己的子域名,文件只允許租戶管理員查看。 結合 OAuth2API Key,在 Middleware 中驗證權限後才返回文件,否則回傳 403
快速原型驗證 開發階段需要即時測試,部署後立即關閉。 在 CI pipeline 完成部署後,執行 kubectl set env deployment/myapi ENV=production,觸發 範例 2 的自動切換。

總結

  • 關閉 /docs/redoc 是 Production 環境的安全基本功,能有效降低資訊外洩與不必要的效能負擔。
  • FastAPI 提供了 多種彈性方式(在實例化時直接設定、環境變數控制、啟動事件移除、OpenAPI 自訂、Middleware 攔截)讓開發者依需求選擇最合適的方案。
  • 最佳實踐:使用環境變數統一管理、同時關閉 openapi_url、加入自動化安全測試與日誌監控,確保文件在 Production 完全不可被未授權的使用者取得。
  • 在實務上,根據 業務需求(金融、電商、內部工具)與 部署管線(Docker、K8s、CI/CD)選擇適當的實作方式,可讓你的 FastAPI 應用在保護資訊安全的同時,仍保持開發效率與部署彈性。

祝你在 FastAPI 的開發與部署旅程中,既能快速迭代,又能安全無憂! 🚀