DEV Community

Casper Tran
Casper Tran

Posted on

When you can't edit the size in an image URL: signed variants on itch.io and Flickr

In my last two posts I rewrote size tokens in image URLs: change feed_thumbnail to feed_fullsize, drop .600x338, and the CDN hands you the bigger file. That trick has a limit. Some CDNs sign each size, so the URL for the large copy contains a value you can't compute from the small one. Edit the size and keep the old signature, and the server refuses.

I measured two sites that work this way and one that doesn't, on 10 November 2026. Each file was fetched with curl (Flickr through Chrome, since it rate limits plain curl from my IP) and the pixel size was read from the image itself with Pillow or createImageBitmap.

itch.io: a signature per size

A screenshot on an itch.io game page looks like this:

https://img.itch.zone/aW1hZ2UvNDM0NTU0LzcwMTEwMTIuanBn/347x500/ca0rKj.jpg
Enter fullscreen mode Exit fullscreen mode

The first segment is base64 for image/434554/7011012.jpg, the stored file. The second is the size box. The last one, ca0rKj, is a signature over both. The page uses three variants of that screenshot, each with its own signature:

URL tail Where it appears Result
347x500/ca0rKj.jpg src 347×195, 30,571 bytes
794x1000/ei%2BnwE.jpg srcset 2x 794×446, 86,736 bytes
original/3t28LO.jpg <a href> around the image 1920×1080, 263,991 bytes

Now the obvious edit, swapping the size and keeping the signature:

URL tail Result
original/ca0rKj.jpg 404, body not found (bad signature)
794x1000/ca0rKj.jpg 404, same body
1000x1000/ca0rKj.jpg 404, same body
original/3t28LO.jpg on another screenshot's path 404

The last row matters: the signature covers the image path too, so you can't reuse one valid original signature across screenshots. Without the server's secret there is no URL to construct.

The page already has the answer, though. Here is the markup, trimmed:

<a data-image_lightbox="true"
   href="https://img.itch.zone/aW1hZ2Uv.../original/3t28LO.jpg">
  <img src="https://img.itch.zone/aW1hZ2Uv.../347x500/ca0rKj.jpg"
       srcset=".../347x500/ca0rKj.jpg 1x, .../794x1000/ei%2BnwE.jpg 2x"
       class="screenshot">
</a>
Enter fullscreen mode Exit fullscreen mode

The lightbox link points at the 1920×1080 upload, already signed.

Flickr: a different secret per size

Flickr URLs put a hex secret before the size letter. On one Explore photo:

URL tail Result
55638737042_37e1244f5b.jpg 499×333
55638737042_37e1244f5b_w.jpg 400×267
55638737042_37e1244f5b_b.jpg 1024×683
55638737042_37e1244f5b_h.jpg 410
55638737042_37e1244f5b_k.jpg 410
55638737042_37e1244f5b_o.jpg 410

So the small sizes share one secret and the large ones don't. The photo page's HTML lists the large sizes with their own secrets:

URL tail Result
55638737042_e02d4a895b_h.jpg 1600×1067
55638737042_91033b130b_k.jpg 2048×1365
55638737042_93ce021d94_o.jpg 8256×5504, 24,303,853 bytes

Three sizes, three secrets, none derivable from 37e1244f5b. On Explore and album grids only the small URLs are in the DOM, so anything that works from the grid has to stop at _b or go fetch every photo page.

Gumtree: a named variant, no signature

For contrast, Gumtree's image CDN names its variants and checks nothing else. One ad photo:

URL tail Result
/86 (what the ad page loads) 768×507, 46,852 bytes
/public 1163×768, 96,698 bytes
/1 400×264
/79 320×211
/original 404

Here a rewrite rule is fine, because the variant name is the whole key. I can't tell whether /public is the uploaded file or just the largest variant, only that it's the largest one I found.

What the extension does about it

When the size is signed, guessing a URL is pointless, so the image downloader I build does two cheaper things. First, when an <img> is wrapped in a link whose href looks like an image file, it keeps that link as the full-size candidate:

const IMG_LINK_RE = /\.(jpe?g|png|gif|webp|avif|bmp|svg|tiff?|jfif)(?:$|[?#])/i;

const a = el.closest('a[href]');
const href = a && a.getAttribute('href');
const linkFull = href && IMG_LINK_RE.test(href) ? href : null;
Enter fullscreen mode Exit fullscreen mode

On itch.io that picks up original/3t28LO.jpg for every screenshot, with no site-specific code. Second, it parses srcset per the HTML spec and takes the largest candidate, so even without the link it would get the 794 px copy instead of the 347 px one.

The link still goes through the same URL rules as everything else, because some gallery links are themselves resized copies, and the unmodified link stays as a fallback if the rewritten one fails. For Flickr the rule only rewrites small suffixes to _b; a test pins that boundary so nobody "improves" it into a 410 later.

The general lesson I took from this: before writing a rewrite rule, fetch the variant with the old signature and see whether the server checks it. If it does, look in the page for a link or a srcset the site already signed for you.

I wrote up the itch.io steps, with and without an extension, here: how to download itch.io screenshots in full size. The extension that does the link and srcset handling above is PixLasso, a Chrome extension for saving images from a page.

Top comments (0)