{"record":{"id":"25ff484475b51dc4","repo":"BerriAI/litellm","slug":"url-targets-a-blocked-address-resolved-ip-if","errorCode":null,"errorMessage":"URL targets a blocked address ({resolved_ip}). If this is a legitimate internal service, add the host to `user_url_allowed_hosts` in general_settings.","messagePattern":"URL targets a blocked address \\((.+?)\\)\\. If this is a legitimate internal service, add the host to `user_url_allowed_hosts` in general_settings\\.","errorType":"exception","errorClass":"SSRFError","httpStatus":null,"severity":"critical","filePath":"litellm/litellm_core_utils/url_utils.py","lineNumber":291,"sourceCode":"    effective_port: Final = port if port is not None else default_port\n    host_header: Final = _format_host_header(hostname, effective_port, default_port)\n\n    is_allowlisted: Final = _is_host_allowlisted(hostname, effective_port)\n\n    # Resolve hostname and validate ALL addresses\n    try:\n        addrinfo: Final = socket.getaddrinfo(hostname, effective_port, proto=socket.IPPROTO_TCP)\n    except socket.gaierror as e:\n        raise SSRFError(f\"DNS resolution failed for '{hostname}': {e}\")\n\n    if not addrinfo:\n        raise SSRFError(f\"No addresses found for '{hostname}'\")\n\n    if not is_allowlisted:\n        for family, type_, proto, canonname, sockaddr in addrinfo:\n            resolved_ip = _sockaddr_host(sockaddr)\n            if _is_blocked_ip(resolved_ip):\n                raise SSRFError(\n                    f\"URL targets a blocked address ({resolved_ip}). \"\n                    \"If this is a legitimate internal service, add the host \"\n                    \"to `user_url_allowed_hosts` in general_settings.\"\n                )\n\n    # For HTTPS with SSL verification enabled, TLS certificate validation\n    # binds the connection to the hostname — DNS rebinding can't redirect\n    # to a different server because the cert wouldn't match.\n    # When SSL verification is disabled, this defense doesn't apply, so\n    # we rewrite to the validated IP like HTTP.\n    ssl_verify: Final = getattr(litellm, \"ssl_verify\", True)\n    if parsed.scheme == \"https\" and ssl_verify is not False:\n        return url, host_header\n\n    # For HTTP, rewrite URL to connect to the validated IP directly\n    # to prevent DNS rebinding (no TLS to bind the connection).\n    validated_ip: Final = _sockaddr_host(addrinfo[0][4])\n    is_ipv6: Final = addrinfo[0][0] == socket.AF_INET6","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/litellm_core_utils/url_utils.py#L273-L309","documentation":"The core SSRF block from litellm's validate_url: after DNS resolution, every resolved IP is checked with _is_blocked_ip, which blocks any non-global IP (RFC 6890: private, loopback, link-local, CGNAT), multicast, unparseable addresses, IPv4-mapped IPv6 forms, and cloud metadata ranges (e.g. Azure Wire Server 168.63.129.16). If any resolved address is blocked, fetching is refused unless the host:port was admin-allowlisted via user_url_allowed_hosts in general_settings. The message deliberately includes the remediation path.","triggerScenarios":"validate_url/safe_get on a URL whose hostname resolves to a private/internal IP — e.g. http://localhost:8080/fetch, http://169.254.169.254/latest/meta-data (cloud metadata), http://10.0.0.5:9000, or a public-looking DNS name that resolves (round-robin or rebinding) to an internal address. Also triggered when a hostname resolves to multiple addresses and ANY one of them is non-global.","commonSituations":"Legitimate internal services (self-hosted models, internal file/media hosts) referenced by user-supplied URL fields in the proxy; developers testing locally against 127.0.0.1; multi-A records where one record is private; deployments where the 'user URL' feature is exposed to end users who request internal targets (the actual attack this guards against).","solutions":["If the target is a legitimate internal service, ask the proxy admin to add the exact host (optionally host:port, e.g. 'internal-api.corp:8080') to user_url_allowed_hosts under general_settings in the litellm config, then restart the proxy.","If you did not expect an internal IP, investigate the DNS name — it may be a DNS rebinding attempt or a misconfigured record.","Point the URL at a globally routable endpoint instead (public hostname or correct external IP).","Admins: remember allowlisting skips IP checks for that host, so only allowlist hosts you control and trust."],"exampleFix":"# before\nresponse = safe_get(client, \"http://internal-llm.corp.local:8080/health\")\n# SSRFError: URL targets a blocked address (10.1.2.3)...\n\n# after: litellm_config.yaml\n# general_settings:\n#   user_url_allowed_hosts:\n#     - \"internal-llm.corp.local:8080\"\nresponse = safe_get(client, \"http://internal-llm.corp.local:8080/health\")","handlingStrategy":"try-catch","validationCode":"import socket\nfrom ipaddress import ip_address\n\ndef all_ips_global(hostname: str) -> bool:\n    try:\n        infos = socket.getaddrinfo(hostname, 443, proto=socket.IPPROTO_TCP)\n    except socket.gaierror:\n        return False\n    ips = [i[4][0] for i in infos]\n    return all(_is_public(ip) for ip in ips)\n\ndef _is_public(addr: str) -> bool:\n    try:\n        ip = ip_address(addr)\n    except ValueError:\n        return False\n    return ip.is_global and not ip.is_multicast","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 \"blocked address\" in str(e):\n        return bad_request(\"target is not reachable under SSRF policy; ask the admin to allowlist it\")\n    raise","preventionTips":["Do NOT auto-add hosts to user_url_allowed_hosts; that skips IP checks — make it a deliberate admin decision.","Test locally with globally routable hosts, or run the proxy with an allowlist entry for your dev host.","Remember every resolved A/AAAA record must be global; one private record blocks the hostname.","Never point user-supplied URL features at 169.254.169.254 or loopback."],"tags":["ssrf","security","network","private-ip","dns-rebinding","cloud-metadata"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}