Guide: Resolving “421 Invalid Host Header” (DNS Rebinding Protection)

该报错通常发生在通过反向代理(Nginx、Caddy)、Cloudflare Tunnel、Kubernetes 或自定义域名访问 MCP 服务器时——SDK 自带的 DNS 重绑定防护机制会检查 Host 头,若不在允许列表内则返回 421。优先排查 FastMCP 的 transport_sec

快速结论:该报错通常发生在通过反向代理(Nginx、Caddy)、Cloudflare Tunnel、Kubernetes 或自定义域名访问 MCP 服务器时——SDK 自带的 DNS 重绑定防护机制会检查 Host 头,若不在允许列表内则返回 421。优先排查 FastMCP 的 transport_security 配置,显式加入你的网关域名或端口。

适用环境:MCP Python SDK(v2 相关更新,提交 86bb54c、PR #861 引入 DNS 重绑定防护);受影响文件为 src/mcp/server/fastmcp/server.py:178。Issue 中未明确确认特定 Python/CUDA/显卡版本。

最快修复方案:在 FastMCP 实例化时显式传入 TransportSecuritySettings,将网关或自定义域名加入 allowed_hostsallowed_origins(端口可用 * 通配);确认代理正确传递原始 Host 头。

注意事项:若服务通过 streamable_http_app()sse_app() 挂载到 Starlette/FastAPI 应用时,FastMCP 的 host 参数不控制实际监听地址(由 uvicorn/gunicorn 控制),此场景下默认 host="127.0.0.1" 可能触发自动启用防护,需手动设置。关闭防护仅建议本地开发或已有其他安全边界时使用。

问题场景

用户运行 MCP Python SDK 构建的 FastMCP 服务器,在以下常见部署模式下触发 421:

  • 通过 Nginx、Caddy、Cloudflare Tunnel 等反向代理暴露服务
  • Docker/Kubernetes 容器部署,服务绑定 0.0.0.0
  • 使用自定义域名或集群内部 DNS(如 service.namespace.svc.cluster.local)访问
  • streamable_http_app()sse_app() 挂载到更大的 Starlette/FastAPI 应用

报错原文

421 Misdirected Request / Invalid Host Header

原因分析

可能原因是 SDK 在提交 86bb54c(PR #861)引入了 DNS 重绑定防护:当 FastMCP 的 host 参数被设置为 0.0.0.0(或默认值 127.0.0.1 与代理传入的 Host 不匹配)时,防护机制自动启用并生成空白的 allowed_hosts 列表,导致所有外部请求因 Host 头不在允许列表中而被拒绝。该行为在启动时无警告,错误响应中也没有排查提示。

另一种可能原因是:当服务通过 streamable_http_app()/sse_app() 挂载到更大的 Web 应用时,存在“服务器绑定地址”与“FastMCP host 参数”之间的脱节——即使 uvicorn 监听 0.0.0.0,FastMCP 内部默认 host="127.0.0.1" 也可能触发自动启用逻辑。

环境排查

  • 确认 MCP Python SDK 版本是否包含提交 86bb54c(PR #861)之后的行为变化
  • 确认 uvicorn/gunicorn 实际监听地址(如 0.0.0.0)与 FastMCP host 参数之间的差异
  • 确认反向代理是否修改或传递了正确的 Host
  • 检查是否配置了环境变量(Issue 中社区建议增加 MCP_ALLOWED_HOSTS/MCP_ALLOWED_ORIGINS/MCP_DNS_REBINDING_PROTECTION,但未确认已被实现,需查阅当前 SDK 文档)

解决步骤

  1. 方案一(推荐,保留防护):在 FastMCP 初始化时显式传入 TransportSecuritySettings,将你的网关域名、服务名或自定义域加入白名单(端口可用 * 通配):
    from mcp.server.fastmcp import FastMCP
    from mcp.server.transport_security import TransportSecuritySettings
    
    mcp = FastMCP(
        "MyServer",
        transport_security=TransportSecuritySettings(
            enable_dns_rebinding_protection=True,
            allowed_hosts=["localhost:*", "127.0.0.1:*", "your-gateway-host:*"],
            allowed_origins=["http://localhost:*", "http://your-gateway-host:*"],
        )
    )
    
  2. 方案二(本地开发或已有安全边界时):直接关闭 DNS 重绑定防护:
    transport_security=TransportSecuritySettings(
        enable_dns_rebinding_protection=False,
    )
    
  3. 检查反向代理配置:确保 Nginx/Caddy/Cloudflare Tunnel 将原始 Host 头传递给 MCP 服务器,不要覆盖或重写。
  4. 若使用 streamable_http_app()/sse_app() 挂载:需明确该函数返回的 app 实际由 uvicorn/gunicorn 监听,FastMCP 的 host 参数不控制监听地址。建议始终显式设置 TransportSecuritySettings,不要依赖自动检测。
  5. 可优先尝试:如果不想修改代码,检查是否可通过环境变量配置(截至 Issue 讨论时未确认支持,需查阅最新 SDK 文档)。社区建议的 MCP_ALLOWED_HOSTS/MCP_ALLOWED_ORIGINS/MCP_DNS_REBINDING_PROTECTION 可作为未来改进方向,但在当前版本中可能不可用。

验证方法

修改配置后,通过代理域名或外部地址发起一次 MCP 请求,确认不再返回 421 Invalid Host Header,且响应中无 421 Misdirected Request 状态码。若关闭防护,需确认服务仍能正常处理请求(注意此时 DNS 重绑定防护不再生效)。对于容器部署,可检查代理日志和 MCP 服务器日志确认 Host 头是否匹配白名单。

参考来源

modelcontextprotocol/python-sdk #1798

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 18303

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注