{"record":{"id":"afbfc2822c3a705b","repo":"BerriAI/litellm","slug":"origin-mismatch-on-port","errorCode":null,"errorMessage":"Origin mismatch on port","messagePattern":"Origin mismatch on port","errorType":"exception","errorClass":"SSRFError","httpStatus":null,"severity":"error","filePath":"litellm/litellm_core_utils/url_utils.py","lineNumber":360,"sourceCode":"    candidate: Final = urlparse(candidate_url)\n    expected: Final = urlparse(expected_url)\n\n    if candidate.scheme not in _ALLOWED_SCHEMES:\n        raise SSRFError(\"URL scheme is not allowed\")\n\n    if candidate.scheme != expected.scheme:\n        raise SSRFError(\"Origin mismatch on scheme\")\n\n    candidate_host: Final = _normalize_host(candidate.hostname or \"\")\n    expected_host: Final = _normalize_host(expected.hostname or \"\")\n    if not candidate_host or candidate_host != expected_host:\n        raise SSRFError(\"Origin mismatch on host\")\n\n    default_port: Final = 443 if candidate.scheme == \"https\" else 80\n    candidate_port: Final = candidate.port if candidate.port is not None else default_port\n    expected_port: Final = expected.port if expected.port is not None else default_port\n    if candidate_port != expected_port:\n        raise SSRFError(\"Origin mismatch on port\")\n\n\n_MAX_REDIRECTS: Final = 10\n\n\ndef _extract_redirect_url(response: Any, request_url: str) -> str:\n    \"\"\"Extract and resolve the redirect target from a response's Location header.\"\"\"\n    location: Final = response.headers.get(\"location\")\n    if not isinstance(location, str) or not location:\n        raise SSRFError(\"Redirect response has no Location header\")\n    # Resolve relative URLs against the request URL\n    return str(httpx.URL(request_url).join(location))\n\n\ndef safe_get(client: Any, url: str, **kwargs: Any) -> Any:\n    \"\"\"\n    Fetch a user-supplied URL with SSRF protection on every redirect hop.\n","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/litellm_core_utils/url_utils.py#L342-L378","documentation":"Raised during litellm's SSRF-protected fetch when a redirect target keeps the same scheme and host but changes the effective port (explicit port or scheme-default difference, e.g. https://host:8443 -> https://host:443). Port is part of the origin contract enforced on every redirect hop, so any port change is rejected before the hop is fetched. The specific ports are not included in the message.","triggerScenarios":"safe_get('https://api.example.com:8443/x') where the server redirects to 'https://api.example.com/x' (port implicitly 443), or any 3xx whose Location carries a different explicit port — candidate_port != expected_port in _validate_same_origin.","commonSituations":"Services behind a non-standard port redirecting to their canonical 443 URL (or vice versa); internal tools on :8080 redirecting to a login page on :443; proxies that rewrite ports in Location headers.","solutions":["Call safe_get directly on the canonical final URL (the redirect target's port) so no port-changing hop occurs.","Fix the origin server to keep Location on the same port as the requested URL.","If the port change is legitimate, perform two independent safe_get calls — one per origin — instead of relying on the redirect."],"exampleFix":"# before\nresp = safe_get(client, \"https://api.example.com:8443/v1/file\")  # 301 -> :443\n# SSRFError: Origin mismatch on port\n\n# after\nresp = safe_get(client, \"https://api.example.com/v1/file\")","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\ndef canonical_origin_url(url: str) -> str:\n    # ensure the port you pass matches the port the service canonicalizes to\n    return url  # document/verify the canonical port before calling safe_get","typeGuard":null,"tryCatchPattern":"from litellm.litellm_core_utils.url_utils import SSRFError\n\ntry:\n    resp = safe_get(client, url)\nexcept SSRFError as e:\n    if \"Origin mismatch on port\" in str(e):\n        return bad_request(\"redirect changes port; request the canonical port directly\")\n    raise","preventionTips":["Call safe_get on the service's canonical origin (scheme+host+port) so no port-changing redirect occurs.","Keep Location headers port-consistent on servers you control.","Treat port-mismatch rejections as expected behavior for multi-port services."],"tags":["ssrf","redirect","origin","port","security"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}