Codegea

Development

Custom software developmentSystems, platforms and integrations built around your operationCorporate websitesProfessional, fast and built to inspire trustOnline storesCatalog, payments and shipping ready to sell 24/7WooCommerceFast WooCommerce stores with each country’s payments and invoicingLanding pagesA page focused on one goal: turning visitors into customersMobile appsCustom iOS and Android apps that put your business in your customers’ pockets

Integration and CMS

CMS integrationsWordPress, Strapi, Directus and Arc XP connected to your whole ecosystemArc XPSupport, implementation and integrations for the media platformMigrationsMigrate your site, platform or database without losing anything

Growth and infrastructure

AI automationTechnology does the repetitive work and your team focuses on what mattersOn-page SEOBe found and recommended by Google: technical and content optimizationServers & hostingFast, secure servers monitored 24/7
RetainerYour dev team, by the monthFrom 20h a month. Fixed fee.
An AI quoting tool to quote services in minutesProfessional quotes in minutes, with projects, sellers and payments tracked in one place.See the case

By type

EcommerceStores and catalogsCorporate sitesBrand and lead genPlatforms & appsCustom software

Explore

All projectsCases and resultsClientsWho trusts usRetainerMonthly dev team

Blog

All articlesGuides and opinionNewsletterOne email a monthContactWe reply in 24h

Latest articles

How codegea.com is built: Next.js, Payload, Turso and R2CMS · 2026-10-20Cloudflare Workers: what they are and when to use themNode.js · 2026-10-19Live Preview in Payload with Next.jsCMS · 2026-10-19
NewsletterJoin our newsletterPractical web, SEO and AI notes.
ServicesCustom software developmentCorporate websitesOnline storesWooCommerceLanding pagesMobile appsCMS integrationsArc XPMigrationsAI automationOn-page SEOServers & hostingYour dev team, by the month
ProjectsProfessional quotes in minutes, with projects, sellers and payments tracked in one place.EcommerceCorporate sitesPlatforms & appsAll projectsClientsRetainer
Retainer
BlogAll articlesNewsletterContactHow codegea.com is built: Next.js, Payload, Turso and R2Cloudflare Workers: what they are and when to use themLive Preview in Payload with Next.jsJoin our newsletter
ESEN
Let's talk

Servers

Cloudflare R2 vs Amazon S3 for storing your website images

R2 and S3 speak the same protocol but are billed differently. When each one makes sense for website images and how we connect it to Payload.

Derian Pinto · Founder & Full Stack Developer2026-10-183 min

If your website stores images in a bucket, you probably thought of Amazon S3. Cloudflare R2 does the same thing, with the same API, and is billed differently. For website images, that difference weighs more than it seems.

The difference that matters: data transfer out

Every time someone opens your site, their browser downloads the images from your bucket. That download is outbound traffic. S3 charges for it. R2, according to Cloudflare's documentation, does not charge for data transferred out of the bucket. What it charges is storage and the number of operations (those that modify data and those that read it), and there is a monthly free allowance for standard storage.

For a site with many images and visits, that is the part that changes the bill the most. For a small one, with few images and little traffic, the difference may be minimal. Check each provider's current pricing before deciding: it changes.

What they share

R2 can be used with the S3 API. In practice, libraries and plugins that talk to S3 work when pointed at another endpoint. That lets us use Payload's official S3 storage plugin on this site, the same one you would use with Amazon.

What changes

  • Custom domain. R2 lets you serve the bucket from your own domain, something like media.yourdomain.com. That option requires the domain to be added as a zone in the same Cloudflare account as the bucket. The r2.dev URL is rate-limited and for development only; use your domain in production.
  • Ecosystem. S3 has many more services around it: notifications, very fine-grained lifecycle policies, integration with the rest of AWS. If your application already lives on AWS, S3 in the same region is the most natural choice.
  • Cloudflare. R2 integrates with Workers and with Cloudflare's network, which gives good performance for public files.

How we connect it to Payload

With the @payloadcms/storage-s3 plugin. This is this site's configuration, abridged:

s3Storage({
  collections: { media: publicFiles },
  bucket: process.env.S3_BUCKET ?? '',
  config: {
    endpoint: process.env.S3_ENDPOINT,
    region: process.env.S3_REGION ?? 'us-east-1',
    forcePathStyle: true,
    credentials: {
      accessKeyId: process.env.S3_ACCESS_KEY_ID ?? '',
      secretAccessKey: process.env.S3_SECRET_ACCESS_KEY ?? '',
    },
  },
}),

The endpoint decides who you talk to. In production it points to R2; locally, to MinIO, an S3 server in a Docker container. The code doesn't change.

So that the browser requests images straight from the bucket, and not through Payload, we define how the public URL is built:

const publicFiles = process.env.S3_PUBLIC_URL
  ? {
      disablePayloadAccessControl: true as const,
      generateFileURL: ({ filename, prefix }) =>
        [process.env.S3_PUBLIC_URL?.replace(/\/$/, ''), prefix, filename].filter(Boolean).join('/'),
    }
  : true;

With S3_PUBLIC_URL defined (your R2 domain), Payload returns that URL. Without it, as happens locally, it serves the files itself. For credentials, R2 gives its own API key pair, created in the Cloudflare dashboard.

A detail that bit us

If you upload images from a script, don't reuse the same context between payload.create calls. The S3 plugin keeps state there, and the second upload in the process went missing. Passing a fresh context on each call fixed it. We tell it along with other stumbles in how codegea.com is built.

When to choose each

  • R2: sites with many images or downloads, where data transfer out would weigh, and projects where you already use Cloudflare for DNS.
  • S3: if your application already runs on AWS, or you need Amazon ecosystem features that R2 doesn't have.

And after that infrastructure decision, the next step is usually putting the rest of the site behind Cloudflare. We cover it in Cloudflare in front of your website.

Share this article?LinkedInWhatsAppX

Written by

Derian Pinto

Founder & Full Stack Developer

Founder of Codegea. Builds custom websites, stores and platforms, and integrates and supports Arc XP for media companies.

Join our newsletter

An email with news and practical web, SEO and AI notes. No spam, unsubscribe anytime.

Shall we build your project?

Need help? Chat with us