cloudflare / cloudflare/workers-sdk
🐛 BUG: Local Images binding ignores EXIF orientation - photos render sideways in local dev
- Dominant language
- TypeScript
- Stars
- 4.5k
- Forks
- 1.5k
- Avg merge
- 3d 8h
- Merged PRs (30d)
- 186
Description
### Which Cloudflare product(s) does this pertain to?
Miniflare
### What versions are you using?
miniflare 4.20260730.0 (also present on `main` at time of filing)
### What operating system and version are you using?
Windows 11 (reproduces on all platforms)
### Describe the Bug
**Observed behavior**
The local (low-fidelity) Images binding polyfill does not apply EXIF orientation before transforming. Photos taken on phones (commonly stored as landscape pixels + EXIF `Orientation: 6`) come back sideways from `env.IMAGES.input(...).transform(...)` in local dev.
In production, the Images binding auto-applies EXIF orientation before transforms, so the same code produces upright images when deployed. This is a local/production fidelity gap that makes phone photos appear rotated 90° only in local dev.
Related: #14795 fixed the `fit`/`gravity`/`background` handling in the same code path (thanks!). Before that fix this bug presented as *sideways + black letterbox bars*; after it, images are still sideways.
**Where it happens**
In `packages/miniflare/src/plugins/images/fetcher.ts`, both local fetchers construct sharp without EXIF auto-orientation:
- `imagesLocalFetcher`: `const transformer = sharp(await body.arrayBuffer(), {});`
- `cfImageLocalFetcher`: `const transformer = sharp(source);`
`runTransform` only applies an *explicit* `transform.rotate` value; a parameterless EXIF-based rotation is never applied.
**Expected behavior**
The local binding should match production and orient images per their EXIF flag before applying user transforms. Since miniflare already depends on sharp 0.35.x, this is a one-line-per-fetcher fix:
```ts
sharp(await body.arrayBuffer(), { autoOrient: true })
```
(`autoOrient` bakes the EXIF rotation into pixels at decode and clears the orientation flag; an explicit user `rotate` then composes on top with production-matching semantics.)
**Downstream impact**
Any framework whose dev server routes image optimization through the local binding shows user photos sideways in dev but correct in production. Concretely: Astro's `@astrojs/cloudflare` adapter serves `/_image` through `env.IMAGES` in `astro dev`, so any EXIF-rotated photo renders sideways locally — this is how I found the bug.
### Please provide a link to a minimal reproduction
See steps below — the repro is a few lines in any Worker with an `images` binding.
### Steps to reproduce
1. Create a Worker with an `images` binding and this handler:
```ts
export default {
async fetch(request, env) {
const src = await fetch("https://github.com/recurser/exif-orientation-examples/raw/master/Portrait_6.jpg");
const result = await env.IMAGES.input(src.body)
.transform({ width: 300 })
.output({ format: "image/jpeg" });
return result.response();
},
};
```
2. `wrangler dev` (local): the image is sideways.
3. `wrangler dev --remote` or deploy: the image is upright.
### Please provide any relevant error logs
No errors are logged; the transform succeeds but produces incorrectly oriented output.
Contributor guide
Research direction
Start in packages/miniflare/src/plugins/images/fetcher.ts and inspect imagesLocalFetcher, cfImageLocalFetcher, and runTransform. Reproduce the issue with the provided Portrait_6.jpg Worker example, then verify that local output is upright for EXIF-oriented images and that explicit rotate transforms still compose correctly.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100