Remote & Authorized Images
Astro can optimize images that live on your own server, but it can also process images hosted on external URLs — like a CMS, an S3 bucket, or a marketing CDN. Because fetching and transforming arbitrary remote files is a potential abuse vector (server-side request forgery, runaway bandwidth, or proxying for someone else), Astro will only optimize remote images whose source domains you have explicitly authorized. This page explains how to allowlist domains and remote URL patterns so the <Image /> and <Picture /> components can resize, convert, and serve external images with the same care as local ones.
Why authorization is required
The <Image /> component optimizes local assets automatically because Astro fully controls them at build time. Remote images are different: Astro has to download the bytes before it can resize or re-encode them. If any URL were allowed, your build (or your server in SSR mode) could be coerced into fetching internal endpoints or acting as an open image proxy. To keep that safe, Astro processes a remote image only when its host matches an entry you added to image.domains or image.remotePatterns. Anything else is passed through untouched as a plain <img> tag with no optimization.
Authorizing whole domains
The simplest approach is to list trusted hostnames in the image.domains array of astro.config.mjs. Every image served from those exact hosts becomes eligible for optimization.
// astro.config.mjs
import { defineConfig } from "astro/config";
export default defineConfig({
image: {
domains: ["images.unsplash.com", "cdn.mysite.com"],
},
});
With that in place, you can point the src of an <Image /> at a full URL. Note that for remote images you must supply width and height (or one of them plus inferSize) because Astro cannot read the dimensions of a remote file the way it can for an imported local asset.
---
import { Image } from "astro:assets";
---
<Image
src="https://images.unsplash.com/photo-1682687220742"
alt="A scenic mountain view"
width={1200}
height={800}
/>
Authorizing with remote patterns
When you need finer control — for example, only HTTPS images, only a specific path prefix, or every subdomain of a provider — use image.remotePatterns. Each pattern is matched against the URL’s protocol, hostname, port, and pathname.
// astro.config.mjs
import { defineConfig } from "astro/config";
export default defineConfig({
image: {
remotePatterns: [
{
protocol: "https",
hostname: "**.amazonaws.com",
pathname: "/my-bucket/**",
},
],
},
});
The hostname field accepts wildcards: * matches a single subdomain segment, and ** matches any number of segments. The pathname field uses the same * / ** convention for URL path segments.
| Field | Purpose | Example |
|---|---|---|
protocol | Restrict to http or https | "https" |
hostname | Allowed host, with optional wildcards | "**.amazonaws.com" |
port | Restrict to a specific port (rarely needed) | "443" |
pathname | Allowed URL path, with optional wildcards | "/my-bucket/**" |
Prefer
remotePatternswithprotocol: "https"over a baredomainsentry when you can. It blocks plaintexthttpsources and lets you scope optimization to a specific bucket or folder instead of an entire host.
Inferring dimensions automatically
If you do not know a remote image’s size ahead of time, add the inferSize attribute. Astro fetches the image and reads its intrinsic dimensions for you. This costs an extra network request per image during the build, so reserve it for cases where you genuinely cannot hardcode the size.
---
import { Image } from "astro:assets";
---
<Image
src="https://cdn.mysite.com/hero.jpg"
alt="Product hero"
inferSize
/>
When a domain is not authorized
If you reference a remote image whose host is not in your allowlist, Astro does not error — it simply skips optimization and renders a standard <img>. The image still loads in the browser, but it is served at its original size and format with no responsive variants.
Output:
[astro:assets] The image at https://random-cdn.example.com/pic.jpg
is not from an authorized domain or pattern and was not optimized.
Rendering as a plain <img> tag.
To confirm whether optimization happened, inspect the rendered HTML: optimized remote images point at Astro’s /_image endpoint (or your image service), while skipped ones keep the original external URL in src.
Working with content collections
Remote URLs stored in a content collection (for example, an image string field from a headless CMS) are just strings, so the same authorization rules apply when you pass them to <Image />. Validate the field as a URL in your schema and render it as you would any other remote source.
// src/content.config.ts
import { defineCollection, z } from "astro:content";
const posts = defineCollection({
type: "content",
schema: z.object({
title: z.string(),
cover: z.string().url(),
}),
});
export const collections = { posts };
Best practices
- Authorize the narrowest set of hosts you actually use; never wildcard to
**across all hosts. - Favor
remotePatternswithprotocol: "https"so plaintext sources are rejected outright. - Provide explicit
widthandheightfor remote images to avoid layout shift and skip extra fetches. - Reserve
inferSizefor unknown dimensions, since it adds a network request per image at build time. - Treat CMS-supplied URLs as untrusted input: validate them with
z.string().url()and scopepathnameto the expected folder. - Check the rendered HTML during reviews to confirm remote images are hitting the optimizer and not silently falling back to plain
<img>.