Operations

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.

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_url in the admin console (Settings → Runtime settings → Access & links → Public site address), and add the CDN's origin-pull ranges to IMGYOUNG_TRUSTED_PROXIES so 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:

ResponseCache-ControlETagVary
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 resizedAccept when the target format was negotiated
Original or variant under a /o/ aliaspublic, no-cachesame as abovesame as above
Original served as a fallback after processing failedno-storenonenone
If-None-Match matches304 with the matching Cache-Control, ETag and Vary
302 (redirect mode)no-storenonenone
Invalid parameters (400), hotlink rejection (403), not found or expired (404), metadata lookup failure (500, retryable)nonenonenone

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

  • w must be one of the width tiers and format one of the target formats or auto; 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 w and format.
  • 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:

SettingWhereNotes
Cloudflare Zone IDCDN cache purge groupID of the zone your site is in
Cloudflare API tokenCDN cache purge groupNeeds the Cache Purge permission for that zone; stored encrypted with the master key and never shown again
Public site address (base_url)Access & links groupUsed 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 / format parameters, or WebP negotiation) are still processed and served by ImgYoung, not redirected. To make browsers' original-image requests redirect as well, remove webp from 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).

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.

On this page