Configuring Cloudflare Image Resizing URL parameters

The /cdn-cgi/image/<options>/<source> URL is deceptively simple: a comma-separated option list, then the path to your master. But each option interacts with the others, and a wrong fit or a missing format=auto quietly ships the wrong bytes. This guide — part of Cloudflare Image Resizing and Polish within CDN & Edge Media Delivery — walks every option in the string, gives one fully annotated worked example, and shows the exact curl that proves the edge did what you asked.

Prerequisite checklist

Anatomy of the URL

The URL has three fixed segments, and getting their boundaries right matters more than any single option. The prefix triggers interception, the middle segment carries the options, and the tail names the master. The options are part of the path, never a query string.

Anatomy of a /cdn-cgi/image/ URL A single URL split into three labelled segments. The first segment /cdn-cgi/image/ is the edge-interception prefix. The middle segment width=640,fit=cover,quality=75,format=auto is the comma-separated options list, each option annotated. The final segment /media/hero.jpg is the origin path of the master image. A caption warns that options must be in the path, not a query string. /cdn-cgi/image/ width=640,fit=cover,quality=75,format=auto /media/hero.jpg interception prefix options (comma-separated) origin master path target width (px) crop mode perceptual quality negotiate AVIF/WebP from Accept Options live in the PATH — ?width=640 as a query string is ignored and the source is served untouched.

The option string, parameter by parameter

Options are a comma-separated list with key=value pairs, placed between /cdn-cgi/image/ and the source path. Order does not matter; unknown keys are ignored.

Sizing: width, height, dpr, fit

  • width (w) — target width in device pixels. This is the primary lever; it is what actually shrinks payloads. Bound it to your breakpoint set.
  • height (h) — target height. With only one of width/height set, the other is derived from the aspect ratio.
  • dpr — device-pixel-ratio multiplier applied on top of width. width=320,dpr=2 produces a 640-pixel image intended to paint into a 320 CSS-pixel slot on a 2× display.
  • fit — how the image is fitted to the width×height box:
    • scale-down — fit within the box, never enlarge. Preserves aspect ratio. Safest default.
    • contain — fit within the box, may enlarge. Preserves aspect ratio.
    • cover — fill the box, cropping overflow. Preserves aspect ratio; pair with gravity.
    • crop — fill and hard-crop to the exact box dimensions.
    • pad — fit within the box and pad the remainder with background.

The five modes differ along only four axes — whether they may enlarge past the master’s intrinsic size, whether they discard pixels, whether they add padding, and whether the output is exactly the box you asked for. Reading them as a matrix is faster than reading five prose definitions, and it makes the two genuinely dangerous cells obvious: contain and cover will upscale a small master into a blurry large one, and only cover/crop respect gravity.

The five fit modes compared A matrix with five columns — scale-down, contain, cover, crop and pad — and five property rows. Each column shows a small glyph of the requested box as a dashed rectangle with the resulting image drawn against it. The rows record whether the mode may enlarge past the master, whether it crops pixels away, whether it adds padding, whether the output is exactly the requested width by height, and whether it honours gravity. scale-down does none of these. contain may enlarge only. cover may enlarge, crops, fills the exact box and honours gravity. crop does not enlarge but crops, fills the exact box and honours gravity. pad may enlarge, adds padding and fills the exact box but ignores gravity. scale-down contain cover crop pad fit= value dashed = requested box May enlarge past the master Crops pixels away Adds padding Output is exactly width×height Honours gravity scale-down is the only mode that can never make an image worse than the master; it is the right default for untrusted uploads.

Format and quality: format, quality

  • format=auto — negotiate AVIF/WebP/JPEG from the request Accept header. This is what makes one URL serve the right codec to every client. Omit it and you get the source format for everyone.
  • quality (q) — perceptual quality 1–100. Around 75 is a strong photographic default; drop to 55–65 for thumbnails, raise to 85+ for detailed hero art.

Cropping and focus: gravity, background

  • gravity — focal point for cover/crop. Accepts auto (saliency detection), a side (left, right, top, bottom), or normalized coordinates like 0.5x0.2 (horizontal×vertical, 0–1).
  • background — fill colour for fit=pad or for flattening transparency when producing a format without an alpha channel. Accepts CSS colours or %23RRGGBB (URL-encoded hex).

