BerriAI/litellm · error · SSRFError
Origin mismatch on port
Error message
Origin mismatch on port
What it means
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.
Source
Thrown at litellm/litellm_core_utils/url_utils.py:360
candidate: Final = urlparse(candidate_url)
expected: Final = urlparse(expected_url)
if candidate.scheme not in _ALLOWED_SCHEMES:
raise SSRFError("URL scheme is not allowed")
if candidate.scheme != expected.scheme:
raise SSRFError("Origin mismatch on scheme")
candidate_host: Final = _normalize_host(candidate.hostname or "")
expected_host: Final = _normalize_host(expected.hostname or "")
if not candidate_host or candidate_host != expected_host:
raise SSRFError("Origin mismatch on host")
default_port: Final = 443 if candidate.scheme == "https" else 80
candidate_port: Final = candidate.port if candidate.port is not None else default_port
expected_port: Final = expected.port if expected.port is not None else default_port
if candidate_port != expected_port:
raise SSRFError("Origin mismatch on port")
_MAX_REDIRECTS: Final = 10
def _extract_redirect_url(response: Any, request_url: str) -> str:
"""Extract and resolve the redirect target from a response's Location header."""
location: Final = response.headers.get("location")
if not isinstance(location, str) or not location:
raise SSRFError("Redirect response has no Location header")
# Resolve relative URLs against the request URL
return str(httpx.URL(request_url).join(location))
def safe_get(client: Any, url: str, **kwargs: Any) -> Any:
"""
Fetch a user-supplied URL with SSRF protection on every redirect hop.
View on GitHub (pinned to 6c2dcb801b)
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.
Example fix
# before resp = safe_get(client, "https://api.example.com:8443/v1/file") # 301 -> :443 # SSRFError: Origin mismatch on port # after resp = safe_get(client, "https://api.example.com/v1/file")
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
def canonical_origin_url(url: str) -> str:
# ensure the port you pass matches the port the service canonicalizes to
return url # document/verify the canonical port before calling safe_get Try / catch
from litellm.litellm_core_utils.url_utils import SSRFError
try:
resp = safe_get(client, url)
except SSRFError as e:
if "Origin mismatch on port" in str(e):
return bad_request("redirect changes port; request the canonical port directly")
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Origin mismatch on scheme
- Origin mismatch on host
- URL scheme is not allowed
- MAVVRIK_API_ENDPOINT host must be a Mavvrik domain (e.g. htt
- Mavvrik FOCUS destination: {label} must be a GCS endpoint (s
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/afbfc2822c3a705b.
Report an issue: GitHub.