Skip to content

Cloudflare Images and Image Resizing: When the Built-In Service Wins

Three options, three different cost curves. Picking right matters more than people think.

John Cravey with AIFounder3 min readUpdated Jul 6, 2026

There are three reasonable ways to serve optimized images for an SMB site in 2026: next/image fronted by Supabase Storage, next/image fronted by R2, or Cloudflare Images (the dedicated service). Each has a different cost model and a different operational story. Here’s how to pick.

Free estimate · 2 minutes

Slow pages lose rankings and leads. Price the fix.

Build the estimate to see what a fast rebuild would look like for your site and what we would ship in the first thirty days.

Option 1: next/image + Supabase Storage

What it is: store originals in Supabase Storage, serve through next/image which resizes and converts on demand. The FH default for any SMB site under 100GB of images.

Cost: Supabase Pro at $25/month includes 100GB storage + 250GB egress. Most SMB sites stay inside that. next/image processing happens on your Next.js host; CPU cost is bundled with the host you’re already paying for.

Wins when: small-to-medium sites, you’re already on the Supabase + Coolify/Vercel stack, you want one integrated platform.

Option 2: next/image + Cloudflare R2

What it is: store originals in R2, serve through next/image. R2 has zero egress fees, which matters once you exceed Supabase’s included egress.

Cost: R2 storage is $0.015/GB-month. 1TB stored = $15/month. Zero egress. next/image processing still happens on your Next.js host.

Wins when: media-heavy site, you exceed Supabase’s 250GB included egress, you want predictable storage cost at scale.

Option 3: Cloudflare Images

What it is: Cloudflare’s dedicated image service. Upload originals, get back URLs with resize/transform parameters in the path. Cloudflare handles storage + transformation + delivery, all at the edge. No next/image required — just an `<img>` tag with the right URL.

Cost: $5/month per 100k images stored, $1/month per 100k images delivered. At 50k images stored, 500k requests/month, you’re paying $7.50/month. Scales linearly.

Wins when: user-uploaded photos at scale, sites with hundreds of thousands of unique images, sites where you don’t want to run your own resizing pipeline.

The decision tree

  1. Under 100GB total image storage, predictable usage? → Supabase Storage. Stay there.
  2. Heavy growth in images, going past 250GB egress? → R2 + next/image.
  3. Tens of thousands of user-uploaded images, transformation requirements (multiple sizes, watermarks, format conversion)? → Cloudflare Images.
  4. Cost-sensitive at scale with global audience? → R2 + Cloudflare Images. The combination eliminates egress fees entirely.

Cloudflare Images integration with Next

Cloudflare Images URLs look like `https://imagedelivery.net/<hash>/<image-id>/<variant>`. The `<variant>` is the resize/quality preset. You define variants in the Cloudflare dashboard (e.g., `thumbnail-300x300`, `hero-2400x1350`, `og-1200x630`) and reference them in URLs.

// app/portfolio/[slug]/page.tsx
import Image from "next/image";

export default function PortfolioItem({ params }: { params: { slug: string } }) {
  const heroId = "abc123";
  return (
    <Image
      src={`https://imagedelivery.net/${HASH}/${heroId}/hero`}
      alt=""
      width={2400}
      height={1350}
    />
  );
}

Polish and transformations

Cloudflare Images supports cropping, sharpening, format conversion, blur, brightness adjustment. We use these for: thumbnails (square crop, blur background fallback during load), social previews (1200×630 with brand overlay), responsive variants (multiple widths). The transformation cost is bundled in the delivery cost.

Migrating to Cloudflare Images from Supabase

Upload each image to Cloudflare Images via their API, get back the new image ID, store it in your database alongside the old Supabase path. Update your code to use the new URLs. Run both for a week to verify. Then delete the Supabase paths. We did this for one retail client — 45k images, $11/month savings vs. their previous Supabase egress overage. Migration time: 1 day.

When Cloudflare Images isn’t worth it

  • Under 5k images. The dashboard overhead and integration cost outweighs the benefit.
  • Images that change frequently. Cloudflare Images is optimized for upload-once-serve-many; if you re-upload often, you’re paying twice (storage + delivery) for the same content.
  • Sites that need raw image URLs (downloadable originals). Cloudflare Images doesn’t make originals accessible without an authenticated API call.

