MCP StreamableHTTP 协议 421 Misdirected Request 异常深度排查与解决方案

0 阅读4分钟

一、问题概述

基于 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 请求头白名单校验

  1. 默认授信白名单硬编码:仅 127.0.0.1、localhost公网域名、服务器公网IP、Docker容器内网网段(172.30.0.x)全部不在信任列表
  2. Nginx反向代理默认行为:未手动配置Header透传时,转发后端Host变为后端容器内网IP,域名访问时Host为业务域名,均不在默认白名单;
  3. 校验失败日志特征:Invalid Host header: [域名/IP],紧接着返回421。

2. 第二层:Origin 同源跨域校验(最易被忽略,Host正确依旧报错元凶)

MCP 流式协议强制做同源校验:Origin 请求头必须和Host域名同源

  1. 客户端直连场景:工具自动携带合法Origin,校验通过;
  2. Nginx默认不转发Origin头,后端收到Origin为空;空Origin与Host域名无法匹配,同源校验失败,即便Host已经是合法业务域名,依然返回421;
  3. 多数运维仅配置Host透传、遗漏Origin配置,导致改完Nginx Host配置后异常依旧。

三、本地正常、代理转发异常核心原因

  1. 本地直连:客户端Host为127.0.0.1/localhost,命中默认白名单,Origin自动生成合规,双层校验全通过;
  2. 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报错。

五、上线验证步骤

  1. 服务器本地curl自测:携带业务域名Host、API密钥访问后端端口,确认无421;
  2. 外网通过域名+Nginx代理访问,查看应用日志不再打印Invalid Host header
  3. Apifox MCP配置填入公网域名地址、鉴权Header,正常拉取工具列表即修复完成。

六、总结

MCP的421报错并非接口异常,是协议内置的安全防护策略+反向代理Header丢失共同导致,排查优先顺序:

  1. 优先补齐Nginx Host+Origin双Header透传;
  2. 应用侧添加业务域名至授信白名单(规范生产);
  3. 调试场景使用猴子补丁临时关闭Host校验。