FastApi+SqlAdmin经Apache反向代理时/admin路由出现307重定向问题
问题:FastAPI+SqlAdmin在Apache反向代理下/admin路由重定向异常
问题现象
- API端点(如
https://example.com/users/)可正常通过SSL访问,返回200 OK - 访问
https://example.com/admin时出现异常重定向流程:- 307 Temporary Redirect 到
http://example.com/admin - 302 Not Found
- 最终重定向至
https://example.comadmin/(域名与路径拼接错误)
- 307 Temporary Redirect 到
相关配置
Apache配置
<VirtualHost ip:80> ServerName subdomain.example.com ServerAlias www.subdomain.example.com ServerAdmin ****@gmail.com Redirect / https://subdomain.example.com </VirtualHost> <VirtualHost ip:443> ServerName subdomain.example.com ServerAlias www.subdomain.example.com ServerAdmin ******@gmail.com ProxyRequests On ProxyPreserveHost On <Proxy *> Order deny,allow Allow from all </Proxy> SSLEngine on SSLCertificateFile ***** ProxyPass / http://example.com:8000/ ProxyPassReverse / http://example.com:8000/ <Location /> Order allow,deny Allow from all </Location> </VirtualHost>
Gunicorn配置
import json import multiprocessing import os workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1") max_workers_str = os.getenv("MAX_WORKERS") use_max_workers = None if max_workers_str: use_max_workers = int(max_workers_str) web_concurrency_str = os.getenv("WEB_CONCURRENCY", None) host = os.getenv("HOST", "0.0.0.0") port = os.getenv("PORT", "80") bind_env = os.getenv("BIND", None) use_loglevel = os.getenv("LOG_LEVEL", "warning") if bind_env: use_bind = bind_env else: use_bind = f"{host}:{port}" cores = multiprocessing.cpu_count() workers_per_core = float(workers_per_core_str) default_web_concurrency = workers_per_core * cores if web_concurrency_str: web_concurrency = int(web_concurrency_str) assert web_concurrency > 0 else: web_concurrency = max(int(default_web_concurrency), 2) if use_max_workers: web_concurrency = min(web_concurrency, use_max_workers) accesslog_var = os.getenv("ACCESS_LOG", "/var/log/gunicorn/access_log") use_accesslog = accesslog_var or "/var/log/gunicorn/access_log" errorlog_var = os.getenv("ERROR_LOG", "/var/log/gunicorn/error_log") use_errorlog = errorlog_var or "/var/log/gunicorn/error_log" graceful_timeout_str = os.getenv("GRACEFUL_TIMEOUT", "120") timeout_str = os.getenv("TIMEOUT", "120") keepalive_str = os.getenv("KEEP_ALIVE", "5") # Gunicorn config variables loglevel = use_loglevel workers = web_concurrency bind = use_bind errorlog = errorlog worker_tmp_dir = "/dev/shm" accesslog = use_accesslog graceful_timeout = int(graceful_timeout_str) timeout = int(timeout_str) keepalive = int(keepalive_str) # For debugging and testing log_data = { "loglevel": loglevel, "workers": workers, "bind": bind, "graceful_timeout": graceful_timeout, "timeout": timeout, "keepalive": keepalive, "errorlog": errorlog, "accesslog": accesslog, # Additional, non-gunicorn variables "workers_per_core": workers_per_core, "use_max_workers": use_max_workers, "host": host, "port": port, } print(json.dumps(log_data))
已尝试的无效方法
- 添加FastAPI的
HTTPSRedirectMiddleware,导致Gunicorn返回"WARNING: Invalid HTTP request received." - 使用
--forwarded-allow-ips='*'和--proxy-headers启动Gunicorn,无任何变化
解决思路
1. 修复Apache反向代理的协议传递
SqlAdmin的重定向依赖后端感知正确的请求协议(HTTPS),当前Apache未将协议信息传递给Gunicorn/FastAPI,导致后端认为请求是HTTP,从而生成HTTP的重定向地址。需要在Apache的443虚拟主机中添加以下配置,传递X-Forwarded-Proto头:
RequestHeader set X-Forwarded-Proto "https"
同时确保mod_headers模块已启用,可通过a2enmod headers命令启用。
2. 配置FastAPI信任代理头
在FastAPI实例初始化时,设置trust_proxy=True,让应用识别Apache传递的代理头:
from fastapi import FastAPI app = FastAPI(trust_proxy=True)
这会让FastAPI正确解析X-Forwarded-Proto等头,生成正确的HTTPS重定向地址。
3. 检查SqlAdmin的挂载路径
确认SqlAdmin挂载时是否添加了尾部斜杠,避免路径拼接错误:
from sqladmin import Admin admin = Admin(app, engine) # 确保挂载路径为/admin/而非/admin admin.mount_to(app, path="/admin/")
或者访问时使用https://example.com/admin/(带尾部斜杠)测试,若正常则说明是路径匹配问题,可通过Apache配置强制添加尾部斜杠:
RewriteEngine On RewriteRule ^/admin$ /admin/ [R=301,L]
4. 修正Apache ProxyPass配置
确保ProxyPass和ProxyPassReverse的路径都包含尾部斜杠,避免路径解析错误。当前配置已包含,但可确认example.com:8000是否正确指向Gunicorn服务的地址(若Gunicorn运行在本地,建议用127.0.0.1:8000而非域名)。
5. 禁用Gunicorn的HTTP重定向相关设置
移除之前添加的HTTPSRedirectMiddleware,改为通过代理头传递协议信息的方式处理,避免无效请求报错。
内容的提问来源于stack exchange,提问作者Sepehr Alimohamadi
相关产品推荐
相关产品推荐

