Skip to content

Does same-origin redirect enforcement break MCP servers behind a reverse proxy? #3504

Description

@parallelromb

Reading the v2.2.0 release notes: the HTTP client now fails redirects with MCPError unless the target is same-origin. For self-hosted setups this seems like it could bite people running an MCP server behind something like Caddy or nginx — e.g. an HTTP→HTTPS redirect, or a redirect from a bare port to a subpath, which often lands on a different-looking origin even though it's the same server.

The changelog doesn't say whether there's a way to allowlist additional origins for these cases, or whether the guidance is simply "don't let your reverse proxy redirect, terminate TLS and proxy_pass directly." Is there a documented escape hatch, or is this an intentional hard stop meant to force removing redirects from self-hosted deployments entirely? A line in the migration notes about the reverse-proxy case would save people a confusing debugging session when their server that worked in 2.1.x suddenly throws on upgrade.

Activity

  1. arhancanli commented on Oct 9, 2026

    @arhancanli

    Read against main (v2.3.0 plus one commit), src/mcp/shared/_httpx_utils.py. The two cases you list come out differently, and there is no allowlist.

    Followed (no change needed):

    • http→https on the same host with default ports (_within_origin: url.scheme == "http", both ports None, same host, location.scheme == "https").
    • Same scheme/host/port redirects such as trailing-slash normalisation, as long as the method is kept: 307/308 for a POST, any redirect status for the GET stream. At most client.max_redirects hops.

    Not followed:

    • A redirect to a different host, port or scheme. A bare port to a subpath on the same origin is fine, because a path change doesn't change the origin, but if the proxy sends clients from :8080 to :443, or from http://host:8000 to https://host, that is a different origin and fails. The http→https exception only covers default ports on both sides.
    • A 301/302/303 on a POST, because httpx2 would turn it into a body-less GET and drop the message. That is the one a proxy rule like return 301 https://... hits when the configured URL was already http://host/mcp on a non-default port.
    • A Location that carries its own userinfo.

    The follow_redirects flag on your own http_client is ignored (the docstring says so: "The client's follow_redirects setting is not consulted"), so there is no client-side escape hatch. The workaround is to give the client the final URL: configure https://host/mcp directly, and have the proxy answer on the path the client uses with proxy_pass instead of a redirect. For nginx, return 308 instead of 301 also lets a same-origin rewrite through; a cross-origin one will still be refused.

    On failure the error names the redirect target (query and userinfo stripped), so the message tells you which Location to put in the configured URL. A line in the 2.2 migration notes would help, since the symptom looks like a connection error, not a config problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions