Skip to content
Astro as images 4 min read

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.

FieldPurposeExample
protocolRestrict to http or https"https"
hostnameAllowed host, with optional wildcards"**.amazonaws.com"
portRestrict to a specific port (rarely needed)"443"
pathnameAllowed URL path, with optional wildcards"/my-bucket/**"

Prefer remotePatterns with protocol: "https" over a bare domains entry when you can. It blocks plaintext http sources 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 remotePatterns with protocol: "https" so plaintext sources are rejected outright.
  • Provide explicit width and height for remote images to avoid layout shift and skip extra fetches.
  • Reserve inferSize for 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 scope pathname to 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>.
Last updated June 14, 2026
Was this helpful?