Configuration / Serving and Caching Settings
Files, Images and Prefill
How prefill warms a site and mirrors its pages, where files are served from, and how image styles are delivered.
Prefill and Mirroring
| var | default | what it does |
|---|---|---|
PREFILL |
on for free, off for paid | seed the serving table from assets/prefill.json |
PREFILL_ON_SAVE |
on | re-render invalidated paths after a content save |
PREFILL_ON_SAVE_LIMIT |
25 | paths one save may queue |
SAVE_DEBOUNCE_MS |
2000 | how long the refill after a save waits for the rest of a burst; 0 refills at once |
MIRROR_LIMIT |
per plan | files one alarm firing may push to R2; capped at 25 |
R2_WRITES_PER_MONTH |
900000 | R2 writes both mirrors may spend in a UTC month; 0 turns both off |
HTTP_DRAIN_ON_ALARM |
on | drain queued outbound requests from the alarm |
HTTP_DRAIN_LIMIT |
per plan | queued outbound requests one firing may fetch; capped at 25 |
PREFILL is on for free because free is where a cold first request costs the most: a prefilled path
is a hit on its first ever request. It is off during a bake, or every render would be a hit of the
file being rebuilt.
SAVE_DEBOUNCE_MS holds the refill after a save for a short window, so a burst of saves costs one
generation bump and one render of each affected page. A refill that ran between two saves would
reopen the invalidation and make the second save pay for both again. The window starts at the first
save and later saves do not extend it. A visitor who asks for an invalidated page is still served at
once; only the pages nobody has asked for wait.
R2_WRITES_PER_MONTH caps what the file and page mirrors spend together. R2's allocation is
1,000,000 Class A operations a month per account, and every put past it is billed; the default stops
one site at 90% of that. The count is kept in cfw_meta under a key that names the month, so it
resets on the first of each month. Deletes count as well, so the tally reads high. A spent budget
leaves the rest of the queue in place and every file keeps serving from the object, which holds the
bytes either way. /serve-stats reports the month's tally as r2. Set it to 0 to stop both
mirrors while leaving FILES bound. On an account running several sites, divide the allocation
between them.
The share of traffic worth serving off the Worker depends on the traffic mix and on CDN absorption.
optimalOffWorker() in scripts/measure/free-envelope.ts computes it from the same model the free-plan
envelope uses.
Files and Images
| var | default | what it does |
|---|---|---|
FILES_PUBLIC_URL |
unset | origin a mirrored public file is linked from; unset uses the Worker |
IMAGE_ENGINE |
tinyimg |
which engine produces derivatives; images for Cloudflare Images |
EAGER_DERIVATIVES |
on | render an upload's image styles on the rendering lanes; 0 off |
Image Styles
A public image style's URL points at the Worker's delivery path, /cfw-img/<id>/<uri>?<transform>,
where the front worker renders the derivative with tinyimg. The drupflare module rewrites the URL
from the style's effect chain: scale, scale-and-crop, resize and convert each map to one transform.
A chain that cannot be expressed as one transform, and any private file, keeps Drupal's stock URL.
With RENDER_LANES bound, an image written to public:// is queued, and the next alarm renders
every configured style at once on the rendering lanes, Durable Objects that hold no Drupal state.
The results are stored under public://cfw-derivatives/, and the image route answers a stored copy
in place of the source on the request it already makes, marked x-cfw-image: STORED. In measurement on
a deployed worker with the four shipped styles (n=5), the render job took 984-1,718 ms on four lanes
with five or six Durable Object requests. A stored first view answered in 41-91 ms, and the same style
rendered on first view took up to 1,647 ms. Stored and rendered bytes were identical in all 20
comparisons. Through the media form, an upload answered 303 in 750-939 ms and all four styles were
stored 3.9-4.4 s after the POST. EAGER_DERIVATIVES=0 returns to rendering on first view.
FILES_PUBLIC_URL
Unset, every file is served through the Worker, which is correct and costs one Worker request per
file. The front worker answers /sites/default/files/<path> from the object's file store and caches
it for 300 seconds. Drupal has no route for a public file, so until this route existed every public
original answered Drupal's 404 on a site with no mirror. PNG, JPEG, GIF, WebP and AVIF render inline;
every other type, SVG and PDF included, is sent as a download under a sandboxing
Content-Security-Policy, because the file shares the site's origin and its session cookie. Set to
an R2 custom domain, a public file that has already mirrored to the FILES bucket is
linked at that origin instead and costs no Worker request at all.
That is one of only two paths on the platform that cost nothing. A zone Cache Rule is not one of them: the Worker runs before the cache is consulted, so a cache hit still spends an invocation. The two are a static asset and a hostname that is not routed to the Worker.
Two files never get the external URL. A private:// file is never linked externally at any setting,
because the bucket has no session and the URL would bypass Drupal's access check. A public file that
has not mirrored yet keeps the Worker URL, because the alternative is a 404 on a file the site holds.
IMAGE_ENGINE
tinyimg is the default and runs in the front worker, where it never meets PHP's heap. It needs no
zone, no binding and no subrequest, and it has no monthly cap.
Cloudflare Images is reachable with IMAGE_ENGINE=images. It is the only one of the two that encodes
AVIF, and it costs a zone with transformations enabled, does not exist on *.workers.dev, and caps at
5,000 transformations a month. Against the four shipped image styles that is 1,250 images before a
site stops generating derivatives.
The toolkit reports the engine's real format list. All four
shipped styles are image_scale plus image_convert_avif, and core's AvifImageEffect asks the
toolkit whether AVIF is supported before using it, falling through to the style's configured fallback
of webp when the answer is no. So core degrades on its own with no config change, provided the
toolkit tells the truth. Claiming avif on the tinyimg engine produces a logged failure instead of a
fallback.
Measured on a deployed free worker, cpuTime amortised over 10 transforms per invocation, median of
12, against a source-only control at 0 ms: thumbnail 36.3 ms, medium 48.6, large 63.5, wide 188.2.
Styles at or below 480 px on the long edge are produced inline; larger ones go to the fill queue.
gd is not an engine. Code that calls PHP's imagecreate* functions directly finds them absent on this
runtime.