Skip to content

Asset serving & caching (Cloudflare + S3)

We serve public ecommerce assets (mostly images) from S3, fronted by Cloudflare, to minimize origin bandwidth and keep performance predictable under load.

Why Cloudflare (instead of CloudFront)

CloudFront is a solid default for S3, but for our usage patterns it can become expensive and operationally heavy (invalidation workflows, distribution management, per-request cost profile).

Cloudflare gives us:

  • A global CDN with strong caching controls
  • Simple DNS-based onboarding
  • Consistent edge behavior across our stack (Pages, Access, DNS, WAF, CDN)

Core pattern: “domain bucket” + Cloudflare proxy

For assets that must be reachable at:

https://ecomm-assets.example.com/<object-key>

we use an S3 bucket whose name equals the hostname:

bucket name == ecomm-assets.example.com

This enables the S3 Website endpoint hosting mode, where the bucket is selected by hostname.

DNS

In Cloudflare DNS:

  • Create a CNAME record for the hostname (e.g. ecomm-assets.example.com)
  • Point it to the bucket’s S3 website endpoint (region-specific hostname)
  • Keep it proxied (orange cloud) so Cloudflare can cache and terminate TLS

TLS/SSL mode: Website endpoints are HTTP-only

S3 Website endpoints do not support HTTPS at the origin.

That implies:

  • Visitors connect to Cloudflare over HTTPS
  • Cloudflare connects to the S3 website origin over HTTP

So the hostname must be configured in Cloudflare with SSL/TLS mode = Flexible (for that hostname), otherwise Cloudflare may attempt HTTPS-to-origin and fail.

Avoid Workers for asset delivery

This architecture intentionally avoids Cloudflare Workers on the hot path for assets:

  • Workers introduce per-request cost/limits
  • A Worker route attached to an asset hostname will intercept traffic and can degrade availability under load

Rule of thumb: ensure no Worker routes match your asset hostname.

Caching strategy

  • Origin headers: set long-lived Cache-Control on immutable assets (common for content-addressed/unique filenames).
  • Cloudflare cache rules: cache “static” paths aggressively; rely on filename changes for cache busting.
  • Public access: assets are publicly readable; Cloudflare is the primary performance and bandwidth control layer.

CORS (for browser-based uploads)

If the admin/CMS uploads directly to S3 using presigned URLs, the bucket should allow the required CORS methods/headers (commonly PUT, POST, GET, HEAD and ETag exposure). CORS is orthogonal to caching, but it’s required for reliable browser uploads.

Trade-offs and alternatives

The “domain bucket + website endpoint” pattern is simple and cost-effective, but it’s not the only option:

  • If you require an HTTPS origin, consider CloudFront (or another HTTPS-capable origin) and terminate TLS with ACM at the origin.
  • If you want to keep Cloudflare but avoid website endpoints, you can use the S3 REST endpoint + different hostname patterns, but dotted bucket names and certificate constraints can complicate TLS.