different-ai/openwork · error · LocalManagedMcpPrivateUrlError
an HTTPS request cannot redirect to a less secure protocol
Error message
an HTTPS request cannot redirect to a less secure protocol
What it means
When following redirects, the guarded fetch refuses to let an https: request be redirected to a non-https protocol (http:, ftp:, etc.). This blocks protocol-downgrade attacks during the redirect chain and throws LocalManagedMcpPrivateUrlError for the target URL.
Source
Thrown at apps/server/src/local-managed-mcp-url-guard.ts:208
type FetchLike = (url: string | URL, init?: RequestInit) => Promise<Response>;
const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
const guardedDispatcher = new Agent({
connect: {
lookup: createLocalManagedMcpPublicLookup(),
// Node's 250 ms family-attempt default is too aggressive for otherwise
// healthy dual-stack MCP providers on some macOS networks. Keep fallback
// enabled, but give the first family enough time to establish TLS before
// trying the validated alternative address.
autoSelectFamily: true,
autoSelectFamilyAttemptTimeout: 1_000,
},
});
function redirectedRequestInit(init: RequestInit | undefined, status: number, from: URL, to: URL): RequestInit {
const headers = new Headers(init?.headers);
if (from.protocol === "https:" && to.protocol !== "https:") {
throw new LocalManagedMcpPrivateUrlError(to.toString(), "an HTTPS request cannot redirect to a less secure protocol");
}
const method = (init?.method ?? "GET").toUpperCase();
if (from.origin !== to.origin) {
if ((method !== "GET" && method !== "HEAD") || init?.body != null) {
throw new LocalManagedMcpPrivateUrlError(to.toString(), "a request body cannot be redirected to another origin");
}
for (const name of ["authorization", "cookie", "proxy-authorization", "mcp-session-id", "last-event-id", "x-api-key", "x-auth-token"]) {
headers.delete(name);
}
}
const switchToGet = (status === 303 && method !== "HEAD") || ((status === 301 || status === 302) && method === "POST");
if (switchToGet) {
headers.delete("content-length");
headers.delete("content-type");
return { ...init, method: "GET", body: undefined, headers, redirect: "manual" };
}
return { ...init, headers, redirect: "manual" };
}View on GitHub (pinned to 2b7df46e8a)
Solutions
- Fix the server/proxy redirect so it targets an https: URL
- If the destination only supports HTTP, use it directly as an intentionally insecure connection via the allowPrivateUrls() path (dev only)
- Trace the redirect chain (curl -I) to find which hop downgrades the scheme
- Update hardcoded redirect/base-URL config to include the https scheme
Example fix
// before (server)
return Response.redirect("http://mcp.example.com/v2", 302);
// after
return Response.redirect("https://mcp.example.com/v2", 302); Defensive patterns
Strategy: try-catch
Validate before calling
const res = await fetch(url, { redirect: "manual" });
if ([301,302,303,307,308].includes(res.status)) {
const next = new URL(res.headers.get("location"), url);
if (next.protocol !== "https:") throw new Error(`Redirect downgrades to ${next.protocol}`);
} Try / catch
try {
await createLocalManagedMcpConnection({ url });
} catch (error) {
if (error instanceof LocalManagedMcpPrivateUrlError && error.message.includes("less secure protocol")) {
// inspect server redirect chain; fix scheme on the redirecting hop
}
throw error;
} Prevention
- Audit server redirects so they always target https://
- Check TLS termination at the proxy preserves the original scheme
- Trace redirect chains with curl -sIL when configuring new endpoints
- Treat any http Location header on a production endpoint as a bug
When it happens
Trigger: An HTTPS MCP request receives a 301/302/303/307/308 response whose Location header points at an http: (or otherwise non-https) URL, while private URLs are not allowed.
Common situations: Misconfigured reverse proxy or load balancer redirecting https to http; redirect target built with a wrong scheme in a server config; intercepted redirect chain during corporate proxy rewriting.
Related errors
- managed MCP egress requires HTTPS
- a request body cannot be redirected to another origin
- connect-link claims contain a non-https URL: ${insecure}
- External URL protocol "${parsed.protocol}" is not allowed.
- invalid_payload
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/58c65012a8bb2d09.
Report an issue: GitHub.