santifer/career-ops · info
Blocked request to restricted destination (DNS): ${requestUr
Error message
Blocked request to restricted destination (DNS): ${requestUrl} - ${err.message} What it means
The guard's second layer — validateUrlSecurity — resolved the request's hostname with real DNS (resolve4 + resolve6 + lookup) and either got no addresses at all (dead host, tagged livenessCode 'dns_no_addresses') or got addresses inside private/loopback space ('Access denied: Egress guard blocked private target IP', a DNS-rebinding-shaped hit). The request is aborted either way. Per the 2026-08-14 measurement note in the source, a dead third-party host (shut-down trackers like addthis) must NOT poison the verdict — only the main document failing to resolve says the posting itself is dead.
Source
Thrown at liveness-browser.mjs:264
}
if (page) {
page._blockedByGuard = null;
}
if (page && typeof page.route === 'function' && !page._routeInterceptorRegistered) {
page._routeInterceptorRegistered = true;
await page.route('**/*', async (route) => {
const requestUrl = route.request().url();
const errGuard = rejectPrivateOrInvalid(requestUrl);
if (errGuard) {
console.warn(`Blocked request to restricted destination: ${requestUrl}`);
page._blockedByGuard = errGuard;
return route.abort('blockedbyclient');
}
try {
await validateUrlSecurity(requestUrl);
return route.continue();
} catch (err) {
console.warn(`Blocked request to restricted destination (DNS): ${requestUrl} - ${err.message}`);
// A host that resolves to nothing is a DEAD THIRD-PARTY SCRIPT, not a
// statement about the posting. Measured 2026-08-14 over a 217-URL
// recheck: 78 live postings were returned as `uncertain` because an
// analytics or ad host on the page no longer exists — 53 on
// personalisation.visitorqueue.com, 17 on s7.addthis.com (AddThis was
// shut down in 2023), the rest on fluidads and cloudfront. One was
// opened by hand to confirm: 11,178 characters of live posting and a
// working apply control, called uncertain because of a dead tracker.
//
// The request is still aborted either way, so the egress guard loses
// nothing. Only the VERDICT stops being poisoned, and only for a
// subresource: if the main document itself cannot resolve, that is a
// real finding about the posting and still counts.
// Whether this was the main document or a subresource can only be asked
// of a real Playwright request. Callers may pass a lighter route double
// (the test suite does, with request() returning just a url()), and for
// those the answer is unknowable — so default to TRUE, which keeps the
// pre-existing behaviour of poisoning the verdict. The relaxation onlyView on GitHub (pinned to 60398d6549)
Solutions
- Treat as noise if the verdict is still live/dead based on the main document — dead subresource hosts are expected on old pages.
- If MANY hosts trip it or everything returns uncertain, check your own DNS: dig the blocked hostname and look for wildcard or internal answers (VPN, captive portal).
- Confirm the posting's own hostname resolves publicly before re-running the check.
Defensive patterns
Strategy: validation
Validate before calling
import dns from 'node:dns/promises';
const addrs = await dns.lookup(host, { all: true }).catch(() => []);
const isPrivate = (a) => /^(10\.|127\.|169\.254\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|::1|f[cd])/i.test(a);
if (addrs.length === 0 || addrs.some((x) => isPrivate(x.address))) {
console.warn(`host ${host} is dead or resolves privately — expect guard blocks`);
} Prevention
- Run liveness checks from a network with clean public DNS — no captive portals or VPN split-DNS wildcards
- Remember DNS answers are memoized per process (dnsCache); a stale block persists until restart
- Distinguish dns_no_addresses (dead tracker, ignorable) from 'blocked private target IP' (real rebinding signal)
When it happens
Trigger: The page references analytics hosts that no longer exist (ENOTFOUND → dns_no_addresses); a public hostname resolves into 10.x/127.0.0.1/169.254.x (rebinding — hard block); corporate DNS, VPN split-DNS, or a captive portal resolves every name to an internal IP, so every subresource trips the guard.
Common situations: Old job postings with dead trackers; VPN split-DNS wildcards; captive portals resolving all hosts to a login IP; restrictive corporate resolvers.
Related errors
- Access denied: Egress guard blocked private target IP ${ip}
- Access denied: Egress guard blocked private target IP ${ip}
- Access denied: Egress guard blocked private target IPv6 ${ip
- Access denied: Localhost or internal domain target detected.
- Blocked request to restricted destination (DNS): ${reques
AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20).
Data as JSON: /api/errors/95dc9e618c1db51a.
Report an issue: GitHub.