{"record":{"id":"714b585c804201f4","repo":"withastro/astro","slug":"remoteimagenotallowed","errorCode":"RemoteImageNotAllowed","errorMessage":"Remote image ${imageURL} is not allowed by your image configuration.","messagePattern":"Remote image (.+?) is not allowed by your image configuration\\.","errorType":"exception","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/assets/internal.ts","lineNumber":92,"sourceCode":"\n\tconst service = await getConfiguredImageService();\n\n\t// If the user inlined an import, something fairly common especially in MDX, or passed a function that returns an Image, await it for them\n\tconst resolvedOptions: ImageTransform = {\n\t\t...options,\n\t\tsrc: await resolveSrc(options.src),\n\t};\n\n\tlet originalWidth: number | undefined;\n\tlet originalHeight: number | undefined;\n\n\t// Infer size for remote images if inferSize is true\n\tif (resolvedOptions.inferSize) {\n\t\tdelete resolvedOptions.inferSize; // Delete so it doesn't end up in the attributes\n\n\t\tif (isRemoteImage(resolvedOptions.src) && isRemotePath(resolvedOptions.src)) {\n\t\t\tif (!isRemoteAllowed(resolvedOptions.src, imageConfig)) {\n\t\t\t\tthrow new AstroError({\n\t\t\t\t\t...AstroErrorData.RemoteImageNotAllowed,\n\t\t\t\t\tmessage: AstroErrorData.RemoteImageNotAllowed.message(resolvedOptions.src),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst getRemoteSize = (url: string) =>\n\t\t\t\tservice.getRemoteSize?.(url, imageConfig, logger) ?? inferRemoteSize(url, imageConfig);\n\t\t\tconst result = await getRemoteSize(resolvedOptions.src); // Directly probe the image URL\n\t\t\tresolvedOptions.width ??= result.width;\n\t\t\tresolvedOptions.height ??= result.height;\n\t\t\t// We've already paid for the fetch; reuse it to pin down the output format so the URL\n\t\t\t// (and any baked filename) doesn't have to defer or refetch.\n\t\t\tif (result.format) {\n\t\t\t\tresolvedOptions.format ??= resolveDefaultOutputFormat(result.format);\n\t\t\t}\n\t\t\toriginalWidth = result.width;\n\t\t\toriginalHeight = result.height;\n\t\t}","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/assets/internal.ts#L74-L110","documentation":"When `inferSize: true` is set, `getImage()` must probe the remote image to learn its dimensions (packages/astro/src/assets/internal.ts:88-94). Before fetching, it checks the URL against the `images.domains` / `images.remotePatterns` allowlist; a remote `src` that is not allowed throws `RemoteImageNotAllowed` instead of making the request.","triggerScenarios":"`getImage({ src: 'https://cdn.example.com/hero.png', inferSize: true })` while `cdn.example.com` is absent from `image.domains` and matches no `image.remotePatterns` entry.","commonSituations":"Enabling `inferSize` for existing remote images without updating the image security config; new CDNs or subdomains after a migration; typos in the domain config (`www.` vs bare domain).","solutions":["Add the host to `image.domains` in `astro.config.mjs` (or a matching `image.remotePatterns` entry) and restart dev.","Alternatively supply explicit `width`/`height` and drop `inferSize`, which keeps the URL opaque without a probe.","Check the exact hostname in the error message against your config — scheme, subdomain, and port must all line up with `remotePatterns`."],"exampleFix":"// astro.config.mjs — before\nimage: { domains: [] }\n\n// astro.config.mjs — after\nimage: { domains: ['cdn.example.com'] }","handlingStrategy":"validation","validationCode":"// mirror Astro's allowlist check before calling getImage with inferSize\nfunction isRemoteAllowed(src: string, domains: string[], remotePatterns: { hostname?: string }[]): boolean {\n  const host = new URL(src).hostname;\n  return domains.includes(host) || remotePatterns.some((p) => p.hostname === host);\n}\n\nif (opts.inferSize && !isRemoteAllowed(opts.src, image.domains, image.remotePatterns ?? [])) {\n  throw new Error(`Domain not allowlisted for inferSize: ${opts.src}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const img = await getImage({ src: url, inferSize: true });\n} catch (err) {\n  if (err.name === 'RemoteImageNotAllowed') {\n    // fall back to explicitly sized transform, or prompt the user to allowlist the domain\n  } else throw err;\n}","preventionTips":["Keep image.domains in config reviewed next to any new CDN onboarding.","Prefer remotePatterns with explicit hostname patterns over open wildcards.","Add a test page exercising one inferSize remote image per allowed domain."],"tags":["images","remote-images","security-config","getimage"],"backgroundTag":"domain-not-allowlisted","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}