hcengineering/platform · error · LinkPreviewError
BLOCKED_URL
BLOCKED_URL
Error message
Blocked URL: Access to internal addresses is not allowed.
What it means
As SSRF protection, validateUrl calls isBlockedHost on the hostname, rejecting private/internal addresses and IP literals (incl. IPv6-mapped IPv4) with LinkPreviewError code BLOCKED_URL.
Source
Thrown at pods/link-preview/src/parse.ts:224
function validateUrl (urlString: string): URL {
let url: URL
try {
url = new URL(urlString)
} catch {
throw new LinkPreviewError(`Invalid URL: ${urlString}`, 'INVALID_URL')
}
// Only allow HTTP(S) protocols
if (!['http:', 'https:'].includes(url.protocol)) {
throw new LinkPreviewError(
`Invalid protocol: ${url.protocol}. Only HTTP and HTTPS are allowed.`,
'INVALID_PROTOCOL'
)
}
// SSRF protection: block private/internal hosts and IP literals (incl. IPv6-mapped IPv4)
if (isBlockedHost(url.hostname)) {
throw new LinkPreviewError('Blocked URL: Access to internal addresses is not allowed.', 'BLOCKED_URL')
}
return url
}
function isRedirectStatus (status: number): boolean {
return status >= 300 && status < 400
}
async function fetchWithValidatedRedirects (
url: string,
options: RequestInit,
timeoutMs: number,
maxRedirects: number = 5
): Promise<{ response: Response, finalUrl: string }> {
let currentUrl = url
for (let i = 0; i <= maxRedirects; i++) {View on GitHub (pinned to 63e28dc964)
Solutions
- Use a publicly reachable URL for the resource
- Expose an internal mirror/proxy of the internal content and preview that instead
- If legitimately needed (self-hosted deployments), adjust isBlockedHost's allowlist consciously, keeping SSRF risks in mind
Example fix
// before
preview('http://localhost:8080/dashboard') // throws BLOCKED_URL
// after
preview('https://public.example.com/dashboard') Defensive patterns
Strategy: try-catch
Validate before calling
function isPubliclyRoutable(s: string): boolean {
try {
const u = new URL(s.trim())
const host = u.hostname
return !['localhost', '127.0.0.1', '::1', '0.0.0.0'].includes(host) &&
!/^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|169\.254\.)/.test(host) &&
!/(^|\.)internal$/i.test(host)
} catch { return false }
} Try / catch
try {
return await loadImageSize(client, url)
} catch (err) {
if (err instanceof LinkPreviewError && err.code === 'BLOCKED_URL') {
console.warn('internal address blocked (SSRF protection):', url); return null
}
throw err
} Prevention
- Never preview localhost/private-range/intranet URLs
- Filter private IPs and .internal hostnames in the UI before submit
- Be aware redirects can also target internal hosts — the library validates redirect targets too
- For intranet content, publish a public mirror instead
When it happens
Trigger: Requesting previews for 'http://localhost:3000', 'http://127.0.0.1', 'http://192.168.1.10', 'http://[::1]', or '.internal'/metadata hostnames via fetchOEmbedData / loadImageSize / parsedUrl.
Common situations: Internal dashboards or intranet links pasted by users; redirect-based SSRF probes to 169.254.169.254 cloud metadata; local dev URLs shared into a preview field; corporate hostnames resolving to private IPs.
Related errors
- INVALID_PROTOCOL
- INVALID_URL
- Cannot process provided link
- Failed to load server config
- getDisplayMedia not supported
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/e15df8620469138c.
Report an issue: GitHub.