Skip to content

fix(cloudflare): resolve image format from the source when f is absent - #17677

Open
andreialba wants to merge 1 commit into
withastro:mainfrom
andreialba:fix/cloudflare-image-missing-format
Open

fix(cloudflare): resolve image format from the source when f is absent#17677
andreialba wants to merge 1 commit into
withastro:mainfrom
andreialba:fix/cloudflare-image-missing-format

Conversation

@andreialba

Copy link
Copy Markdown
Contributor

Changes

Remote images whose URL has no file extension fail with 400 Unsupported format: null under the cloudflare-binding image service (the adapter default).

Astro's baseService.validateOptions only sets options.format when it can infer a source format from the URL, and since #16665 deliberately leaves it undefined otherwise so the image service resolves the format from the source bytes instead — which is what the Sharp service does at sharp.ts#L155-L156. With format undefined, no f parameter is emitted onto the /_image URL, and transformStream treated a missing f as a client error.

Extensionless remote images are the common case here — GitHub avatars like https://avatars.githubusercontent.com/u/192622539?s=200&v=4 have no extension to infer from, which is why every theme author avatar on astro.build is currently broken:

$ curl -sD - -o /dev/null 'https://astro.build/_image?href=https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F192622539%3Fs%3D200%26v%3D4&w=300&h=300'
HTTP/1.1 400 Bad Request
Unsupported format: null

# ...the same URL with an explicit format works
$ curl -so /dev/null -w '%{http_code} %{content_type}\n' '...&w=300&h=300&f=webp'
200 image/webp
  • transformStream now takes the source's media type and uses it when f is absent: SVG sources pass through unchanged, everything else is encoded as WebP. This mirrors core's resolveDefaultOutputFormat, which webp-encodes every non-SVG source.
  • Requests that explicitly ask for a format the IMAGES binding cannot produce (e.g. f=tiff) still return 400.
  • This also fixes SVG images, which core requests as f=svg and which previously failed the same way — the IMAGES binding cannot emit SVG, so those bytes are served as-is.

Fixes withastro/astro.build#2610

Testing

Added four cases to test/binding-image-service.test.ts (build + preview, exercising the IMAGES binding for real) covering remote/local sources with no f, and SVG passthrough. A local HTTP server serves images from extensionless paths to reproduce the GitHub avatar shape. Also added the no-f case to test/dev-image-endpoint.test.ts.

All four new binding tests fail on main with 400 and pass with this change; the seven pre-existing tests in that file are unaffected.

Note

Two things worth flagging for maintainers, both pre-existing and left untouched here:

  • caches.default is persisted under test/fixtures/binding-image-service/.wrangler/state/v3/cache, so a previously-cached 200 can mask a genuine failure for any stable /_image URL. I had to clear that directory to see the new tests fail on main.
  • createPreviewServer returns the requested port rather than the bound one (preview.ts#L97), so the whole suite 404s if port 4321 is already taken.

Docs

No docs changes needed. This restores the documented behavior of image.remotePatterns/image.domains for remote images; there's no API or configuration surface change.

Astro's base image service only sets the `f` (format) parameter on `/_image`
URLs when it can infer a format from the source, and deliberately leaves it
off otherwise so the image service resolves the format from the source itself.
Extensionless remote images take that path, GitHub avatars such as
`https://avatars.githubusercontent.com/u/1234` being the common case, and the
Cloudflare image transform endpoint rejected them with
`400 Unsupported format: null`.

Fall back to the source's media type when `f` is absent: SVG sources are
passed through unchanged and everything else is encoded as WebP, matching
core's `resolveDefaultOutputFormat`. Requests that explicitly ask for a format
the IMAGES binding cannot produce still return a 400.

This also fixes SVG images, which core requests as `f=svg` and which
previously failed the same way.

Fixes withastro/astro.build#2610
@changeset-bot

changeset-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6c1f435

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 43 packages
Name Type
@astrojs/cloudflare Patch
@test/astro-cloudflare-node-prerender-mdx Patch
@test/astro-cloudflare Patch
@test/astro-cloudflare-allowed-hosts Patch
@test/astro-cloudflare-astro-dev-platform Patch
@test/astro-cloudflare-astro-env Patch
@test/astro-cloudflare-binding-image-cache Patch
@test/astro-cloudflare-binding-image-service Patch
@test/astro-cloudflare-cache-provider-wait-until Patch
@test/astro-cloudflare-cache-provider Patch
@test/astro-cloudflare-client-address Patch
@test/astro-cloudflare-compile-custom-image-service Patch
@test/astro-cloudflare-compile-image-service Patch
@test/astro-cloudflare-custom-entryfile Patch
@test/astro-cloudflare-custom-image-service Patch
@test/astro-cloudflare-dev-image-endpoint Patch
@test/astro-cloudflare-external-image-service Patch
@test/astro-cloudflare-external-redirects Patch
@test/cloudflare-incremental-images Patch
@test/astro-cloudflare-internal-redirects Patch
@test/astro-cloudflare-no-output Patch
@test/astro-cloudflare-prerender-node-env Patch
@test/astro-cloudflare-prerender-queue-consumers Patch
@test/astro-cloudflare-prerender-styles Patch
@test/astro-cloudflare-prerenderer-errors Patch
@test/astro-cloudflare-prerenderer-render-error Patch
@test/routing-priority-cloudflare Patch
@test/cf-server-entry Patch
@test/astro-cloudflare-server-island-prerender-framework Patch
@test/astro-cloudflare-sessions Patch
@test/astro-cloudflare-sql-import Patch
@test/cf-ssr-deps Patch
@test/astro-cloudflare-static Patch
@test/astro-cloudflare-svelte-rune-deps Patch
@test/astro-cloudflare-top-level-return Patch
@test/cf-user-optimize-deps Patch
@test/astro-cloudflare-vite-plugin Patch
@test/astro-cloudflare-with-base Patch
@test/astro-cloudflare-with-react Patch
@test/astro-cloudflare-with-solid-js Patch
@test/astro-cloudflare-with-svelte Patch
@test/astro-cloudflare-with-vue Patch
@test/astro-cloudflare-wrangler-preview-platform Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added the pkg: integration Related to any renderer integration (scope) label Aug 13, 2026
@matthewp matthewp added the pr: astro-review Triggers a bot to do an automated review label Aug 13, 2026
@astro-review astro-review Bot removed the pr: astro-review Triggers a bot to do an automated review label Aug 13, 2026

@astro-review astro-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

No findings.

Review Status

Scope: PR #17677 @astrojs/cloudflare image binding fix (base 09f0dc7 through head 6c1f4352).
Changeset: present and covers @astrojs/cloudflare.
Validation: Static review only; no project code, tests, builds, or checks were run.
Fetch: not needed for the supplied PR scope.

This review was made by an LLM. The analysis may be wrong, and reports might be incorrect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg: integration Related to any renderer integration (scope)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Avatars are broken on theme author pages

2 participants