BerriAI/litellm · error · SSRFError
URL scheme is not allowed
Error message
URL scheme is not allowed
What it means
Raised by litellm's same-origin redirect validator when a candidate (redirect target) URL's scheme is not in the allowed http/https set. During SSRF-protected fetches (safe_get/async_safe_get), every redirect hop must pass _validate_same_origin, and a Location header pointing at a non-web scheme (ftp://, file://, data:) is rejected before any connection is made. Like the other origin errors, the message intentionally does not echo the offending URL to avoid leaking infrastructure details to a potential attacker.
Source
Thrown at litellm/litellm_core_utils/url_utils.py:346
configured ``api_base``, but the URL it hands back must actually point
back at the same origin or we'd be blindly forwarding credentials
wherever the upstream told us to.
Hostnames are compared case-insensitively. Default ports are made
explicit (HTTP→80, HTTPS→443) so ``https://api.example.com:443/...``
and ``https://api.example.com/...`` are treated as the same origin.
Error messages identify *which* component mismatched but never echo
the operator's ``expected`` host or the candidate's hostname back to
the caller — in the SSRF threat model the caller is the attacker,
and reflecting host info would be a secondary leak of operator
infrastructure details.
"""
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
View on GitHub (pinned to 6c2dcb801b)
Solutions
- Fetch the URL outside the proxy (curl -I) and inspect the Location header of the 3xx response; fix the destination to an http(s) URL.
- If you control the redirecting server, correct the redirect target scheme.
- If this is unexpected on a trusted URL, treat it as suspicious — the redirect chain may have been tampered with.
Example fix
# server-side before: redirect to non-web scheme # Location: ftp://cdn.example.com/file # after # Location: https://cdn.example.com/file
Defensive patterns
Strategy: try-catch
Validate before calling
from urllib.parse import urlparse
def location_is_http(location: str) -> bool:
if "://" not in location:
return True # relative, inherits scheme
return urlparse(location).scheme in {"http", "https"} Try / catch
from litellm.litellm_core_utils.url_utils import SSRFError
try:
resp = safe_get(client, url)
except SSRFError as e:
if "scheme is not allowed" in str(e) or "Origin mismatch" in str(e):
return bad_request("redirect target rejected by SSRF policy")
raise Prevention
- Pre-check redirect targets you control: Location must be http(s) or relative.
- Do not echo rejected URLs back to end users — litellm omits them deliberately.
- Audit any redirect chain that trips scheme checks; it may be tampering.
When it happens
Trigger: safe_get on a URL whose response is a redirect (3xx) with a Location header using a scheme other than http/https — e.g. Location: ftp://evil.example.com/x or a malformed relative value that httpx.URL.join resolves to an unexpected scheme.
Common situations: Servers misconfigured to redirect HTTP endpoints to ftp:// or other schemes; attacker-controlled redirect targets probing the proxy; CDN misconfigurations returning exotic scheme redirects; test servers returning hand-crafted Location headers.
Related errors
- Origin mismatch on scheme
- URL scheme '{parsed.scheme}' is not allowed
- Origin mismatch on host
- Origin mismatch on port
- MAVVRIK_API_ENDPOINT host must be a Mavvrik domain (e.g. htt
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/9bf6e728d2a213f4.
Report an issue: GitHub.