Guides · July 6, 2023
Image & Asset Pipelines for Storefronts (R2, CDNs, Zero Egress)
A working image and asset pipeline for a modern storefront means object storage with no egress fees, an image-resizing edge layer, and disciplined use of next/image or an equivalent — here is how to wire it together end to end.
By Polo Themes
For a headless or Next.js storefront, the fastest, cheapest, and most maintainable image pipeline in 2026 is: store originals in an S3-compatible bucket with zero egress fees (Cloudflare R2 is the default choice), transform and cache them at the edge with a resizing layer such as Cloudflare Images, an open-source proxy like imgproxy, or a CDN's built-in image service, and serve them into your components through next/image (or your framework's equivalent) so responsive sizes and modern formats are generated automatically. The reason this combination wins is cost and control: bandwidth-billed CDNs turn a single viral product page into a surprise invoice, while R2-style storage decouples what you pay for storage from what you pay for serving.
This guide walks through the full pipeline — storage, transformation, delivery, and the framework-level plumbing — with enough specifics that you can actually implement it, not just understand it conceptually.
Why the Image Pipeline Matters More Than It Used to
Product imagery is usually the single largest source of page weight on a storefront, and it is also the asset class most directly tied to conversion — shoppers judge fit, quality, and trust from photos before they read a word of copy. At the same time, storefronts increasingly ship dozens of images per product (multiple angles, color variants, lifestyle shots, zoom-ready high-resolution masters) multiplied across a catalog that can run into the thousands of SKUs. That combination — image-heavy pages, image-heavy catalogs — means the pipeline decisions you make early (where files live, how they get resized, how they get cached) compound into either a fast, cheap storefront or a slow, expensive one. Retrofitting a pipeline after a catalog has grown is possible but painful, so it is worth getting the architecture right before the first hundred products go live.
Step 1: Choose Object Storage With Zero Egress Fees
The traditional pattern — store originals in Amazon S3, serve through CloudFront — still works, but it bills you for every gigabyte that leaves AWS's network. For an image-heavy storefront that can add up fast, and it creates a strange incentive to under-serve image quality to control cost. Cloudflare R2 exists specifically to break that model: S3-compatible API, no egress fees, and pricing based only on storage and Class A/B operations. Backblaze B2 takes a similar zero-egress-friendly stance when paired with Cloudflare's bandwidth alliance. The practical upshot is that you can store full-resolution masters without worrying that a busy month turns into a bandwidth bill shock.
- Store originals, not pre-resized variants. Keep one high-resolution master per image in R2 (or your bucket of choice) and generate every derivative — thumbnail, card, zoom, WebP/AVIF — on demand at the edge. This keeps your bucket as the single source of truth and avoids a proliferation of half-updated resized copies.
- Use content-addressed or product-scoped keys. A key structure like "products/{productId}/{imageId}.jpg" (or a hash-based key if you want automatic cache-busting on replacement) keeps the bucket organized and makes it trivial to find every asset tied to a product when it's discontinued.
- Set bucket-level lifecycle and versioning policies deliberately. Product photography rarely needs infinite version history, but accidental overwrites do happen — a short-lived versioning window (a few weeks) is a cheap insurance policy against a bad re-upload.
- Keep originals private, serve through signed URLs or a proxy. Don't make the raw bucket publicly readable at full resolution; route everything through your transformation layer so you control caching headers, formats, and access.
Step 2: Add an Image Transformation Layer
Storage alone doesn't solve responsive images — you still need to produce the right size and format for each device and viewport, and you need that to happen without pre-generating every permutation by hand. Three approaches dominate in 2026, and they aren't mutually exclusive:
Managed edge image services
Cloudflare Images and similar CDN-native services sit in front of your bucket and handle resizing, format negotiation (serving AVIF or WebP to browsers that support it, falling back to JPEG otherwise), and quality tuning through URL parameters or a transform API. This is the lowest-maintenance option: you point the service at your R2 bucket (or upload directly to the service), request "/cdn-cgi/image/width=800,format=auto/..."-style URLs, and the service handles the rest, caching results at the edge close to the shopper.
Self-hosted proxies (imgproxy, thumbor)
imgproxy is the most commonly deployed open-source option — a small, fast Go service that reads from your bucket, applies transformations specified in the URL (often signed for security), and returns the processed image with proper caching headers. Running it yourself means more operational surface (you need to deploy it, usually behind your own CDN for edge caching), but it gives you full control over transformation logic, supports self-hosted deployments in regions or environments where a managed image service isn't available, and avoids per-transformation vendor pricing entirely — you pay for compute, not per image processed.
Framework-level transformation (next/image's built-in loader)
Next.js ships an image optimization API out of the box that can resize and reformat images on request, cache the results, and serve them through its own route. This works well for small-to-medium catalogs and simple deployments, but at real storefront scale you generally want to delegate to a dedicated edge service (managed or self-hosted) and configure next/image's "loader" option to point at it, rather than routing every image transform through your application server — that keeps the compute-heavy work off your Next.js runtime and closer to the shopper geographically.
Step 3: Wire It Into next/image (or Your Framework's Equivalent)
Whichever transformation layer you pick, the goal at the component level is the same: never ship a raw, full-resolution master into a product grid or hero image. In a Next.js storefront, that means using next/image everywhere product and content imagery appears, with a custom loader configured to call your R2-backed transformation service instead of (or alongside) Next's built-in optimizer. Practical points that matter in production:
- Always set explicit width/height or use the "fill" mode with a sized parent — this is what prevents cumulative layout shift as images load, which is both a user-experience and a Core Web Vitals concern.
- Use the "sizes" attribute accurately for responsive grids. A four-column product grid on desktop that collapses to two columns on mobile needs a "sizes" value that reflects that, or the browser will request an oversized image on small screens.
- Mark above-the-fold hero images as "priority" so they aren't lazy-loaded behind the fold logic, and leave everything else to lazy-load by default — a hundred-product collection page has no business eagerly loading every thumbnail.
- Prefer "format=auto"-style negotiation over hardcoding AVIF or WebP. Let the edge layer decide based on the requesting browser's Accept header rather than baking a single format into every URL.
- Cache aggressively at the edge with long max-age headers and content-addressed URLs, so a product photo update produces a new URL (via a hash or version suffix) instead of requiring a cache purge across every edge node.
Step 4: Don't Forget Non-Image Assets
"Asset pipeline" for a storefront isn't only photography. Size guides, downloadable spec sheets, video (unboxing or product-demo clips), and increasingly 3D/AR model files (glTF/USDZ for AR try-on) all benefit from the same zero-egress storage plus edge-cache pattern. Video in particular should not be self-hosted raw from your storage bucket — route it through a video-specific CDN or streaming service that handles adaptive bitrate delivery, since a single 4K product video served at full bitrate to every visitor is one of the fastest ways to blow a bandwidth budget even on a zero-egress storage tier (the CDN or streaming layer in front of it may still bill for delivery). Treat these assets with the same discipline: originals in cheap, durable storage; transformation and delivery handled by a purpose-built layer in front of it.
Putting the Pieces Together: a Reference Flow
A concrete end-to-end flow looks like this: a product photo is uploaded once to an R2 bucket at full resolution. A request for that image from a storefront page goes through your CDN, which forwards to an image-transformation worker (managed or self-hosted) that reads the R2 original, resizes and reformats it based on the requested parameters, and returns the result with long-lived cache headers. The CDN caches that specific size/format combination at the edge, so the next visitor in the same region gets it without hitting the transformation layer again. Inside the storefront, next/image (or your framework's image component) is configured with a custom loader pointing at that transformation endpoint, and every product image, collection thumbnail, and hero banner flows through it with correctly set sizes and lazy-loading behavior. The result is a pipeline where storage cost stays flat and predictable as bandwidth grows, and where the shopper — regardless of device or network — gets an appropriately sized, modern-format image with minimal layout shift.
This kind of pipeline is exactly the layer of infrastructure that separates a genuinely fast headless storefront from one that merely looks fast in a demo. It's also one of the areas where design and engineering decisions meet directly — a design system with well-defined image aspect ratios and grid breakpoints (the kind of groundwork in our Figma UI kits) makes the sizes/breakpoint math in step three far more predictable than trying to retrofit responsive rules onto ad hoc layouts.
Common Mistakes Worth Avoiding
- Serving originals directly from the bucket without a transformation layer. This forces the browser to download full-resolution masters even on small screens, and it removes your ability to do modern-format negotiation.
- Choosing bandwidth-billed storage for an image-heavy catalog without modeling the cost. A viral product or a seasonal spike can turn a manageable bill into a five-figure surprise; zero-egress storage removes that risk category entirely.
- Skipping the "sizes" attribute on responsive images. Without it, browsers often fall back to requesting the largest available image regardless of viewport, defeating the purpose of responsive markup.
- Purging the entire CDN cache on every product update. Content-addressed URLs (hash or version suffix in the path) mean only the changed asset gets a new URL — old cached copies simply age out, and there's nothing to purge.
- Routing video through the same pipeline as images. Video needs adaptive bitrate streaming and a purpose-built delivery layer; treating it like a large image leads to slow, expensive playback.
Frequently Asked Questions
Do I need Cloudflare R2 specifically, or will any S3-compatible bucket work?
R2 is the most commonly cited example because of its zero-egress pricing and native pairing with Cloudflare's CDN and image services, but the pattern generalizes — any S3-compatible storage with low or no egress fees, paired with an edge transformation and caching layer, achieves the same result.
Is next/image's built-in optimizer enough, or do I need a separate image service?
For smaller catalogs and simpler deployments, next/image's built-in optimizer is often enough on its own. As catalog size and traffic grow, delegating transformation to a dedicated edge service (managed or self-hosted) via a custom loader keeps that work off your application server and closer to the shopper.
Should I convert all my images to AVIF ahead of time?
Generally no — pre-converting locks you into one format regardless of what the requesting browser actually supports. Format negotiation at the edge (serving AVIF, WebP, or JPEG based on the browser's Accept header) gives every visitor the best format their browser can render without you maintaining multiple static copies.
How does this pipeline interact with a design system or theme?
A design system that defines consistent image aspect ratios and grid breakpoints makes the transformation and sizes configuration far more predictable, since you're generating a known, finite set of derivative sizes instead of reacting to arbitrary ad hoc layouts. This is one reason image pipeline work and front-end design work are worth planning together rather than bolting one onto the other late.