CDN
Putting a CDN in front of ImgYoung — cache headers, WebP negotiation and Vary, cache keys for on-demand processing, Cloudflare cache purging, and how redirect mode and hotlink protection behave behind a CDN.
Recommended setup: proxy mode plus a CDN
Let the CDN pull from ImgYoung (through your reverse proxy) and keep the access mode at proxy:
- ImgYoung reads image bytes from the channels and fails over between them on each origin pull, so the CDN sees one stable origin;
- on-demand processing (
?w=,?format=) and WebP negotiation keep working, and the CDN caches the results; - the public domain is the CDN's, so set
base_urlin the admin console (Settings → Runtime settings → Access & links → Public site address), and add the CDN's origin-pull ranges toIMGYOUNG_TRUSTED_PROXIESso login rate limiting counts real visitors; see Reverse proxy & HTTPS.
Cache only the image routes /i/* and /o/*; mark /api/* and /s3/* as not cacheable. The admin console's index.html is served with no-cache, and files under /assets/ have content hashes in their names and a one-year cache lifetime, so the CDN's default rules are fine for them.
Cache headers
Responses from the image routes:
| Response | Cache-Control | ETag | Vary |
|---|---|---|---|
Original under /i/<short ID> | public, max-age=31536000, immutable | "<sha256>" | Accept when a JPEG / PNG / WebP is requested without format (or with format=auto) |
Variant under /i/<short ID> (resized or converted) | same | "<sha256>-<width>-<format>", width 0 when not resized | Accept when the target format was negotiated |
Original or variant under a /o/ alias | public, no-cache | same as above | same as above |
| Original served as a fallback after processing failed | no-store | none | none |
If-None-Match matches | 304 with the matching Cache-Control, ETag and Vary | ||
302 (redirect mode) | no-store | none | none |
| Invalid parameters (400), hotlink rejection (403), not found or expired (404), metadata lookup failure (500, retryable) | none | none | none |
Originals also carry Last-Modified (the upload time). The content behind /i/<short ID> never changes, which is why it gets a one-year immutable lifetime.
/o/<user ID>/<object key> is an alias chosen by an S3 client, and uploading again with the same object key points it at the new image. Its responses may therefore be cached but must be revalidated with the ETag before each use: an unchanged image gets 304, a re-pointed alias gets the new image, a deleted one gets 404. Behind a CDN, /i/ links cache better.
WebP negotiation and Vary
When a JPEG / PNG image is requested without a format parameter (or with format=auto), and the Accept header contains image/webp and webp is among the target formats, the response is a WebP variant; otherwise it is the original format. In other words, the same URL returns different formats depending on Accept, and both responses (304s included) carry Vary: Accept.
The CDN must cache separately per Accept (by honoring Vary: Accept or adding Accept to the cache key); otherwise a cached WebP may be sent to clients that cannot display it. Cloudflare lets you configure Vary handling in Cache Rules; for other CDNs, check their documentation. If your CDN cannot do this, remove webp under On-demand processing → Target formats in the admin console: negotiation stops, at the cost of ?format=webp no longer being accepted.
Processing parameters and cache keys
wmust be one of the width tiers andformatone of the target formats orauto; anything else gets 400. The allowlist bounds how many variants an image can have, so arbitrary parameters cannot create unbounded cache entries.- ImgYoung ignores any other query parameter, but the CDN's default cache key includes it: appending a random parameter bypasses the cache and hits the origin. Restrict the cache key's query string to
wandformat. - Always write the parameters in the order
?w=…&format=…. Cache purging only clears URLs in that order (see below); a different order is a different cache entry as far as the CDN is concerned.
Cloudflare cache purging
After an image is deleted, CDN edges keep serving its /i/ links under the one-year cache lifetime. With Cloudflare cache purging configured, ImgYoung clears the edge cache when an image is deleted.
Configure three settings under Settings → Runtime settings:
| Setting | Where | Notes |
|---|---|---|
| Cloudflare Zone ID | CDN cache purge group | ID of the zone your site is in |
| Cloudflare API token | CDN cache purge group | Needs the Cache Purge permission for that zone; stored encrypted with the master key and never shown again |
Public site address (base_url) | Access & links group | Used to build the public URLs to purge; must match the domain on the CDN |
With all three set, the group shows Purge enabled; if any is missing, nothing is purged. The process also logs the purge state when it starts. Changes apply on save without a restart.
When it fires: the moment an image reference is deleted, whatever the source — deletion in the admin console (single or batch), DeleteObject on the S3 endpoint, or expiry of its lifetime. Purging does not wait for the delete grace period or for the channel objects to be removed. Images re-pointed by a deduplication merge are not purged.
What gets purged: <base_url>/i/<short ID>, plus for every variant that was actually generated for the image either ?w=<width> and ?w=<width>&format=<format> (resized variants) or ?format=<format> (format-only variants).
How: as an asynchronous background job calling Cloudflare's purge-by-URL endpoint POST /zones/<Zone ID>/purge_cache, at most 100 URLs per call. Failures are retried with backoff by the job queue and never block the deletion itself.
Not covered: format=auto forms and URLs with other query parameters or a different parameter order; these only go away when the CDN's own cache expires. /o/ alias links need no purging, since they are revalidated with the origin every time.
Redirect mode and CDNs
In redirect access mode, a request for an original gets a 302 to a channel that allows direct links, is not encrypted and has a public address configured. The bytes then come from the channel (or its own domain or CDN) and do not pass through the CDN in front of ImgYoung. Without such a channel ImgYoung falls back to proxying.
- Every 302 carries
Cache-Control: no-store, so neither the CDN nor browsers cache the redirect itself: every request comes back to ImgYoung, and channel switches, deletions and expiry take effect immediately. - Requests that need processing (
w/formatparameters, or WebP negotiation) are still processed and served by ImgYoung, not redirected. To make browsers' original-image requests redirect as well, removewebpfrom the target formats.
If you already have a CDN in front of ImgYoung, use proxy mode: the CDN already carries the traffic. redirect is meant for servers without a CDN and with little upstream bandwidth, or for channels that come with their own public CDN domain (such as a custom domain bound to an object storage bucket).
Hotlink protection behind a CDN
ImgYoung's Referer allowlist only applies to requests that reach the origin:
- requests served from the CDN cache never reach ImgYoung and are not checked; the default cache key does not include the Referer, so once an allowlisted site has caused an image to be cached, any site can load it from the CDN;
- ImgYoung's 403 carries no
Cache-Control; if the CDN caches that 403, allowlisted visitors are rejected too.
Behind a CDN, enforce hotlink protection on the CDN (a rule that blocks by Referer) and leave ImgYoung's allowlist empty or use it only as an extra layer. In redirect mode the channel's direct links are exposed, so hotlink protection does not hold there either.