payloadcms / payloadcms/payload

Why Do Images Load Slowly and Inconsistently? Possible Vercel Edge Cache Issues

Open
#16,299 2 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

invalid-reproduction plugin: storage-* stale status: needs-triage
Dominant language
TypeScript
Stars
44.8k
Forks
4.2k
Avg merge
2d 21h
Merged PRs (30d)
53

Description

Description

We're noticing that image load times are inconsistent and sometimes very slow. When requesting the same optimized image multiple times, we observe unusual cache behavior: the age header values sometimes jump backward, cache misses occur without obvious reason, and load times vary significantly (200ms → 2500ms). This happens repeatedly across multiple requests to identical URLs within seconds, and incognito windows seem to show different cache states than regular browsing. Is this expected behavior, or is something going wrong with how the edge cache is handling these requests?

Expected Behavior

  • Once an image is cached, all requests should consistently return x-vercel-cache: HIT
  • The age value should increase steadily with a predictable progression
  • Load times should remain consistently fast (~150-200ms for cached assets)

Actual Behavior

  • Cache hits and misses occur unpredictably for identical requests to the same image
  • age values jump backward (e.g., 10160 → 2340), reset to 0, then jump forward again (→ 11200)
  • Load times vary dramatically: cached hits load in ~150-200ms, misses take 2000-2500ms (10x slower)
  • Each cache miss forces image re-optimization, further impacting performance
  • Incognito windows receive different age values and cache states than regular browsing, even for the same URL
Example - Cache Age Jumping Backward and Misses
Request 1: age: 10160, load time: 200ms,  x-vercel-cache: HIT    (cached for 10160 seconds)
Request 2: age: 2340,  load time: 150ms,  x-vercel-cache: HIT    (age went backward?!)
Request 3: age: 0,     load time: 2500ms, x-vercel-cache: MISS    (cache miss, had to re-optimize)
Request 4: age: 45,    load time: 200ms,  x-vercel-cache: HIT    (age stays low)
Request 5: age: 11200, load time: 180ms,  x-vercel-cache: HIT    (age jumped forward again)

This pattern seems to repeat, but inconsistently. Sometimes we see multiple misses in a row, sometimes mostly hits. The age values don't follow a clear progression—they jump backward, reset to 0, then jump forward. The incognito window shows different age values for the same URL, which is puzzling—is this expected?

Link to the code that reproduces this issue

https://github.com/leandesigngmbh/payload-website-starter-img-caching

Reproduction Steps
  1. Navigate to any page with Next.js Image component optimized images (https://payload-website-starter-img-caching.vercel.app/test)
  2. Open browser DevTools → Network tab, filter by the image request
  3. Reload page multiple times over 30-60 seconds and note the age and cache headers
  4. The unusual behavior may not appear immediately—sometimes multiple reloads show consistent HIT, then suddenly ages jump or misses occur
  5. Try reloading again after 2-5 minutes—the pattern often becomes visible with this timing
  6. Open the same page in incognito window and compare the age values
  7. Switch back to regular window and reload again—cache state may have changed

Performance Impact

  • Images often take longer to load than expected
  • Load times seem to vary inconsistently between requests to the same asset
  • There may be a correlation with cache misses and age value inconsistencies—is there a connection?

Additional Context

This seems to be impacting performance:

  • Cache hits vary: Images don't seem to stay cached reliably
  • Load times differ: Same image loads in 150ms sometimes, 2500ms other times (17x difference)
  • Possible extra costs: Cache misses might force unnecessary image re-optimization
  • User experience: Pages feel slow and unpredictable—is the CDN cache working as expected?

Possible causes

  1. Is the edge cache being differently handled between PoPs/regions?
  2. Could cache eviction be happening more aggressively than expected?
  3. Are identical URLs being routed to different cache entries?
  4. Could there be an issue with how cache headers or time-based keys (like updatedAt timestamps) are being processed?
Which area(s) are affected?

plugin: storage-*

Environment Info
Binaries:
  Node: 22.14.0
  npm: 10.9.2
  Yarn: 1.22.22
  pnpm: 10.33.0
Relevant Packages:
  payload: 3.82.1
  next: 16.2.3
  @payloadcms/db-vercel-postgres: 3.82.1
  @payloadcms/drizzle: 3.82.1
  @payloadcms/graphql: 3.82.1
  @payloadcms/live-preview: 3.82.1
  @payloadcms/live-preview-react: 3.82.1
  @payloadcms/next/utilities: 3.82.1
  @payloadcms/plugin-cloud-storage: 3.82.1
  @payloadcms/plugin-form-builder: 3.82.1
  @payloadcms/plugin-nested-docs: 3.82.1
  @payloadcms/plugin-redirects: 3.82.1
  @payloadcms/plugin-search: 3.82.1
  @payloadcms/plugin-seo: 3.82.1
  @payloadcms/richtext-lexical: 3.82.1
  @payloadcms/storage-vercel-blob: 3.82.1
  @payloadcms/translations: 3.82.1
  @payloadcms/ui/shared: 3.82.1
  react: 19.2.4
  react-dom: 19.2.4
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 24.6.0: Mon Jan 19 21:58:37 PST 2026; root:xnu-11417.140.69.708.3~1/RELEASE_ARM64_T8103
  Available memory (MB): 16384
  Available CPU cores: 8

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked reproduction site and compare image Network responses, including age and x-vercel-cache, across repeated reloads and incognito browsing. Review the storage-* area and the listed Next.js, Vercel Blob, and Payload versions to determine whether the behavior is caused by Payload or the edge cache; done means the cause and an actionable project-side change are identified.

Written by the indexing model from the issue text.

Assessment

Tech stack
next.js, node.js, react, typescript
Domain
cloud, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.