Bandwidth math at typical SMB volumes

An SMB marketing site averages 25k page views/month, 4 images per page = 100k image requests. At an optimized 80KB per image = 8GB egress/month. Well inside Supabase’s 250GB included tier. We don’t need any of the egress-saving alternatives until egress exceeds 200GB/month, which happens at roughly 12x typical SMB volume.

How this lands across FH client work

Five FH client sites use the default next/image + Supabase Storage pattern. One retail client moved to R2 + next/image for cost reasons after exceeding 600GB egress/month. One UGC-heavy client uses Cloudflare Images. The right answer depends on volume, not on what’s trendy. Book a consultation if you want a recommendation specific to your traffic pattern.

Answers

Frequently asked questions

What are the realistic options for serving images?

Three: the framework's optimizer over object storage, the optimizer over an edge store, or a managed image service handling storage and transformation together. All three work; they differ in who does the resizing, where the bytes live, and what you pay per byte served.

When does the managed image service win?

At high volume, where transformation and delivery in one service removes both the optimizer's compute and the egress bill. Below that volume it is another dependency and another bill for a job the framework already does adequately.

When is the framework optimizer enough?

For most business sites. Traffic is modest, the images are few, and the optimizer's caching means the work happens once per variant. Reaching for a managed service before measuring is optimizing a cost that has not appeared yet.

What is the decision actually based on?

Bandwidth served and transformation volume, not preference. Below a threshold the difference is a rounding error and the simpler stack wins. Above it, the egress savings become the whole argument. Measure the current bill before changing anything.

How does a managed service integrate with the framework?

As a custom loader, so components keep working while the transformation moves out. That is the migration's saving grace: the change is a configuration boundary rather than a rewrite of every usage site.

What do polish and automatic transformations do?

Re-encode and adjust images at the edge without changing the source, including modern formats per browser. It is the same work the framework optimizer does, moved to the edge, which is why running both means paying twice for one result.

How do I migrate without breaking pages?

Switch the loader, verify a sample of pages across breakpoints, then remove the old path. Keep the originals until the new path is confirmed, because a partial migration where some images resolve and some do not is worse than either state alone.

When is it not worth it?

Low-traffic sites, image-light sites, and anywhere the team would now be operating two image systems because the migration stalled halfway. That last case is common and it is worse than either option chosen wholeheartedly.

What does the bandwidth math look like at typical volumes?

For most small-business sites the total image bandwidth is small enough that the saving is a few dollars, which does not justify a migration. The number becomes interesting when a site serves large media at volume, and at that point it is obvious rather than marginal.

Does this affect Core Web Vitals?

Only through delivery speed and correct sizing, which any of the options can achieve. Choosing a service does not improve the metric; serving a right-sized modern-format image with reserved layout space does, and that is available on every path.

Should originals still be stored separately?

Yes. Keep an unmodified master somewhere you control, whatever serves the transformed versions. A pipeline holding only derivatives means re-cropping later is a re-shoot, and that discovery always happens at the worst moment.

What is the simplest setup that works?

Object storage for the masters, the framework optimizer for delivery, correct width, height, sizes, and one priority image per page. That covers the vast majority of sites correctly, and everything above it is an optimization to reach for when a measured bill says so.

Question we did not answer? Ask us directly and we will answer it here.

John Cravey, Founder
Written by
John Cravey
Founder

Founder of Frontend Horizon. Writes most of the long-form work on the FH blog.

Newer post
Supabase Performance: Indexing, Connection Pooling, and the Postgres Settings That Matter
Older post
Tool Use With Claude: Building Agents That Don’t Hallucinate Your Production Data
Keep reading

More from the blog

Cloudflare·6 min

Cloudflare DNS and CDN: The Base Configuration for Every FH Client Site

Every FH site sits behind Cloudflare. Here’s the exact configuration and why each setting is where it is.

Supabase·5 min

Supabase Storage for Marketing Sites: The Bucket-Per-Tenant Pattern

Most teams store images in their build artifact. That doesn’t scale. Supabase Storage with the right bucket layout does.

Cloudflare·4 min

Cloudflare Turnstile: The CAPTCHA That Doesn’t Make Your Users Hate You

reCAPTCHA hurts conversion. Turnstile doesn’t. Here’s the wiring that keeps your forms spam-free without the click-the-bicycles ritual.