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:
we use an S3 bucket whose name equals the hostname:
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-Controlon 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.