◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/App Dev
◇ App Dev/2026-10-16Intermediate

301 or Internal Rewrite for Wallpaper Resolution Routing? Inside One Host, I Chose the Route That Doesn't Change the URL

When one host picks an image resolution for you, should it answer with a 301 or rewrite internally? I ran both on nginx and compared round trips, rule-order traps, and a small verification script.

image deliverynginx301 redirectinternal rewritecaching

✦ Premium Article

I was tidying image delivery for a wallpaper app. The app asks for an image like sunset.jpg?w=640, adding the width it wants. On the server there are folders split by width. The glue between the two is a routing rule, and I stalled on one question: write it as a 301, or as an internal rewrite?

Both serve the image. That is exactly why it is easy to pick one without saying why. Here is the conclusion first: when the routing stays inside one host and the caller has no need to know about it, keep the URL unchanged. What follows is the same decision reproduced on a local nginx, with both versions actually run.

What needs routing

Here is the setup on one page.

URL requestedFile to return
/img/sunset.jpg?w=640/img/w640/sunset.jpg
/img/sunset.jpg?w=1280/img/w1280/sunset.jpg
/img/sunset.jpg (no value, or an unexpected one)/img/sunset.jpg (the original)

For the test I placed dummy files of 2,000 bytes (640 wide), 8,000 bytes (1280 wide), and 30,000 bytes (the original). Different sizes tell you at a glance which file came back.

The 301 version

This is the first thing most of us write.

# A: route with a 301
map $arg_w $res { default ""; "640" "w640"; "1280" "w1280"; }
 
location ~ ^/a/img/(?<f>.+)$ {
  if ($res = "") { return 301 /img/$f; }
  return 301 /img/$res/$f;
}

Here is what curl shows.

$ curl -si "http://127.0.0.1:8081/a/img/sunset.jpg?w=1280" | sed -n '1p;/^Location/p'
HTTP/1.1 301 Moved Permanently
Location: http://127.0.0.1:8081/img/w1280/sunset.jpg

Following the redirect gives num_redirects of 1, so fetching one image costs two round trips, and the access log grows two lines per image. After 300 calls the log held exactly 303 lines with a 301 status (the extra three are my manual checks).

There is a second point that is easy to miss. In the HTTP specification (see the 301 entry in RFC 9110), a 301 is cacheable by default. Browsers remember "this URL moved there," and a corrected rule may not reach a client that already remembered the old answer. That is why you verify in a private window. This part is a reading of the spec; I did not measure how long any given browser keeps it.

✦

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 tell with a single curl whether your image delivery changes the URL or keeps it
✦You can take home a resolution router built from map and rewrite, with no if, that falls back to the original for unexpected values
✦You can avoid shipping a 301 that browsers and CDNs remember and that you can no longer fix
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 $10 for lifetime access
View Membership →

Related Articles

◇ App Dev2026-10-15
My Ads Never Came Back After a Subscription Expired: Fixing the Direction of an Ad-Free Check
An ad-free flag stayed on after the subscription expired because the check pointed the wrong way. Here is how I rebuilt it to wait for two purchase queries, decide only from confirmed facts, and keep the last value on failure.
◇ App Dev2026-10-15
When adjustsFontSizeToFit Shrinks Just One Row, Suspect the Box Height, Not the String
Only the 10th row of a list drops to 4pt on iOS, though the data and strings are fine. I read the Yoga rounding and font-fit sources, then counted how often the box comes up short in Node.
◇ App Dev2026-10-15
When Only EAS Build Fails with "No matching variant", Check Your Local android/build First
Gradle passes on your machine, but the Android build on EAS stops at "No matching variant of project". Reading the reports on GitHub, I traced how a Windows-generated autolinking cache can reach the Linux builder, and wrote a small script that checks for it before you queue a build.