RORK LABJP
EVENT — Apple holds its Surprise and Shine event today, September 9, starting at 10:00 Pacific. That lands in the small hours of September 10 in JapanEXPECT — Expected are the iPhone 18 Pro and Pro Max, a foldable, the 2nm A20 Pro chip, and release dates for iOS 27 and its sibling updatesWAIT — As this is written the event has not happened yet. Rumor-stage writing and post-announcement writing look identical once they are mixed togetherMAX — Since Rork Max generates native Swift, Apple news is not somebody else's problem. Worth repeating that the standard product still writes React NativeSIMULATOR — Rork Max compiles on cloud Macs and lets you check the result in a streaming iOS simulator inside the browser, with no Xcode and no Mac hardwareSEASON — A new OS is when automated build pipelines wobble most. An article selling convenience owes its readers a word about that wobbleEVENT — Apple holds its Surprise and Shine event today, September 9, starting at 10:00 Pacific. That lands in the small hours of September 10 in JapanEXPECT — Expected are the iPhone 18 Pro and Pro Max, a foldable, the 2nm A20 Pro chip, and release dates for iOS 27 and its sibling updatesWAIT — As this is written the event has not happened yet. Rumor-stage writing and post-announcement writing look identical once they are mixed togetherMAX — Since Rork Max generates native Swift, Apple news is not somebody else's problem. Worth repeating that the standard product still writes React NativeSIMULATOR — Rork Max compiles on cloud Macs and lets you check the result in a streaming iOS simulator inside the browser, with no Xcode and no Mac hardwareSEASON — A new OS is when automated build pipelines wobble most. An article selling convenience owes its readers a word about that wobble
Articles/Dev Tools
Dev Tools/2026-08-10Advanced

Switching to Signed URLs Killed My Image Cache — Decoupling expo-image Keys from the URL

Signed URLs rewrite their query string on every expiry, so a URL-keyed cache never hits. Here is how to derive a stable key and drive expo-image's writeToCacheAsync and readFromCacheAsync yourself, with measured results.

Rork558expo-image6signed URLscache designReact Native236

Premium Article

The week after I moved image delivery behind signed URLs, one graph moved: bandwidth. Same catalog, same resolutions, same screens. The only thing that changed was the shape of the URL.

The culprit was the cache key. A signed URL rewrites X-Amz-Signature and X-Amz-Date every time the expiry rolls over. To the library, that is not yesterday's image. It is an image it has never seen.

As an indie developer running image-heavy wallpaper apps, I spent an embarrassing amount of time raising the disk cache ceiling before I understood this. Raising the ceiling does nothing. The cache was not full. It was never being hit.

Every new signature makes the same image a different image

A signed URL attaches proof to an object path: this key, this operation, valid until this moment. Because the proof is bound to an expiry, it has to be regenerated once that expiry passes.

So the same wallpaper, w042.webp, shows up like this across sessions:

Session 1: https://cdn.example.net/wallpapers/w042.webp?X-Amz-Expires=3600&X-Amz-Date=20260810T000000Z&X-Amz-Signature=6f1c...
Session 2: https://cdn.example.net/wallpapers/w042.webp?X-Amz-Expires=3600&X-Amz-Date=20260811T000000Z&X-Amz-Signature=a93e...

The path is identical. The string is not. That gap is the whole problem.

ComponentBehavior across sessionsSafe to key on?
HostUsually stable, but changes when you move CDNsNot on its own
Path (/wallpapers/w042.webp)Stable. Expresses object identityYes
X-Amz-Date / X-Amz-SignatureChanges on every expiry, by designNever
Transform params (?w=1080&fm=webp)Changes when the request changesYes. Different variant, different key

The key should encode which object, in what shape — never who authorized it, and when.

Where the default cache key actually comes from

expo-image derives its on-disk location from the source.uri string. cachePolicy selects which layers store the bytes, memory or disk. It does not change how the key is built. Conflating the two is how you end up with cachePolicy="memory-disk" set correctly and nothing being reused.

// This picks a storage layer. It has zero influence on key stability.
<Image
  source={{ uri: signedUrl }}   // the string changes every session
  cachePolicy="memory-disk"     // bytes get stored, then never looked up again
  style={styles.thumb}
/>

The bytes do land on disk. They simply become entries nobody will ever request again, quietly consuming your disk budget. The bloat side of that story is covered in Your Rork App's "Documents & Data" Keeps Growing, but when signed URLs are involved, tuning the ceiling will not help. The problem is the key, not the capacity.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
You can pinpoint why your bandwidth climbed after moving to signed URLs, and decide exactly which layer to fix
You get working code that manages expo-image cache keys yourself through writeToCacheAsync and readFromCacheAsync
You can estimate how much of your hit rate a key redesign would actually recover, using your own access distribution
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

Related Articles

Dev Tools2026-06-25
When an Image-Heavy Rork App Quietly Bloats Its Cache and Dies on Memory — Field Notes on Measuring and Capping
In a Rork app where images are the product, expo-image's disk cache and resident memory creep up over a session and surface as OOM crashes. Here's how I measured the bloat, where I set caps, and what I trimmed on the delivery side — with working code, in the order that actually helped.
Dev Tools2026-06-19
When Rork-Built Lists Stutter: Designing Image Caching and Prefetch
A FlatList from Rork starts stuttering once the images pile up. Here is how I restore smoothness with expo-image caching, recyclingKey, prefetch, and a move to FlashList, with the device numbers I measured.
Dev Tools2026-06-12
Your Rork App's 'Documents & Data' Keeps Growing — Taming expo-image's Disk Cache
My wallpaper app's binary was 40 MB, yet 'Documents & Data' had ballooned to 2.4 GB. Here is how I diagnosed expo-image's unbounded disk cache and fixed it with cachePolicy tuning, thumbnail URLs, and generational cache clearing.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links