Detail and robustness: sharpen, metadata, onerror, anim

  • sharpen — 0–10 unsharp-mask strength. Downscaling softens edges; sharpen=1 recovers crispness on photos, higher values over-sharpen.
  • metadatanone (default, strips EXIF), copyright (keeps only copyright), or keep (retains all EXIF, larger files).
  • onerror — what to do if the master cannot be fetched or resized. redirect serves the original source instead of an error, so a broken transform degrades to the unoptimized image rather than a broken icon.
  • animtrue preserves animation frames (animated WebP/GIF); anim=false collapses an animation to its first frame, useful for generating a static poster.

Quick reference

Option Alias Accepted values Default if omitted Cache-key impact
width w integer device pixels master’s intrinsic width one variant per distinct value
height h integer device pixels derived from aspect ratio one variant per distinct value
dpr 1–3 1 multiplies with width
fit scale-down, contain, cover, crop, pad scale-down new variant per mode
quality q 1–100 85 new variant per value
format f auto, avif, webp, jpeg, png source format auto splits by negotiated codec
gravity g auto, left/right/top/bottom, XxY centre new variant per focal point
background CSS colour or %23RRGGBB transparent / white new variant per colour
sharpen 0–10 0 new variant per value
metadata none, copyright, keep none new variant per value
onerror redirect error response none — affects the failure path only
anim true, false true new variant per value

Tradeoff: every column marked “new variant” is a multiplier on your stored object count and, on paid plans, on your transformation bill. Four widths × two dpr values × two quality values is sixteen objects for one master before format=auto splits each of them again by negotiated codec. The discipline is to fix everything except width per image role — one quality, one fit, one sharpen — and let only the width vary. The cost side of that arithmetic is worked through in Cloudflare Polish vs Image Resizing: tradeoffs.

Worked example — one fully annotated URL

The following URL derives a 640-pixel-wide, cover-cropped, negotiated variant from a portrait master, keeping the subject’s face in frame and degrading gracefully if the master is missing:

<!--
  Break the option string down comma by comma:
    width=640      target 640 device pixels wide (a real breakpoint)
    height=800     target height; with width this defines a 4:5 box
    fit=cover      fill the whole 640x800 box, cropping overflow
    gravity=0.5x0.3 keep the crop centred horizontally, biased UP toward
                   the face (0.3 vertical) rather than the geometric centre
    quality=75     perceptual quality target for photographic content
    format=auto    negotiate AVIF/WebP/JPEG from the request Accept header
    sharpen=1      restore edge crispness lost when downscaling the master
    metadata=none  strip EXIF to shave bytes and remove location data
    onerror=redirect if the master 404s or fails, serve the original source
                   instead of a broken image
  The trailing /media/portraits/anaïs-master.jpg is the ORIGIN path of the
  master. Note the source is URL-relative to the same zone.
-->
<img
  src="/cdn-cgi/image/width=640,height=800,fit=cover,gravity=0.5x0.3,quality=75,format=auto,sharpen=1,metadata=none,onerror=redirect/media/portraits/anais-master.jpg"
  width="640" height="800"
  alt="Studio portrait, cropped to the subject's face"
  loading="lazy" decoding="async">

Warning: the option string is before the source path, not a query string. A frequent mistake is writing /media/img.jpg?width=640 — Cloudflare Image Resizing ignores query-string options entirely and serves the untouched source. The options must sit in the path segment right after /cdn-cgi/image/.

Warning: if you pass background as a hex colour, URL-encode the # as %23 (for example background=%23ffffff). A literal # starts the URL fragment and the parameter is silently dropped.

How the edge resolves that URL

Knowing the order of operations explains most surprises. The /cdn-cgi/image/ prefix is claimed by the edge before any origin routing runs, so your origin never sees that path and never needs a rewrite rule for it. The edge parses the option string, derives a variant cache key from the option string plus the source path plus the format it negotiated from Accept, and only on a miss does it issue an internal subrequest for the master. That subrequest is an ordinary HTTP request to your origin for the tail path, which is why the master must be publicly reachable and why a 404 there surfaces as an onerror outcome rather than a transform.

