Image Optimizer - StaticDelivr CDN banner

Image Optimizer - StaticDelivr CDN

11 devlogs
300h 54m 18s

Image Optimizer – StaticDelivr CDN is a free, open-source image proxy that works anywhere you can put an image URL. Send any public image to https://cdn.staticdelivr.com/img/images?url= and it returns an optimized WebP, AVIF, or JPEG with automati…

Image Optimizer – StaticDelivr CDN is a free, open-source image proxy that works anywhere you can put an image URL. Send any public image to https://cdn.staticdelivr.com/img/images?url= and it returns an optimized WebP, AVIF, or JPEG with automatic resizing, quality tuning, and format detection. It runs on StaticDelivr’s global edge (570+ PoPs) with a year of browser cache, proper CORS, and no API key. I originally built it for the WordPress plugin, but the endpoint is standalone — you can use it in HTML, React, Jekyll, or just paste it in a browser.

Demo Repository

Loading README...

cw

Spent some time whiteboarding and testing the CDN path. User sends a request, it hits the CDN edge first, not the origin. The edge checks the CDN CACHE. If there’s a hit, it returns the response immediately. If there’s a miss or no response, it forwards the request to the Origin Server, gets the image, runs the optimization, stores it in the cache, then serves it back to the user.

This is why the Vary and Cache-Control headers I added last week matter so much. Without them, the CDN would serve the same WebP to a browser that only accepts PNG. With the proper headers, the cache stores separate variants per Accept header.

I also built the fallback logic shown in the diagram. If the origin is down, the CDN can still serve a stale cached response instead of a 500. Tested it by killing the origin locally and the edge still returned the last good image for 60 seconds.

This isn’t just theory anymore. The diagram is now how the worker actually routes traffic. Next step is adding cache purging by URL pattern so updates propagate faster.

Attachment
0
cw

Reading about region-of-interest-based compression. The flow in the screenshot is exactly what I was looking for. You take the input image, define an ROI and a RONI area either manually or automatically, then treat them differently. ROI gets lossless compression applied so text, faces, or product details stay perfect. RONI gets lossy compression applied because the background doesn’t matter as much. Then you merge the two, store or transmit, and decompress back to a normal output image.

This is different from what StaticDelivr does now, which is uniform WebP/AVIF conversion across the whole image. ROI would let me keep a logo or UI element crisp at 100% quality while crushing the background by 80%. It’s how medical imaging and satellite photos handle bandwidth, and it explains why some compressors look better at the same file size.

Not implementing it yet, but understanding the pipeline helps me decide where to add quality parameters later. Right now I’m doing global quality, future version could do automatic ROI detection for faces or text.

Attachment
0
cw

Spent 20 minutes in the official guides for cwebp and dwebp. The encoder is dead simple: cwebp -q 80 image.png -o image.webp gives you a solid baseline, and the decoder reverses it with dwebp image.webp -o image.png.

I was digging for the default quality curves and the lossless vs near-lossless switches because the optimizer needs to match what browsers expect. The docs also list gif2webp, img2webp, and webpmux which I did not know existed — useful for later when I add animated support.

It is not code, but understanding the reference implementation means my edge conversions will produce the same bytes as Google’s tools, not some weird custom preset. Small reading session, big payoff for consistency.

Attachment
0
cw

Took a standard 800x800 product photo with a transparent background, the exact kind of image that kills page speed on ecommerce sites. Original PNG came out at 320 KB. Ran it through the optimizer in lossless WebP mode and it dropped to 84 KB.

That is not a 26% saving like the generic note says, that is almost 74% smaller with identical visual quality and the alpha channel intact. The difference is because PNG is from 1996 and stores every pixel literally, while WebP lossless actually compresses.

This is why the endpoint defaults to WebP for transparent images. Same pixels, a quarter of the bytes, and it still works in canvas because of the CORS headers I added earlier. The test took 25 minutes to set up properly, but now I have a real number to point to instead of “very small” in the docs.

Attachment
0
cw

Spent 25 minutes comparing WebP vs PNG because the optimizer needs a safe default. The table says it all. WebP does both lossy and lossless compression, keeps transparency, and supports animation, while PNG is lossless only and cannot animate. Typical file size for WebP is very small, PNG is large. Browser support for WebP is now excellent, and PNG is universal since 1996.

That is why the endpoint serves WebP first when the Accept header allows it, and only falls back to PNG for old clients or when someone explicitly requests lossless with alpha at 48-bit color depth. WebP launched in 2010 so it is not new tech anymore, it is just the better default for the web.

This is the reasoning behind the content negotiation logic I added last week. It is not about chasing the newest format, it is about shipping the smallest file that still looks identical.

Attachment
0
cw

Spent 35 minutes looking at how everyone else does image optimization, starting with Tinify.

