Media
Two settings of the API server decide a store's media:
- the host, where the files are kept: ImageKit, or the store's R2 bucket;
- the delivery, how browsers fetch and resize them.
The database keeps only each file's key (for example
products/3f2a9c.webp). The API answers every image with its URL, and
/store/config gives storefronts the delivery, so a storefront has nothing to
configure and moving to another host rewrites no data.
Uploads always go through the API server, so no bucket needs a CORS policy.
An upload waits in the bucket's staging/ folder until it is prepared; run
npm run r2:lifecycle once per bucket (in cod-server), and an upload that
was never finished is deleted after a day.
The settings
| Variable | Values |
|---|---|
MEDIA_HOST | imagekit: ImageKit keeps the files. r2: the MEDIA_BUCKET bucket does |
MEDIA_DELIVERY | imagekit: resized by ImageKit. cloudflare: resized by Cloudflare Images on your zone. direct: served as kept |
MEDIA_ORIGIN | A file's URL is this origin, /, then its key |
IMAGEKIT_PUBLIC_KEY, IMAGEKIT_FOLDER | With MEDIA_HOST = "imagekit" |
IMAGEKIT_PRIVATE_KEY | A secret, with MEDIA_HOST = "imagekit" |
There is no default host: a missing or wrong value refuses uploads with
MEDIA_NOT_CONFIGURED.
Locally
The example configuration keeps files in the local bucket and serves them from the API server itself:
Code
No account and no key.
ImageKit
-
In ImageKit, Developer options shows the public key, the private key and the URL endpoint (
https://ik.imagekit.io/<id>). -
Choose the store's folder, for example
/stores/mystore. One account can hold many stores. -
Set the variables:
CodeMEDIA_ORIGINis the URL endpoint followed by the folder. -
Set the private key:
npx wrangler secret put IMAGEKIT_PRIVATE_KEY --env production.
Locally, the same values go in cod-server/.dev.vars, with a folder of your
own (/dev/your-name).
R2 behind your domain
-
In the Cloudflare dashboard, open the bucket, then Settings → Custom Domains → Connect Domain, for example
media.yourdomain.com. -
Turn on Image Transformations for that zone.
-
Set the variables:
Code
MEDIA_DELIVERY = "direct" also works with R2: images are then served as
uploaded, without resizing.
After any change, redeploy the API server. Storefronts read the new delivery from the store's config; they need no change.
When uploads fail
| Error | Fix |
|---|---|
MEDIA_NOT_CONFIGURED | Set MEDIA_HOST, MEDIA_DELIVERY and MEDIA_ORIGIN |
MEDIA_HOST_REFUSED with ImageKit | Check IMAGEKIT_PUBLIC_KEY and the IMAGEKIT_PRIVATE_KEY secret |
| Images upload but do not show | MEDIA_ORIGIN must be where the host serves the store's files |