payloadcms/payload · error · APIError
The provided URL is not allowed.
Error message
The provided URL is not allowed.
What it means
APIError (HTTP 400, 'The provided URL is not allowed.') thrown when an allowList is configured for pasteURL and the initial requested URL does not match any allowList entry. isURLAllowed compares protocol (with appended ':'), hostname/port/search for equality, and pathname via glob (*/**) translation; an invalid URL denies by default.
Source
Thrown at packages/payload/src/uploads/endpoints/getFileFromURL.ts:66
const { searchParams } = new URL(req.url)
const src = searchParams.get('src')
if (!src || typeof src !== 'string') {
throw new APIError('A valid URL string is required.', 400)
}
const hasAllowList =
typeof config.upload.pasteURL === 'object' && Array.isArray(config.upload.pasteURL.allowList)
let fileURL: string
try {
fileURL = new URL(src).href
} catch {
throw new APIError('A valid URL string is required.', 400)
}
if (hasAllowList && !isURLAllowed(fileURL, config.upload.pasteURL.allowList)) {
throw new APIError('The provided URL is not allowed.', 400)
}
let redirectCount = 0
const maxRedirects = 3
let response!: Response
while (true) {
if (hasAllowList && isURLAllowed(fileURL, config.upload.pasteURL.allowList)) {
// Allow-listed URLs bypass SSRF filtering (e.g. internal/localhost CDNs)
response = await fetch(fileURL, {
headers: { 'Accept-Encoding': 'identity' },
redirect: 'manual',
signal: AbortSignal.timeout(30_000),
})
} else {
response = await safeFetch(fileURL, {
headers: {
'Accept-Encoding': 'identity',View on GitHub (pinned to 00c58b35c0)
Solutions
- Add the source host (and protocol, pathname pattern) to upload.pasteURL.allowList.
- Widen the pathname glob — use '**' for multi-segment matching (e.g. '/media/**').
- Confirm the protocol field matches (pasteURL compares 'https' → 'https:').
- On the client, restrict the picker to allow-listed hosts so users can't submit disallowed URLs.
Example fix
// before — too narrow
pasteURL: { allowList: [{ hostname: 'cdn.example.com', pathname: '/img/*' }] }
// after — cover the host and broad path
pasteURL: {
allowList: [
{ hostname: 'cdn.example.com', protocol: 'https', pathname: '/**' },
{ hostname: 'images.example.com', protocol: 'https', pathname: '/**' },
],
} Defensive patterns
Strategy: validation
Validate before calling
import { isURLAllowed } from 'payload/utilities' // if exported; else replicate
const ALLOW_LIST = [{ hostname: 'cdn.example.com', protocol: 'https', pathname: '/**' }]
function urlAllowed(u: string): boolean {
return isURLAllowed ? isURLAllowed(u, ALLOW_LIST) : (() => { try { const p = new URL(u); return ALLOW_LIST.some(a => p.hostname === a.hostname && p.protocol === a.protocol + ':') } catch { return false } })()
}
if (!urlAllowed(src)) throw new Error('That host is not in the paste-URL allow list') Type guard
const matchesAllowList = (u: string, list: Array<{ hostname: string; protocol?: string }>): boolean => {
try {
const p = new URL(u)
return list.some((a) => p.hostname === a.hostname && (!a.protocol || p.protocol === `${a.protocol}:`))
} catch { return false }
} Try / catch
try {
await fetch(`/api/media/paste-url?src=${encodeURIComponent(src)}`, { method: 'POST' })
} catch (e) {
if (/not allowed/.test(e.message)) alert('Use a URL from an allowed host')
} Prevention
- Mirror the server's pasteURL.allowList in client-side validation.
- Restrict the URL picker to allow-listed hosts.
- Document allowList semantics (protocol compared with trailing ':', pathname globbing).
- Test allowList entries with realistic URLs before shipping.
When it happens
Trigger: pasteURL.allowList is set (array of {hostname, protocol, pathname?, port?} criteria) and the user-submitted src URL's hostname, protocol, port, or pathname fails to match any entry. Also fires if the URL fails to parse (isURLAllowed returns false on parse error).
Common situations: Allow-listing cdn.example.com but the user pastes from a different host; protocol mismatch (allowList https, user submits http); pathname glob too narrow (e.g. '/images/*' won't match '/videos/x.mp4'); forgetting to allow the protocol key; typo in the allowList hostname.
Related errors
- File type ${mimeTypeFromExtension} (from extension ${typeFro
- A valid URL string is required.
- ${value} is not allowed as a JSON query value
- Invalid path segment. Only alphanumeric characters and under
- Field ${field.label} has invalid relationship '${relationshi
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/9ae2e5d5be3a782c.
Report an issue: GitHub.