一、问题概述
基于 FastMCP 开发 MCP 工具网关服务时,本地直连服务端口调用正常,经过 Nginx 反向代理转发后持续返回 421 Misdirected Request,伴随日志 Invalid Host header: xxx,Apifox、各类MCP客户端无法建立长连接会话,是MCP HTTP流式传输部署中高频踩坑问题。
421 状态码释义:请求来源域名与服务授信域名不匹配,服务出于DNS重绑定安全策略拒绝连接,区别于常规4xx参数错误。
二、异常触发两层校验机制(FastMCP内置安全规则)
FastMCP 为防范DNS重绑定攻击,对Streamable HTTP传输做双层强制校验,任意一层校验失败直接抛出421。
1. 第一层:Host 请求头白名单校验
- 默认授信白名单硬编码:仅
127.0.0.1、localhost,公网域名、服务器公网IP、Docker容器内网网段(172.30.0.x)全部不在信任列表; - Nginx反向代理默认行为:未手动配置Header透传时,转发后端Host变为后端容器内网IP,域名访问时Host为业务域名,均不在默认白名单;
- 校验失败日志特征:
Invalid Host header: [域名/IP],紧接着返回421。
2. 第二层:Origin 同源跨域校验(最易被忽略,Host正确依旧报错元凶)
MCP 流式协议强制做同源校验:Origin 请求头必须和Host域名同源。
- 客户端直连场景:工具自动携带合法Origin,校验通过;
- Nginx默认不转发Origin头,后端收到Origin为空;空Origin与Host域名无法匹配,同源校验失败,即便Host已经是合法业务域名,依然返回421;
- 多数运维仅配置Host透传、遗漏Origin配置,导致改完Nginx Host配置后异常依旧。
三、本地正常、代理转发异常核心原因
- 本地直连:客户端Host为
127.0.0.1/localhost,命中默认白名单,Origin自动生成合规,双层校验全通过; - Nginx代理链路
- 未配置Header转发:Host被Nginx改写为后端内网IP,第一层Host校验拦截;
- 仅配置Host透传、未配置Origin:Host为业务域名放行第一层,Origin为空触发第二层同源拦截,持续421。
四、三类落地解决方案(按生产优先级排序)
方案1:规范配置(生产环境首选,保留安全校验)
1)Nginx 完整MCP专用代理配置
补全Host、Origin全量Header透传,同时适配MCP长连接流式特性:
location /mcp {
proxy_pass http://后端服务地址:端口;
# 透传原始访问域名
proxy_set_header Host $http_host;
# 透传原始Origin,空Origin自动补全业务域名
proxy_set_header Origin $http_origin;
if ($http_origin = "") {
proxy_set_header Origin "https://业务域名";
}
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# MCP Streamable HTTP长连接必填配置
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 300s;
}
配置校验生效:nginx -t && nginx -s reload
2)应用侧配置域名白名单
通过环境变量注入授信域名,将业务域名加入FastMCP信任列表,无需关闭安全校验:
import os
# 代码初始化最前方配置授信域名
os.environ["FASTMCP_TRANSPORT_SECURITY__ALLOWED_HOSTS"] = '["业务域名"]'
# 如需临时关闭DNS绑定防护(非生产推荐)
# os.environ["FASTMCP_TRANSPORT_SECURITY__ENABLE_DNS_REBINDING_PROTECTION"] = "false"
方案2:临时调试方案:猴子补丁全局禁用Host校验
项目开发、联调阶段快速绕过Host校验规则,直接覆盖FastMCP底层Host校验函数,所有IP/域名全部放行,仅调试使用,不建议正式上线:
# 代码文件最顶部优先导入打补丁
import mcp.server.http.util
old_check_func = mcp.server.http.util.check_host
def patch_check(host):
return True
mcp.server.http.util.check_host = patch_check
搭配上述Nginx Origin转发配置即可彻底消除421。
方案3:废弃无效方案说明
通过Starlette中间件改写Request.scope中的Host无效:中间件修改请求Header仅作用于上层ASGI应用,无法修改FastMCP底层校验逻辑,修改为127.0.0.1依旧会被白名单拦截,出现Invalid Host header:127.0.0.1报错。
五、上线验证步骤
- 服务器本地curl自测:携带业务域名Host、API密钥访问后端端口,确认无421;
- 外网通过域名+Nginx代理访问,查看应用日志不再打印
Invalid Host header; - Apifox MCP配置填入公网域名地址、鉴权Header,正常拉取工具列表即修复完成。
六、总结
MCP的421报错并非接口异常,是协议内置的安全防护策略+反向代理Header丢失共同导致,排查优先顺序:
- 优先补齐Nginx Host+Origin双Header透传;
- 应用侧添加业务域名至授信白名单(规范生产);
- 调试场景使用猴子补丁临时关闭Host校验。