Flask配置ProxyFix后仍识别Docker容器名而非真实域名问题
我的Nginx和Flask应用运行在同一Docker网络,已为基于Gunicorn的Flask配置Nginx反向代理,并设置ProxyFix修正url_for()生成的路由。目前X-Forwarded-Proto和X-Forwarded-Prefix已生效,但ProxyFix未能将请求的真实域名替换掉proxy_pass中使用的Docker容器名api-service。
使用url_for()生成Google OAuth的redirect_uri时,得到的地址为https://api-service/api/authorize/callback/,被Google拒绝;未配置ProxyFix时地址为http://api-service/authorize/callback。
我怀疑Nginx中$cors_origin未被正确设置,导致X-Forwarded-Host fallback到容器名,但浏览器请求头显示origin、referrer和host均为正确的domain1.com。
相关配置
Nginx配置
http { upstream api-service { server api-service:5010; } # Redirect HTTP to HTTPS server { listen 80; listen [::]:80; server_name domain1.com domain2.com; return 301 https://$host$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; http2 on; server_name domain1.com; location /api/ { # Dynamically set the CORS origin based on the incoming request set $cors_origin ""; if ($http_origin ~* (http://localhost:4025|https://domain1.com)) { set $cors_origin $http_origin; } # Add basic CORS headers for all requests add_header 'Access-Control-Allow-Origin' $cors_origin always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Accept, Content-Type, Authorization' always; # Handle CORS preflight (OPTIONS) requests if ($request_method = OPTIONS) { add_header 'Access-Control-Max-Age' 1728000; return 204; } # Handle other requests if ($request_method != OPTIONS) { add_header 'Access-Control-Allow-Credentials' 'true' always; } # Proxy to the API service proxy_pass http://api-service/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $cors_origin; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Prefix /api; proxy_redirect off; proxy_connect_timeout 500s; proxy_send_timeout 500s; proxy_read_timeout 500s; } } }
Flask配置
app = Flask(__name__) app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1, x_proto=1, x_prefix=1)
OAuth相关代码
authorization_url = client.prepare_request_uri( provider_data["authorize_url"], redirect_uri=url_for( "authorize.oauth2_callback", provider=provider, _external=True ), scope=provider_data["scopes"], )
解决方案
问题核心在于Nginx的X-Forwarded-Host设置:你使用$cors_origin传递主机名,但该变量仅在请求包含符合规则的Origin头时才会被赋值,而OAuth授权流程中的后端请求(比如生成redirect_uri的逻辑)可能没有Origin头,导致$cors_origin为空,ProxyFix只能 fallback 到容器名。
步骤1:修正Nginx的X-Forwarded-Host配置
直接使用$host或$http_host传递真实请求域名,替换原有的$cors_origin:
proxy_set_header X-Forwarded-Host $host; # 若需保留端口信息,可改用$http_host # proxy_set_header X-Forwarded-Host $http_host;
步骤2:检查Flask的SERVER_NAME配置
如果Flask设置了SERVER_NAME,它会覆盖X-Forwarded-Host的作用,确保不要设置该参数,或设置为真实域名:
# 注释或删除该配置,避免覆盖反向代理传递的主机信息 # app.config['SERVER_NAME'] = 'domain1.com'
内容的提问来源于stack exchange,提问作者MKF

