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

  1. 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.
  2. If you control the redirecting server, correct the redirect target scheme.
  3. 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

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


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/9bf6e728d2a213f4. Report an issue: GitHub.