googleapis/mcp-toolbox · error
redirect to blocked IP %s denied
Error message
redirect to blocked IP %s denied
What it means
During redirect handling, if the next hop's hostname is a literal IP address, the source checks it against the SSRF guard (guard.IsIPBlocked). Blocked IPs include private/loopback/link-local ranges, so this error stops a request from being redirected into an internal network. It is a deliberate SSRF protection rejection.
Source
Thrown at internal/sources/http/http.go:362
if r, ok := resolver.(*net.Resolver); ok {
dialer.Resolver = r
}
tr.DialContext = dialer.DialContext
client := &http.Client{
Timeout: duration,
Transport: tr,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) >= 10 {
return fmt.Errorf("stopped after 10 redirects")
}
hostname := req.URL.Hostname()
if ip := net.ParseIP(hostname); ip != nil {
if guard.IsIPBlocked(ip) {
return fmt.Errorf("redirect to blocked IP %s denied", ip)
}
return nil
}
addrs, err := resolver.LookupHost(req.Context(), hostname)
if err != nil {
return fmt.Errorf("failed to resolve redirect host %s: %w", hostname, err)
}
for _, addr := range addrs {
if ip := net.ParseIP(addr); ip != nil {
if guard.IsIPBlocked(ip) {
return fmt.Errorf("redirect host %s resolves to blocked IP %s", hostname, addr)
}
}
}
return nilView on GitHub (pinned to 8cc6e09de2)
Solutions
- Remove or fix the redirect target so the redirect chain stays on allowed public hosts.
- If the IP is legitimately needed, allowlist it in the guard's blocked-IP policy configuration.
- Verify no user-controlled input influences the initial URL that produces the redirect.
Example fix
// before: original URL redirects to http://169.254.169.254/latest/meta-data/
// after: point the tool at a public endpoint instead
toolArgs{URL: "https://example.com/api"} Defensive patterns
Strategy: validation
Validate before calling
function isPublicIPv4(ip) {
const parts = ip.split('.').map(Number);
if (parts.length !== 4 || parts.some(p => isNaN(p))) return false;
const [a, b] = parts;
const blocked = a === 10 || a === 127 || (a === 172 && b >= 16 && b <= 31) ||
(a === 192 && b === 168) || (a === 169 && b === 254) || a === 0 || a >= 224;
return !blocked;
}
// reject URLs whose host is a blocked literal IP before calling the tool
const u = new URL(url); const host = u.hostname;
if (/^[0-9.]+$/.test(host) && !isPublicIPv4(host)) throw new Error('literal private/metadata IP not allowed'); Try / catch
try {
const result = await callHttpTool(url);
} catch (err) {
if (String(err).includes('redirect to blocked IP')) {
console.error('Redirect target was a blocked/internal IP — remove SSRF-prone redirects from the server.');
} else throw err;
} Prevention
- Never let user input control URLs that can trigger redirects toward internal/metadata addresses.
- Prefer pinned public hostnames over raw IP URLs.
- Review server redirect rules for references to localhost or link-local addresses.
When it happens
Trigger: A 3xx response whose Location points to a URL with a literal IP host (e.g. http://169.254.169.254/ or http://127.0.0.1/) that guard.IsIPBlocked classifies as blocked.
Common situations: SSRF probing of cloud metadata endpoints via a redirect; a misconfigured server redirecting to localhost; user-supplied URL shorteners bouncing to internal addresses.
Related errors
- redirect host %s resolves to blocked IP %s
- URL scheme must be https, got %q
- URL host must be an allowed FHIR host, got %q
- path must be relative and cannot override base host
- failed to parse and verify JWT token: %w
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/5e486112542aea3f.
Report an issue: GitHub.