Sequence of one Image Resizing request A sequence diagram with three lifelines: browser, Cloudflare edge POP, and origin. The browser sends a GET for the slash cdn-cgi slash image URL. The edge builds a variant key from the option string, the source path and the negotiated format, and misses. The edge then issues an internal subrequest to origin for the master, which returns a two-thousand by one-thousand-three-hundred JPEG of about 1.9 megabytes. The edge resizes to 640 by 800, sharpens and encodes AVIF at quality 75, then returns a 41 kilobyte AVIF with cf-resized set and cf-cache-status MISS. A footnote states that later requests for the same option string hit the variant cache without any origin subrequest. Browser Cloudflare edge POP Origin GET /cdn-cgi/image/width=640,height=800, fit=cover,format=auto/media/hero.jpg variant key = option string + source path + format negotiated from Accept → MISS subrequest: GET /media/hero.jpg (master) 200 · image/jpeg · 2000×2500 · 1.9 MB decode · fit=cover crop to 640×800 sharpen=1 · encode AVIF q=75 · store variant 200 · image/avif · 41 KB cf-resized: internal=ok · cf-cache-status: MISS Later requests with the same option string are answered from the variant cache: HIT, no origin subrequest, no new transform billed.

Two consequences follow directly from that ordering. Because the key includes the negotiated format, one option string still stores several objects — an AVIF for Chrome, a WebP for older Safari, a JPEG for everything else — which is the same three-way split described for Fastly VCL for image format negotiation, just computed for you instead of hand-written. And because the subrequest is a normal origin fetch, the master’s own Cache-Control governs how often the edge re-reads it; a master served with no-store forces a re-fetch on every cold transform.

Verification

Confirm the negotiated format and that Resizing ran

# Simulate a Chrome request (advertises AVIF + WebP). A correct transform
# returns content-type: image/avif and a cf-resized header. The cf-resized
# header appears ONLY when Image Resizing processed the request — its
# absence means the option string was ignored (feature off, or query-string
# mistake) and the source was served untouched.
curl -sI -H 'Accept: image/avif,image/webp,*/*;q=0.8' \
  'https://yoursite.com/cdn-cgi/image/width=640,height=800,fit=cover,quality=75,format=auto/media/portraits/anais-master.jpg' \
  | grep -iE 'content-type|cf-resized|cf-cache-status'
# Expected:
#   content-type: image/avif
#   cf-resized: internal=ok/…
#   cf-cache-status: HIT

Confirm the fallback tier for Safari 14

# Safari 14 advertises WebP but not AVIF. The same URL must return WebP here,
# proving format=auto negotiated correctly rather than forcing one codec.
curl -sI -H 'Accept: image/webp,image/*,*/*;q=0.8' \
  'https://yoursite.com/cdn-cgi/image/width=640,height=800,fit=cover,quality=75,format=auto/media/portraits/anais-master.jpg' \
  | grep -i 'content-type'
# Expected: content-type: image/webp

Confirm the actual delivered dimensions

# Download the transformed bytes and read the intrinsic size, proving the
# resize happened (not just a re-encode at native resolution). identify ships
# with ImageMagick; use `file` if you only need the format.
curl -s -H 'Accept: image/avif' \
  'https://yoursite.com/cdn-cgi/image/width=640,height=800,fit=cover,format=auto/media/portraits/anais-master.jpg' \
  -o /tmp/variant.avif
identify -format '%wx%h %m\n' /tmp/variant.avif
# Expected: 640x800 AVIF

Common mistakes

1. Options placed as a query string

Symptom: the full-size source is served; cf-resized is absent. Cause: options written as ?width=640 instead of in the path. Fix: move them into the path: /cdn-cgi/image/width=640,format=auto/media/img.jpg.

2. format=auto omitted

Symptom: Chrome receives JPEG even though AVIF was expected. Cause: without format=auto, Resizing keeps the source format. Fix: always include format=auto unless you deliberately want a fixed codec.

3. Continuous widths from client JavaScript

Symptom: cache hit rate near zero; transform bill climbs. Cause: passing window.innerWidth (751, 752, 753…) mints a new variant per pixel. Fix: snap to a fixed breakpoint set before building the URL — the clamp pattern shown in Cloudflare Image Resizing and Polish.

4. fit=cover with no gravity on portraits

Symptom: faces cropped off the top of tall images. Cause: cover defaults to a centre crop. Fix: set gravity to auto or explicit coordinates biased toward the subject.

5. Upscaling with fit=contain

Symptom: small masters look soft and blocky at large width. Cause: contain enlarges past the master’s intrinsic size. Fix: use fit=scale-down so the edge never upscales beyond the source resolution.