Skip to content

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

Merged
Princesseuh merged 2 commits into
withastro:mainfrom
andreialba:fix/cloudflare-image-missing-format
Oct 1, 2026
Merged

Princesseuh merged 2 commits 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.

@changeset-bot

changeset-bot Bot commented Aug 13, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5fd0e82

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

This PR includes changesets to release 1 package
Name Type
@astrojs/cloudflare 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.

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
@andreialba
andreialba force-pushed the fix/cloudflare-image-missing-format branch from 6c1f435 to 36ec416 Compare September 30, 2026 09:15
@andreialba

Copy link
Copy Markdown
Contributor Author

Rebased onto main to resolve the conflict with #18059: the fix now lives in image-binding-transform-stream.ts next to the new quality handling. No other changes.

This is still live on astro.build: the GitHub avatars on the themes pages return 400 Unsupported format: null. @Princesseuh @matthewp could one of you take a look when you have a moment?

@Princesseuh Princesseuh self-assigned this Sep 30, 2026
@Princesseuh
Princesseuh merged commit 4f85749 into withastro:main Oct 1, 2026
24 checks passed
@astrobot-houston astrobot-houston mentioned this pull request Oct 1, 2026
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

3 participants