FastAPI
單元:中介層(Middleware)
主題:HTTPSRedirect Middleware
簡介
在現代 Web 應用程式中,HTTPS 已成為基本安全需求**,它不僅能防止資料在傳輸過程中被竊聽或竄改,還能提升搜尋引擎排名與使用者信任**。然而,許多開發者在部署 FastAPI 時,往往只在伺服器層面(例如 Nginx、Apache)設定 SSL,卻忽略了應用程式本身的「自動轉向」機制。
FastAPI 內建的 HTTPSRedirectMiddleware 正是為了在 HTTP 請求自動跳轉至 HTTPS 而設計的輕量級中介層。只要在程式碼中加入一行,即可確保所有進入的 HTTP 請求被正確導向到安全的 HTTPS 端點,減少因手動設定遺漏而產生的安全漏洞。
本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,帶你完整掌握 HTTPSRedirectMiddleware 的使用方式,讓你的 FastAPI 專案在 安全性與可維護性 兩方面都更上一層樓。
核心概念
什麼是 Middleware?
在 FastAPI(以及底層的 Starlette)中,Middleware 是一段位於請求(Request)與回應(Response)之間的程式碼,負責攔截、修改或記錄 HTTP 流量。它的執行順序遵循「先註冊的先執行」的原則,且每個 Middleware 必須呼叫 await call_next(request) 才能將控制權交給下游的處理流程。
HTTPSRedirectMiddleware 繼承自 Starlette 的 BaseHTTPMiddleware,其核心邏輯非常簡單:
- 檢查
request.url.scheme是否為"http"。 - 若是,產生一個 301(永久)或 307(暫時) 的重新導向回應,目標 URL 為相同的主機與路徑,但使用
https。 - 若不是,直接把請求傳遞給下游的路由或其他 Middleware。
重點:此 Middleware 只負責「協議層」的轉向,不會處理 證書驗證、HSTS(HTTP Strict Transport Security)等其他安全機制——這些仍需自行在伺服器或額外 Middleware 中設定。
為什麼要在程式碼層面加上 HTTPS 轉向?
- 防止遺漏:即使在反向代理(Reverse Proxy)上已設定 SSL,若使用者直接以 HTTP 請求到達 FastAPI(例如在本機測試),仍可能收到未加密的回應。
- 一致性:在多個環境(開發、測試、正式)中,僅透過環境變數切換即可開關轉向,避免在不同環境手動調整 Nginx 設定。
- 支援自訂域名與子路徑:
HTTPSRedirectMiddleware會自動保留原始的 host、port、path 與 query string,確保使用者不會因為轉向而失去原本的網址資訊。
程式碼範例
以下示範 5 個實用的範例,從最簡單的套用到進階的自訂行為,都以 Python 為例(使用 python 標記)。
1️⃣ 基本使用:直接套用 Middleware
from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
app = FastAPI()
# 只要在開發或測試環境不需要,正式環境直接掛上即可
app.add_middleware(HTTPSRedirectMiddleware)
@app.get("/")
def read_root():
return {"message": "Hello, HTTPS!"}
說明:只要有 HTTP 請求進來,FastAPI 會自動回傳
307 Temporary Redirect,指向相同的 URL 但使用https。
2️⃣ 設定 永久轉向 (301)
HTTPSRedirectMiddleware 預設使用 307,若想改為 301(搜尋引擎友好),可以自行繼承或使用 RedirectResponse:
from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
class PermanentHTTPSRedirectMiddleware(HTTPSRedirectMiddleware):
async def dispatch(self, request: Request, call_next):
if request.url.scheme == "http":
url = request.url.replace(scheme="https")
return RedirectResponse(url=str(url), status_code=301)
return await call_next(request)
app = FastAPI()
app.add_middleware(PermanentHTTPSRedirectMiddleware)
說明:繼承後只改變回應的
status_code,其他行為保持不變。
3️⃣ 在 開發環境 暫時關閉轉向
開發時常使用 http://127.0.0.1:8000,若強制轉向會導致瀏覽器頻繁出現安全警告。以下示範如何根據環境變數自動開關:
import os
from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
app = FastAPI()
if os.getenv("ENVIRONMENT") == "production":
app.add_middleware(HTTPSRedirectMiddleware) # 只在正式環境啟用
@app.get("/info")
def get_info():
return {"env": os.getenv("ENVIRONMENT", "development")}
技巧:在
docker-compose.yml或 CI/CD pipeline 中設定ENVIRONMENT=production,即可無痛切換。
4️⃣ 反向代理(如 Nginx)下的特殊處理
當 FastAPI 後面有 Nginx 代理,且 Nginx 已將 HTTPS 終止,FastAPI 收到的仍是 http。此時需要透過 X-Forwarded-Proto 標頭告訴 Middleware 真實協議:
from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware
class ProxyHeadersHTTPSRedirectMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
# 依據 X-Forwarded-Proto 判斷原始協議
forwarded_proto = request.headers.get("x-forwarded-proto")
scheme = forwarded_proto or request.url.scheme
if scheme == "http":
url = request.url.replace(scheme="https")
return RedirectResponse(url=str(url), status_code=307)
return await call_next(request)
app = FastAPI()
app.add_middleware(ProxyHeadersHTTPSRedirectMiddleware)
說明:在 Nginx 配置中加入
proxy_set_header X-Forwarded-Proto $scheme;,即可讓此 Middleware 正確辨識。
5️⃣ 與其他 Middleware 同時使用(順序示範)
如果你同時使用 CORS、GZip、HTTPSRedirect,必須注意加入的順序。以下示範正確的排列方式:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
app = FastAPI()
# 1. 先處理跨域,讓瀏覽器在 HTTP 階段就能取得 CORS 回應
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# 2. 再壓縮回應(不影響轉向)
app.add_middleware(GZipMiddleware, minimum_size=1000)
# 3. 最後放置 HTTPSRedirect,確保所有非安全請求被導向
app.add_middleware(HTTPSRedirectMiddleware)
@app.get("/data")
def get_data():
return {"msg": "This response may be gzipped if large enough."}
重點:
HTTPSRedirectMiddleware必須放在 最後,否則後續的壓縮或 CORS 可能在 HTTP 請求上被觸發,浪費資源。
常見陷阱與最佳實踐
| 陷阱 | 可能的後果 | 解決方案或最佳實踐 |
|---|---|---|
| 在開發環境未關閉 | 瀏覽器不斷跳回 HTTPS,導致 SSL 證書錯誤 或 自簽證書 警告 | 使用環境變數或 if settings.debug: ... 只在正式環境啟用 |
| 與反向代理協議不一致 | 仍收到 HTTP 回應,使用者資料被明文傳輸 | 在 Nginx/Traefik 設定 X-Forwarded-Proto,並在 Middleware 讀取此標頭 |
| 重複加入 Middleware | 出現 雙重 301/307 迴圈,最終導致瀏覽器「過多重新導向」錯誤 | 確認 app.add_middleware 只呼叫一次,或使用 app.middleware("http") 裝飾器取代 |
轉向前已產生回應 (如在 ExceptionMiddleware 前) |
轉向無法執行,錯誤訊息仍以 HTTP 回傳 | 確保 HTTPSRedirectMiddleware 登記在 所有其他 Middleware 之前(即最外層) |
| 忘記設定 HSTS | 雖然已轉向 HTTPS,仍允許瀏覽器以 HTTP 請求連線,降低安全性 | 在 CDN 或 Nginx 中加入 Strict-Transport-Security 標頭;或自行寫 SecurityHeadersMiddleware 追加此標頭 |
最佳實踐清單
- 只在 Production 啟用:以
ENVIRONMENT、DEBUG或settings.is_production判斷。 - 配合 HSTS:在最外層加上
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload。 - 使用 301 永久轉向:對外部搜尋引擎友好,但在測試階段仍建議保留 307,避免快取問題。
- 保持 Middleware 執行順序:
HTTPSRedirectMiddleware應是 最後 加入的 HTTP Middleware(除非有特殊需求)。 - 在 Docker / Kubernetes 中統一設定:將
ENVIRONMENT=production放在容器的環境變數,避免忘記手動切換。
實際應用場景
1️⃣ SaaS 平台的多租戶服務
一個提供 SaaS 服務的公司,所有客戶都必須透過 HTTPS 交換機密資訊(帳號、付款資料)。即使客戶自行在防火牆或本機測試,仍可能誤以為未加密。透過在 FastAPI 專案中加入 HTTPSRedirectMiddleware,可以保證 所有入口(無論是 CDN、負載平衡器或直接 IP)都會自動導向安全通道,省去在每個子域名或客戶環境中個別設定的麻煩。
2️⃣ 微服務架構中的內部 API
在微服務叢集內部,部分服務仍使用 HTTP(因為在同一個 VPC 中被認為足夠安全),但對外的 API 必須使用 HTTPS。此時可以在 gateway / API 聚合層(如 FastAPI 作為 API Gateway)加入 HTTPSRedirectMiddleware,讓外部請求自動升級,而內部服務保持輕量的 HTTP 通訊。
3️⃣ IoT 裝置的遠端管理平台
許多 IoT 裝置僅支援 HTTP 直接連線,但管理平台必須確保管理者的操作是加密的。透過在 FastAPI 管理介面加上 HTTPSRedirectMiddleware,即使裝置端仍使用 HTTP,管理者的 Web UI 仍會被強制升級到 HTTPS,降低敏感指令被竊聽的風險。
總結
HTTPSRedirectMiddleware 是 FastAPI 提供的 零配置、即插即用 的安全防護機制,讓開發者能在程式碼層面確保所有 HTTP 請求自動導向 HTTPS。透過本文的五個實作範例,你可以:
- 快速上手:只要一行
app.add_middleware(HTTPSRedirectMiddleware)。 - 自訂行為:如改用 301 永久轉向或依據
X-Forwarded-Proto判斷。 - 彈性開關:根據環境變數在開發與正式環境間切換。
- 正確排序:與其他 Middleware(CORS、GZip、SecurityHeaders)協同工作。
在實務上,將 HTTPS 轉向納入程式碼,可以避免因部署環境差異產生的安全漏洞,同時減少在反向代理或負載平衡器上重複設定的工作量。結合 HSTS、安全標頭 以及 適當的環境切換,即可打造一個 安全、可維護且易於部署 的 FastAPI 服務。
最後提醒:安全是一個多層防禦的概念,
HTTPSRedirectMiddleware只是第一層;請務必配合 SSL 證書、HSTS、CSP、Rate Limiting 等機制,才能提供完整的防護。祝開發順利,讓你的 API 在安全與效能上都能發光發熱!