Image Service Reference
Purpose and Scope
Astro’s image service contract is the adapter-facing layer behind astro:assets, <Image />, <Picture />, and image references that need provider-specific optimization. The official image service model distinguishes local services, which can transform image bytes directly, from external services, which build a URL that points at a platform or remote optimizer. The files covered here show how Astro adapters implement that contract for Vercel, Cloudflare, and Netlify rather than documenting every possible service. They are useful when you need to understand which options are preserved, which URLs are generated, and how development behavior differs from production deployment behavior.
An image service definition is an exported service object with hooks such as validateOptions, getURL, getHTMLAttributes, getSrcSet, parseURL, and, for local services, transform. The adapter implementations usually start from baseService or a shared adapter base, then override the pieces required by the host platform. This matters because image rendering is partly an Astro authoring concern and partly a deployment concern: the same component options may become a Vercel optimization URL, a Cloudflare /cdn-cgi/image URL, a Netlify image CDN URL, or a local development endpoint.
Sources: packages/integrations/vercel/src/image/build-service.ts, packages/integrations/cloudflare/src/entrypoints/image-service-external.ts, packages/integrations/cloudflare/src/entrypoints/image-service-workerd.ts, packages/integrations/netlify/src/image-service.ts, packages/integrations/vercel/src/image/dev-service.ts, packages/integrations/vercel/src/image/shared-dev-service.ts
Relevant Source Files
packages/integrations/vercel/src/image/build-service.tsimplements Vercel’s productionExternalImageService, including option validation, HTML attribute normalization, URL generation for/_vercel/image, SVG passthrough, and Vercel-specificsrcsetwidth handling.packages/integrations/cloudflare/src/entrypoints/image-service-external.tsimplements Cloudflare’s external URL-building image service using/cdn-cgi/imagewith width, height, quality, fit, format, and remote-source allow-list behavior.packages/integrations/cloudflare/src/entrypoints/image-service-workerd.tsprovides a workerd-compatible local service stub that avoids importing Sharp while preserving the local service shape needed in prerender and binding-based environments.packages/integrations/netlify/src/image-service.tsimplements Netlify’s external image service, including Netlify query parameter mapping, supported output formats, fit-mode mapping, quality-name conversion, and validation errors.packages/integrations/vercel/src/image/dev-service.tsimplements Vercel’s local development image service by delegating attribute and transform behavior to Astro’s Sharp service while sharing Vercel validation and endpoint behavior.packages/integrations/vercel/src/image/shared-dev-service.tscontains Vercel development-service primitives: shared validation, the/_imageURL format, andparseURLreconstruction for development image transforms.
Image Service Contract
The shared contract is visible in the type imports: Vercel production, Cloudflare external, and Netlify all implement ExternalImageService, while Vercel development and Cloudflare workerd implement LocalImageService. External services are responsible for returning a final image src URL from getURL; the remote platform then downloads, transforms, and serves the asset. Local services can expose a URL for a framework endpoint and implement transform so Astro can produce the optimized bytes itself. Both service types can inherit defaults from baseService, then selectively override behavior that is platform-specific.
Vercel’s production build service demonstrates the richest external implementation in this set. It spreads baseService, validates options through sharedValidateOptions in production mode, computes HTML attributes with sensible defaults, and routes non-SVG images through /_vercel/image. It also treats imported image metadata specially. If an imported image has only width or only height, the service derives the missing dimension from the original aspect ratio; if neither dimension is supplied, it uses the imported image’s original dimensions. That keeps rendered HTML attributes consistent with the image metadata available to Astro.
Cloudflare’s external image service is narrower and URL-focused. Its getURL assembles Cloudflare resizing parameters, always beginning with onerror=redirect, then conditionally appending width, height, quality, fit, and format. It accepts imported images directly, and it accepts remote string sources only when isRemoteAllowed approves them against the current image configuration. If a string source is neither imported nor allowed as a remote image, the service returns the original URL rather than forcing it through the Cloudflare image endpoint. This behavior preserves user-authored URLs when the optimizer should not handle them.
Sources: packages/integrations/vercel/src/image/build-service.ts, packages/integrations/cloudflare/src/entrypoints/image-service-external.ts
Platform Implementations
Vercel has separate production and development services because the production platform endpoint and the local development transform path are not the same. In production, getURL emits /_vercel/image?url=... plus optional w and q query parameters. Imported SVG files bypass the optimizer and return their original source path. For non-SVG imported images, the implementation removes a leading slash before passing the file source to the Vercel image endpoint. The service’s custom getSrcSet is adapted from the base service, but it constrains generated widths to values valid for the configured Vercel image sizes.
The Vercel development service is local, so it implements LocalImageService and delegates actual byte transformation to astro/assets/services/sharp. Its shared base, baseDevService, validates with sharedValidateOptions in development mode, emits /_image?href=... URLs, and exposes parseURL so the development endpoint can reconstruct a transform object from href, w, and q search parameters. The development transform forces the output format to webp for most images and keeps SVG as svg. The source comment explicitly treats this as an approximation of Vercel’s production optimizer rather than an exact simulation.
Netlify’s service is an external URL builder that maps Astro options onto /.netlify/images query parameters. It sets url to either the imported image path without a leading slash or the original string source. It maps format to fm, width to w, height to h, quality to q, and supported fit values to Netlify’s accepted fit values. Netlify only supports contain, cover, and fill, so Astro values such as inside, outside, and scale-down are converted to the nearest supported behavior. Imported SVG files bypass optimization and return their original source path.
Cloudflare also includes a workerd-compatible local service stub. The comments explain that this stub exists to handle getURL, getHTMLAttributes, and static image behavior in the workerd prerender environment without importing Sharp. Actual image transforms are handled elsewhere: compile-time generation can use Sharp on the Node side, and Cloudflare binding mode can use an image transform endpoint backed by the IMAGES binding. The stub’s transform method returns the original input buffer and the requested format, allowing generic endpoint code to call a local-service-shaped object in development without performing a real transform in workerd.
Sources: packages/integrations/vercel/src/image/build-service.ts, packages/integrations/vercel/src/image/dev-service.ts, packages/integrations/vercel/src/image/shared-dev-service.ts, packages/integrations/netlify/src/image-service.ts, packages/integrations/cloudflare/src/entrypoints/image-service-workerd.ts
Compact API Reference
| Service file | Service type | Key hooks | Generated URL shape | Notable behavior |
|---|---|---|---|---|
packages/integrations/vercel/src/image/build-service.ts | ExternalImageService | validateOptions, getHTMLAttributes, getURL, getSrcSet | /_vercel/image?url=...&w=...&q=... | SVG passthrough; imported-image dimension inference; Vercel-configured width handling. |
packages/integrations/vercel/src/image/shared-dev-service.ts | Omit<LocalImageService, 'transform'> | validateOptions, getURL, parseURL | /_image?href=...&w=...&q=... | Shared Vercel development URL and transform parsing contract. |
packages/integrations/vercel/src/image/dev-service.ts | LocalImageService | getHTMLAttributes, transform | Uses shared dev URL behavior | Delegates to Sharp; forces webp except SVG. |
packages/integrations/cloudflare/src/entrypoints/image-service-external.ts | ExternalImageService | getURL | ${BASE_URL}/cdn-cgi/image/onerror=redirect,.../source | Uses imported images or allowed remote sources; otherwise returns original URL. |
packages/integrations/cloudflare/src/entrypoints/image-service-workerd.ts | LocalImageService | transform | Inherits URL behavior from baseService | Workerd-safe passthrough stub that avoids Sharp. |
packages/integrations/netlify/src/image-service.ts | ExternalImageService | getURL, getHTMLAttributes, getSrcSet, validateOptions | /.netlify/images?url=...&fm=...&w=...&h=...&q=...&fit=... | Format validation, quality conversion, fit mapping, SVG passthrough. |
The most important option names to track across services are src, width, height, format, quality, and fit. Vercel production serializes width and quality as w and q, while Vercel development serializes source as href. Netlify uses fm, w, h, q, and fit. Cloudflare serializes transformations as comma-separated path parameters such as width=, height=, quality=, fit=, and format=. These differences are hidden from most component authors, but adapter authors and debugging workflows need to know where an Astro transform option lands in the final URL.
Validation is also platform-specific. Netlify calls verifyOptions first, then rejects formats outside avif, jpg, png, and webp. It converts named qualities through low, mid, high, and max, then throws an AstroError if numeric quality falls outside the inclusive range from 1 to 100. Vercel delegates validation to sharedValidateOptions and passes either production or development, which allows one shared policy to adapt to runtime mode. Cloudflare external URL generation performs source eligibility checking through isRemoteAllowed, which is a guard around whether a remote string URL may be optimized.
Sources: packages/integrations/netlify/src/image-service.ts, packages/integrations/vercel/src/image/build-service.ts, packages/integrations/vercel/src/image/shared-dev-service.ts, packages/integrations/cloudflare/src/entrypoints/image-service-external.ts
Execution Flow and Debugging Notes
For an Astro component author, the flow starts with image options passed to astro:assets components or markdown image handling. Astro validates and normalizes those options through the configured service, asks the service for HTML attributes, then asks for a URL or srcset entries. If the configured service is external, that URL points to the deployment platform’s optimizer. If the configured service is local, the URL normally points to an Astro-controlled endpoint that parses the request and calls transform. The service files in this page sit exactly at that boundary between authored component options and deployment-specific image delivery.
When debugging an unexpected image URL, first identify the adapter and mode. A Vercel production build should produce /_vercel/image URLs, while Vercel development should produce /_image URLs that can be parsed by parseURL. A Netlify deployment should use /.netlify/images and may omit unsupported fit mappings. A Cloudflare external URL should include /cdn-cgi/image only for imported images or allowed remote images; otherwise the original source string is intentionally preserved. SVG passthrough on Vercel and Netlify is not a failure to optimize accidentally; it is explicit service behavior for imported SVG assets.
If you are implementing or reviewing another image service, use these adapters as concrete patterns rather than copying one blindly. Start from baseService when default HTML attributes and srcset behavior are sufficient. Override getURL when a hosting provider expects a specific endpoint or query format. Add validateOptions when the provider has stricter accepted formats, width sets, quality ranges, or remote-source rules than Astro’s generic options. Use a local service and transform only when the runtime can safely process image bytes; the Cloudflare workerd stub shows why some runtimes need a service-shaped passthrough instead of importing a native optimizer.
Sources: packages/integrations/vercel/src/image/build-service.ts, packages/integrations/vercel/src/image/dev-service.ts, packages/integrations/vercel/src/image/shared-dev-service.ts, packages/integrations/cloudflare/src/entrypoints/image-service-external.ts, packages/integrations/cloudflare/src/entrypoints/image-service-workerd.ts, packages/integrations/netlify/src/image-service.ts
Next Steps
Read this page together with the broader images and assets documentation when you need the author-facing <Image /> and <Picture /> behavior, and with deployment adapter pages when you need to understand how an optimizer is selected for a host. For implementation work, trace the exact service object exported by the adapter, then compare its getURL, validation, and transform hooks against the platform’s documented image API. For application debugging, inspect the rendered src and srcset first; their endpoint shape usually tells you which service path was active and whether the issue belongs to Astro option normalization, adapter validation, or the remote optimizer.