本文 AI 產出,尚未審核

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,其核心邏輯非常簡單:

  1. 檢查 request.url.scheme 是否為 "http"。
  2. 若是,產生一個 301(永久)或 307(暫時) 的重新導向回應,目標 URL 為相同的主機與路徑,但使用 https。
  3. 若不是,直接把請求傳遞給下游的路由或其他 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 追加此標頭

最佳實踐清單

  1. 只在 Production 啟用:以 ENVIRONMENT、DEBUG 或 settings.is_production 判斷。
  2. 配合 HSTS:在最外層加上 Strict-Transport-Security: max-age=31536000; includeSubDomains; preload。
  3. 使用 301 永久轉向:對外部搜尋引擎友好,但在測試階段仍建議保留 307,避免快取問題。
  4. 保持 Middleware 執行順序:HTTPSRedirectMiddleware 應是 最後 加入的 HTTP Middleware(除非有特殊需求)。
  5. 在 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 在安全與效能上都能發光發熱!