Image CDN — chained 3-tier fallback (Cloudflare Images → wsrv.nl → ImageKit)
Image CDN — chained 3-tier fallback (Cloudflare Images ? wsrv.nl ? ImageKit)
Decision
Every image in the family resolves through a 3-tier chained fallback:
- Cloudflare Images — primary. Same edge as our Pages deploys, bundled with the free Pages plan, no extra signup.
- wsrv.nl — fallback 1. Public URL-transform proxy, no signup, no auth — survives outages of any authenticated provider.
- ImageKit — fallback 2. 20 GB/mo + DAM, email-only signup.
The chain is implemented as the <Image> component wrapper inside
load failure (5xx, transformation error, network) the wrapper rewrites
the src to the next rung's URL and retries.
Why
- Quota survivability. Each rung has independent operators, edges, billing surfaces. If Cloudflare Images hits a quota or has an edge failure, the next rung must work without operator intervention. wsrv.nl is deliberately the no-account middle rung so the chain survives even when an authenticated provider is the failure mode.
- Cost control. All three rungs are free at our scale. No card on
file is required at any rung — see
rules/no-card-on-file.md. - Latency-first ordering. Cloudflare Images sits on the same edge as our Pages sites, so it has the lowest p50 latency. We try fast before we try resilient.
Implementation hint
// inside @chirag127/oriz-kit
import { useState } from 'react';
const CHAIN = [
(src, opts) => `/cdn-cgi/image/${cfImageOpts(opts)}/${src}`,
(src, opts) => `https://wsrv.nl/?url=${encodeURIComponent(src)}&${wsrvOpts(opts)}`,
(src, opts) => `https://ik.imagekit.io/oriz/${imagekitOpts(opts)}${src}`,
];
export function Image({ src, ...opts }) {
const [tier, setTier] = useState(0);
return (
<img
src={CHAIN<!-- TODO: broken link, was [tier](../src, opts) -->}
onError={() => setTier(t => Math.min(t + 1, CHAIN.length - 1))}
{...opts}
/>
);
}
The Astro variant follows the same shape — is:inline script swaps
src on error. The kit owns this logic so individual sites are
unaware of the chain.
Implications
- Three separate accounts to provision: Cloudflare (already exists), wsrv.nl (no account), ImageKit (email-only signup).
- All image upload flows still target a single canonical home — see
object-storage-split.md. The CDN chain only governs delivery, not storage. <picture>/srcsetand width-set generation must repeat per rung so each tier gets the correct transform syntax. The kit helper handles this — sites don't see it.- Older
services/business/tooling/imagekit.mdandservices/business/tooling/cloudinary.mdentries continue to document those services in the tooling role (DAM, image manipulation). The newservices/media/image-cdn/entries document them specifically as CDN fallback rungs.