Their flow is super clear: drop up to 20 images, max 5MB each, toggle “convert automatically,” and it gives you back AVIF, WebP, PNG, or JPEG. It’s built for designers who want to manually compress files before upload. Works great, but it’s a manual step, and the API is paywalled after the free tier.

That confirmed what I did not want to build. StaticDelivr is not a drag-and-drop compressor. It’s a URL. You give it an image link and it returns the optimized version on the fly, with format negotiation, long edge caching, and CORS headers already set. No upload, no dashboard, no 20-file limit.

Looking at their “Smart AVIF, WebP, PNG and JPEG Compression” messaging also made me realize I need to be clearer in the docs that we do the same conversions, just automatically at the edge instead of in a web UI. The panda is cute though, I will give them that.

Attachment
0
cw

Quick docs pass to match what the optimizer actually does now.

Updated the feature list to call out the four things people keep asking about. Image Optimization now specifically mentions WebP and AVIF conversion and the 2MB to 20KB savings, Google Fonts Privacy Proxy notes the GDPR stripping, Automatic Fallback explains the failure memory from 1.7.0, and Localhost Detection clarifies that dev environments stay local.

It was only 19 minutes of editing, but it stops the support questions where people think the CDN will break their local setup. The code already did all of this, the readme just finally says it clearly.

Attachment
0
cw

Adding proper Vary headers to the image endpoint.

I was seeing a weird bug where Safari would sometimes get a WebP that was cached for Chrome, or a mobile client would get a desktop-sized image. That happens when you negotiate format with the Accept header but you do not tell the CDN to vary the cache key.

Spent the last 2h 7m wiring Vary: Accept, Accept-Encoding, Origin into every image response. Now the edge stores a separate version for WebP, AVIF, and JPEG, and also keeps CORS variants separate. It stops the cache poisoning without adding extra query strings.

Checked MDN before shipping and Vary has been supported since Chrome 1, Firefox 1, Safari 1, Edge 12, and even Android WebView 4.4, so it is safe to rely on everywhere. The screenshot is the compatibility table.

It is not a feature anyone notices, but it is the reason the optimizer can safely serve the right format to every browser without breaking the cache.

Attachment
0
cw

Fixing cross-origin for canvas and fonts.

The diagram shows the problem exactly. Same-origin requests to foo.example are always allowed, but anything pulled into a canvas from bar.other — like an image or a font.woff — is blocked unless CORS says it is okay.

I spent the last hour adding proper CORS headers to the image endpoint. Every response from cdn.staticdelivr.com/img now returns Access-Control-Allow-Origin: *, Timing-Allow-Origin: *, and the correct Vary: Origin so browsers will actually use the image in a canvas, WebGL texture, or with crossorigin="anonymous" without tainting it.

It is a one-line header change, but without it the optimizer is useless for anyone doing client-side image editing, meme generators, or custom font rendering. Now it just works cross-origin, which is the whole point of a public CDN.

Attachment
0
cw

I have been using hackclub.com as a baseline because it is a real site with real traffic. The PageSpeed report shows exactly the problem I was fixing. LCP is 1.8s, CLS is 0, FCP is 1.6s, TTFB is 0.6s, all green, but the Core Web Vitals Assessment still says Failed because INP is 306ms.

That 306ms is not a backend issue. It is images. Large PNGs that are not next-gen, no edge caching, and no format negotiation force the browser to download more bytes and do more decoding work on the main thread. That pushes Interaction to Next Paint over the 200ms threshold.

So I rebuilt the image pipeline around that number. The endpoint now does automatic WebP and AVIF conversion on first request, stores each variant separately with Cache-Control: public, serves with Access-Control-Allow-Origin: *, and keeps it hot on the edge. It also accepts width and quality parameters so mobile does not pull desktop-sized files.

The work was not flashy. It was persistent caching, proper content-type handling, fixing the file handle leak from the WebP workers, adding request coalescing so 50 hits do not trigger 50 encodes, and making sure the Age header actually climbs like in the last devlog. The screenshot is the before. After routing the same images through cdn.staticdelivr.com/img, total image bytes drop 30 to 40 percent and the main thread has less to decode, which is what pulls INP down.

This is the foundational work that makes the optimizer useful for any site, not just for passing a lab test.

Attachment
0
cw

First live test of the image endpoint, and the headers tell the real story.

Request to cdn.staticdelivr.com/img/images?url=https://hackclub.com/home/workshops/splatter_paint.png returned 200 OK as image/webp instead of the original PNG. It came with Access-Control-Allow-Origin: *, so it drops straight into any <img> tag with no CORS errors.

Cache-Control is public, max-age=691200 and Age is 669, which means that exact WebP has been sitting hot in cache for over 11 minutes already. Cf-Ray shows SIN, so Singapore edge served it, and Content-Encoding is gzip. It is converting, caching, and delivering from a single URL.

This is the core of the project working in the wild. No SDK, no plugin, just a link that returns a faster image.

Attachment
0