dgtlmoon/changedetection.io · critical · Exception
Redirect blocked: '{redirect_url}' resolves to a private/res
Error message
Redirect blocked: '{redirect_url}' resolves to a private/reserved IP address or contains a parser-differential payload. What it means
The requests fetcher manually follows redirects (allow_redirects=False) and blocks any Location target that resolves to a private/reserved IP or looks like a parser-differential (SSRF) payload, when allow_iana_restricted is off.
Source
Thrown at changedetectionio/content_fetchers/requests.py:116
data=request_body.encode('utf-8') if type(request_body) is str else request_body,
url=url,
headers=request_headers,
timeout=timeout,
proxies=proxies,
verify=False,
allow_redirects=False)
# Manually follow redirects so each hop's resolved IP can be validated,
# preventing SSRF via an open redirect on a public host.
current_url = url
for _ in range(10):
if not r.is_redirect:
break
location = r.headers.get('Location', '')
redirect_url = urljoin(current_url, location)
if not allow_iana_restricted:
if is_url_private_or_parser_confused(redirect_url):
raise Exception(f"Redirect blocked: '{redirect_url}' resolves to a private/reserved IP address "
f"or contains a parser-differential payload.")
current_url = redirect_url
r = session.request('GET', redirect_url,
headers=request_headers,
timeout=timeout,
proxies=proxies,
verify=False,
allow_redirects=False)
else:
raise Exception("Too many redirects")
except Exception as e:
msg = str(e)
if proxies and 'SOCKSHTTPSConnectionPool' in msg:
msg = f"Proxy connection failed? {msg}"
raise Exception(msg) from e
# If the response did not tell us what encoding format to expect, Then use chardet to override what `requests` thinks.View on GitHub (pinned to 5d9c7c6da7)
Solutions
- Verify the redirect chain (curl -IL) and where Location points
- If the internal target is legitimate in your network, configure the fetcher to allow it / set allow_iana_restricted appropriately
- Re-check the watch URL — a changed or hijacked domain may be redirecting somewhere unexpected
Defensive patterns
Strategy: try-catch
Validate before calling
# Resolve redirect target host pre-check (mirror of guard)
import socket, ipaddress, urllib.parse
loc = urljoin(url, response.headers.get('Location',''))
host = urllib.parse.urlparse(loc).hostname
ip = socket.gethostbyname(host)
assert not ipaddress.ip_address(ip).is_private, 'redirect would be blocked' Try / catch
try:
fetcher.run()
except Exception as e:
if 'Redirect blocked' in str(e):
log_security_event(str(e)) # potential SSRF attempt by target site
disable_watch_or_alert() Prevention
- Treat this error as a security signal, not a bug — alert on it
- Watch final canonical URLs directly to avoid redirect chains
- Keep the SSRF protection enabled in shared/multi-tenant deployments
When it happens
Trigger: A watched public URL redirecting to http://127.0.0.1, 10.x, 169.254.169.254, or a URL with mixed encodings that parsers resolve differently.
Common situations: Sites redirecting to internal hosts; malicious or compromised target attempting SSRF via redirect; legitimate services redirecting to internal hostnames in on-prem networks.
Related errors
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/9e699a0e737729f1.
Report an issue: GitHub.