fix(deploy): add ProxyHeadersMiddleware for HTTPS behind Caddy
Some checks failed
CI / ruff (push) Failing after 7s
CI / dependency-scanning (push) Successful in 27s
CI / audit (push) Successful in 8s
CI / docs (push) Has been skipped
CI / pytest (push) Failing after 22s
CI / architecture (push) Failing after 9s

Caddy proxies HTTPS requests to FastAPI as HTTP on localhost:8001.
Without ProxyHeadersMiddleware, request.scheme stays "http" and
url_for() generates http:// URLs, causing mixed content blocking.

The middleware reads X-Forwarded-Proto from Caddy and sets the
correct scheme so all generated URLs use https://.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 23:51:29 +01:00
parent c58ceb9872
commit af3f04a23f

View File

@@ -26,6 +26,7 @@ from fastapi import Depends, FastAPI, HTTPException, Request, Response
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
from sentry_sdk.integrations.fastapi import FastApiIntegration from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from sqlalchemy import text from sqlalchemy import text
@@ -119,6 +120,7 @@ app.add_middleware(
# So we add them in REVERSE order of desired execution: # So we add them in REVERSE order of desired execution:
# #
# Desired execution order: # Desired execution order:
# 0. ProxyHeadersMiddleware (trust X-Forwarded-Proto from Caddy)
# 1. PlatformContextMiddleware (detect platform from domain/path) # 1. PlatformContextMiddleware (detect platform from domain/path)
# 2. StoreContextMiddleware (detect store, uses platform_clean_path) # 2. StoreContextMiddleware (detect store, uses platform_clean_path)
# 3. FrontendTypeMiddleware (detect frontend type using FrontendDetector) # 3. FrontendTypeMiddleware (detect frontend type using FrontendDetector)
@@ -163,9 +165,16 @@ app.add_middleware(StoreContextMiddleware)
logger.info("Adding PlatformContextMiddleware (detects platform from domain/path)") logger.info("Adding PlatformContextMiddleware (detects platform from domain/path)")
app.add_middleware(PlatformContextMiddleware) app.add_middleware(PlatformContextMiddleware)
# Add proxy headers middleware (runs before all other middleware)
# Caddy proxies HTTPS → HTTP internally; this reads X-Forwarded-Proto
# so request.scheme = "https" and url_for() generates correct URLs
logger.info("Adding ProxyHeadersMiddleware (trust X-Forwarded-Proto from Caddy)")
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts=["*"])
logger.info("=" * 80) logger.info("=" * 80)
logger.info("MIDDLEWARE ORDER SUMMARY:") logger.info("MIDDLEWARE ORDER SUMMARY:")
logger.info(" Execution order (request →):") logger.info(" Execution order (request →):")
logger.info(" 0. ProxyHeadersMiddleware (proxy headers)")
logger.info(" 1. LoggingMiddleware (timing)") logger.info(" 1. LoggingMiddleware (timing)")
logger.info(" 2. PlatformContextMiddleware (platform detection)") logger.info(" 2. PlatformContextMiddleware (platform detection)")
logger.info(" 3. StoreContextMiddleware (store detection)") logger.info(" 3. StoreContextMiddleware (store detection)")