Skip to content
LogoLogo

Transform parameters

imgx uses a /image/<OPTIONS>/<SOURCE-IMAGE> URL convention: a fixed image/ prefix, then an OPTIONS segment, then the source image path. imgx treats the segment immediately after the prefix as the options segment when it contains =. Separate multiple parameters with commas.

GET /image/<options>/<image-path>
GET /image/w=400,h=300,f=webp,q=85/photos/hero.jpg

The source-image segment is normally a relative path, resolved against IMGX_ORIGIN_BASE_URL. It may instead be an absolute http:///https:// URL, fetched directly instead of from the configured origin — but only when the operator has set IMGX_ALLOW_REMOTE_SOURCES=true (default false); otherwise such a request returns 403 Forbidden before any network call. See IMGX_ALLOW_REMOTE_SOURCES in Configuration for the security guards this enables and the risk it accepts.

GET /image/w=400/https://example.com/photos/hero.jpg

Dimensions

w / width

Output width in pixels, resized according to the current fit mode.

  • Range: 1–8192
  • Default: original width
/image/w=400/photo.jpg
/image/width=400/photo.jpg

h / height

Output height in pixels.

  • Range: 1–8192
  • Default: original height
/image/h=300/photo.jpg
/image/w=400,h=300/photo.jpg

When you specify only one dimension, imgx derives the other from the source aspect ratio. The ratio uses the image as displayed. A JPEG with EXIF orientation 5 to 8 turns by 90 degrees, so w=300 on a stored 2000x1500 image gives 300x400.

dpr

Device pixel ratio. Multiplies w and h for retina and HiDPI displays. The effective dimensions are clamped to 8192.

  • Range: 1.0–5.0
  • Default: 1.0
# 400px at 2x = 800px actual output
/image/w=400,dpr=2/photo.jpg

Quality

q / quality

Compression quality for lossy formats (JPEG, WebP, AVIF). Higher values produce larger files with fewer artifacts. Has no effect on PNG.

  • Range: 1–100, or a perceptual quality string: high, medium-high, medium-low, low
  • Default: 80
/image/q=90/photo.jpg
/image/w=400,q=60/photo.jpg
/image/quality=high/photo.jpg

scq / slow-connection-quality

Overrides quality when the request's client-hint headers (rtt, save-data, ect, downlink) indicate a slow connection: rtt over 150ms, save-data: on, ect in slow-2g/2g/3g, or downlink under 5Mbps.

  • Range: same as quality — 1–100, or a perceptual quality string
  • Default: unset (no override)
/image/q=80,scq=40/photo.jpg

compression

Prioritizes encoding speed over output quality/size. Accepts fast, which biases format selection away from AVIF/WebP (the slowest encoders) toward JPEG — this applies even when an explicit format=avif/format=webp is requested.

  • Values: fast
  • Default: unset
/image/compression=fast/photo.jpg

Format

f / fmt / format

Output format. When set to auto or omitted, imgx negotiates the format from the client's Accept header.

  • Values: jpeg, jpg, png, webp, avif, gif, baseline-jpeg, json, thumbhash, auto
  • Default: auto
/image/f=webp/photo.jpg
/image/format=avif/photo.jpg
/image/fmt=png/photo.jpg
/image/f=gif/animation.gif
/image/f=baseline-jpeg/photo.jpg
/image/w=200,format=json/photo.jpg
/image/w=800,h=450,fit=cover,format=thumbhash/photo.jpg

baseline-jpeg produces non-progressive (baseline sequential) JPEG — the same encode path as jpeg today (libvips defaults to baseline unless progressive encoding is explicitly requested).

format=json returns a metadata-only JSON response instead of image bytes:

{
  "original": { "width": 2000, "height": 1500, "file_size": 123456 },
  "transformed": { "width": 200, "height": 150, "format": "webp", "file_size": 4821 }
}

transformed.format/transformed.file_size are computed by actually negotiating and encoding a real codec — real numbers, not placeholders. See docs/CLOUDFLARE_PARITY.md gap 5 for the schema's provenance (spec-derived — Cloudflare doesn't publish an exact field-by-field schema).

format=thumbhash

format=thumbhash returns a short text hash of the transformed image. A client turns the hash into a small blurred placeholder while the full image loads. The body is standard base64 (RFC 4648, with = padding). It has 24 to 36 ASCII characters. The response has Content-Type: text/plain; charset=utf-8.

/image/format=thumbhash,w=800,h=450,fit=cover/photos/cat.jpg

Decode the hash in the browser with the MIT-licensed thumbhash package:

import { thumbHashToDataURL } from "thumbhash";
 
const text = await (await fetch("/image/format=thumbhash,w=800/photos/cat.jpg")).text();
const hash = Uint8Array.from(atob(text), (c) => c.charCodeAt(0));
img.src = thumbHashToDataURL(hash);

imgx runs every other option before it computes the hash. These options change the hash: w, h, fit, gravity, dpr, rotate, flip, trim, border, bg, the effects, and frame. An animated source gives the hash of frame 0, or of the frame that frame=N selects.

These options have no effect on the body: q, metadata, compression, anim, onerror, and the Accept header. The first four still change the cache key, so a different q stores a separate copy of the same hash. onerror and Accept never enter the cache key.

  • Size of the hash: 17 to 25 bytes. The hash keeps the aspect ratio of the output image, but not its exact size. imgx hashes a thumbnail of at most 100 by 100 pixels, so a near-square output such as 4000 by 3999 hashes as a square.
  • draw: a request that combines format=thumbhash with draw overlays returns 422. The error detail says that draw overlays cannot be combined with format=thumbhash. The check runs before the draw permission check and before imgx fetches any overlay.
  • Errors: a failed request returns the JSON error envelope. A source over IMGX_TRANSFORM_MAX_PIXELS, or a source that libvips cannot process, returns 422. A source that is cut short or partly corrupt also returns 422. The strict decode applies only to format=thumbhash. Other formats still encode the part of such a source that libvips decodes. The strict decode does not catch every damaged source. For example, libvips 8.15.1 still decodes a PNG that misses only its last bytes. An internal failure returns 500. imgx never returns the raw origin bytes for this format. It ignores onerror=redirect, and it never caches a failure.
  • Vary: the response still carries Vary: Accept, although Accept does not change the body. A CDN that honours Vary can store more than one copy of the hash. Set the CDN to ignore Accept for this format to avoid that.
  • Byte parity: the hash equals the output of thumbhash@0.1.1 for normal images. Flat images, exact gradients, and symmetric images are degenerate cases. Other implementations already disagree on them, so imgx makes no promise for them. A small size alone does not make an image degenerate.

Fit mode

fit

Controls how the image fits the target dimensions.

ValueAliasesBehavior
containscale-downScale down to fit within the dimensions. Preserves aspect ratio. Never upscales. (default)
cover—Scale and crop to fill the exact dimensions. Uses gravity to pick the crop anchor. Upscales if needed.
fillsqueezeStretch to exactly fill the dimensions. Ignores aspect ratio.
inside—Same as contain.
outsidescale-upScale up to cover the dimensions. Preserves aspect ratio. Never downscales.
pad—Resize like contain, then letterbox onto the target canvas using the background color.
crop—Fill the target area like cover, but never upscales — behaves like contain when the source is smaller than the target.
aspect-crop—Crop to the target aspect ratio. Downscales (never upscales) when the source is large enough to cover the target; otherwise crops the original-size image directly to the target ratio.
/image/w=400,h=400,fit=cover/photo.jpg
/image/w=400,h=300,fit=fill/photo.jpg
/image/w=400,h=400,fit=crop/photo.jpg
/image/w=400,h=200,fit=aspect-crop/photo.jpg

With fit=pad, set both w and h to force a fixed output canvas. The padding color comes from bg/background and defaults to white. For animated output, fit=pad currently behaves like contain.

scale-down, squeeze, and scale-up are Cloudflare Images' names for pixel-dimension-equivalent behavior imgx already had under contain, fill, and outside respectively — see docs/CLOUDFLARE_PARITY.md gap 3 for the equivalence proof. imgx's own default (contain) is unchanged.

Gravity

g / gravity

When fit=cover crops the image, this controls which region to keep.

ValueAliasesBehavior
centercentreCrop from center (default)
northn, topKeep the top edge
souths, bottomKeep the bottom edge
easte, rightKeep the right edge
westw, leftKeep the left edge
northeastneKeep the top-right corner
northwestnwKeep the top-left corner
southeastseKeep the bottom-right corner
southwestswKeep the bottom-left corner
smartautoEntropy-based detection (keeps high-detail areas)
attentionattAttention-based detection (keeps visually interesting areas)
/image/w=400,h=400,fit=cover,g=smart/photo.jpg
/image/w=400,h=400,fit=cover,g=north/photo.jpg
/image/w=400,h=400,fit=cover,g=auto/photo.jpg
/image/w=400,h=400,fit=cover,g=top/photo.jpg

top/bottom/left/right and auto are Cloudflare Images' own gravity vocabulary — aliased onto imgx's existing compass words and smart respectively (same meaning, verified against Cloudflare's docs). Cloudflare's face (face-detection-based gravity) and XxY relative-coordinate focal points are not implemented — see docs/CLOUDFLARE_PARITY.md gap 12.

Effects

sharpen

Unsharp mask with the given sigma value.

  • Range: 0.0–10.0
/image/sharpen=1.5/photo.jpg
/image/w=400,sharpen=2.0/photo.jpg

blur

Gaussian blur with the given sigma value.

  • Range: 0.1–250.0
/image/blur=3.0/photo.jpg
/image/w=400,blur=10.0/photo.jpg

brightness

  • Range: 0.0–2.0 (1.0 is normal)
/image/brightness=1.2/photo.jpg

contrast

  • Range: 0.0–2.0 (1.0 is normal)
/image/contrast=1.3/photo.jpg

saturation

  • Range: 0.0–2.0 (1.0 is normal)
/image/saturation=0.0/photo.jpg

gamma

Gamma correction.

  • Range: 0.1–10.0
/image/gamma=2.2/photo.jpg

rotate

  • Values: 0, 90, 180, 270
/image/rotate=90/photo.jpg

flip

  • Values: h (horizontal), v (vertical), hv (both)
/image/flip=h/photo.jpg

bg / background

Hex RGB color used for fit=pad canvas fill and alpha flattening before encoding. Useful when converting transparent PNGs to JPEG.

  • Format: 6-character hex, no # prefix
/image/f=jpeg,bg=ffffff/photo.png

border / border.color / border.top / border.right / border.bottom / border.left

Draws a solid-color border around the image, applied after resizing. Border widths scale with dpr.

  • border: uniform width in pixels on all four sides
  • border.color: 6-character hex, no # prefix (default: 000000)
  • border.top/.right/.bottom/.left: per-side width, overriding border for that side
/image/border=10/photo.jpg
/image/border=4,border.color=ff0000/photo.jpg
/image/border.top=2,border.left=8/photo.jpg

trim

Detect and crop uniform borders around the image (border-color-aware).

  • Range: 1.0–100.0 (threshold)
/image/trim=10/photo.jpg

trim.top / trim.right / trim.bottom / trim.left

Crop a fixed amount from one or more edges independently — NOT border-color aware (unlike trim above), and may be combined with each other or with trim.

  • Values: an integer pixel count, or a decimal 0.0–1.0 fraction of that side's dimension
/image/trim.top=100,trim.left=50/photo.jpg
/image/trim.left=0.1,trim.right=0.1/photo.jpg

See docs/CLOUDFLARE_PARITY.md gap 9 for what's implemented (per-side keys) versus not yet (the combined top;right;bottom;left syntax, trim.height/trim.width).

metadata

Controls EXIF and ICC metadata in the output.

  • Values: strip (default), keep, copyright
/image/metadata=keep/photo.jpg

Animation

anim

Controls whether animated images (GIF, animated WebP) preserve animation in the output.

ValueAliasesBehavior
autotruePreserve animation when the input is animated and the output format supports it. (default)
staticfalseStrip animation. Serve the first frame only.
animate—Request animated output. Degrade to static if the format does not support animation.
# Serve an animated GIF, resized
/image/w=64/spinner.gif
 
# Strip animation (Cloudflare-compatible syntax)
/image/anim=false/spinner.gif
 
# Strip animation (imgx syntax)
/image/anim=static/spinner.gif

When anim=auto and no explicit format is set, imgx negotiates an animated-capable format from the Accept header: WebP > GIF. If the client accepts neither, the output degrades to a static first frame in the best available format.

frame

Extract a specific 0-indexed frame from an animated image and serve it as a static image.

  • Range: 0–999
  • Default: none (all frames preserved)
/image/frame=0,f=png/spinner.gif
/image/frame=2/spinner.gif

If the index exceeds the number of frames, imgx returns the last frame.

Animation safety limits

LimitDefaultEnvironment variable
Max frames100IMGX_TRANSFORM_MAX_FRAMES
Max total pixels (width x height x frames)50,000,000IMGX_TRANSFORM_MAX_ANIMATED_PIXELS

When the pixel budget is exceeded, the output falls back to a static first frame. When the frame count exceeds max_frames, only that many frames load.


Content negotiation

When the output format is auto (the default), imgx picks the best format from the client's Accept header.

Static images: AVIF > WebP > JPEG > PNG

Animated images: WebP > GIF

The negotiation follows these rules:

  1. Explicit format wins. If you set f=webp, imgx always outputs WebP regardless of Accept.
  2. Animation preservation. For animated sources, imgx prefers animated-capable formats (WebP, GIF).
  3. Alpha channel handling. When the source has transparency, JPEG is deprioritized because it cannot represent alpha.
  4. Client support. Only formats the client advertises in Accept are considered.
  5. Fallback. If no acceptable format matches, JPEG is the universal fallback.

The response includes a Vary: Accept header so CDNs cache different format variants correctly.


Caching behavior

imgx caches transform results under a deterministic key. The key holds the image path, the transform parameters, and a format segment.

Key format: <image-path>|<transform-string>|<format>

Example: photos/hero.jpg|w=400,h=300|webp

With an explicit format, the format segment is that format's name. With auto, the segment is auto: followed by the formats that the Accept header supports, joined by +, in the order avif, webp, jpeg, png, gif. For example, Accept: image/jpeg gives auto:jpeg, and Accept: */* gives auto:avif+webp+jpeg+png+gif. Two Accept headers that support the same formats share one cache entry. Two headers that support different formats never share one. A client therefore always gets a format that it accepts.

Example: photos/hero.jpg|w=400,h=300|auto:avif+webp

Parameter order does not matter. w=400,h=300 and h=300,w=400 produce the same cache key.


Error responses

Invalid transforms return structured JSON errors:

ConditionStatusExample
Unknown parameter400banana=42
Empty value400w=
Invalid format400f=bmp
Out of range422w=0, w=9000, q=101
{"error":{"status":400,"message":"Bad Request","detail":"invalid transform parameters"}}

onerror

Opt-in per-request behavior when the transform pipeline itself fails (a corrupt/unsupported source, an unavailable codec, etc). By default (no onerror), imgx falls back to serving the raw origin bytes as-is. Setting onerror=redirect instead returns a 302 redirect to the origin image URL — only for HTTP origins (not R2, which has no public URL to redirect to).

  • Values: redirect
  • Default: unset (raw-bytes fallback)

With IMGX_TRANSFORM_FAILURE=error imgx returns the JSON error envelope for every failed transform and ignores onerror. See Configuration.

/image/w=400,onerror=redirect/photo.jpg

draw (overlays) — opt-in, off by default

draw.<N>.url, draw.<N>.width/.height, .top/.left/.bottom/.right, .opacity, .repeat, .background, .rotate, .fit, .gravity parse and validate (an array of overlays, indexed by <N> starting at 0). By default (IMGX_ALLOW_DRAW_OVERLAYS=false), every request naming a draw overlay returns 403 Forbidden before any network call. Set IMGX_ALLOW_DRAW_OVERLAYS=true to enable fetching draw[].url overlay images through imgx's SSRF-safe remote fetcher (see IMGX_ALLOW_REMOTE_SOURCES in Configuration — this is a separate flag, so an operator can enable overlays without enabling arbitrary remote main-image sources). See docs/CLOUDFLARE_PARITY.md gap 11 for the full status.

/image/draw.0.url=https://example.com/logo.png,draw.0.opacity=0.5/photo.jpg
→ 403 Forbidden by default; composites the overlay onto the base image when IMGX_ALLOW_DRAW_OVERLAYS=true

Full example

GET /image/w=800,h=600,fit=cover,g=attention,f=auto,q=85,dpr=2,sharpen=0.5/products/shoe-red.png

This request:

  1. Fetches products/shoe-red.png from the origin
  2. Resizes to 1600x1200 effective pixels (800x600 at dpr 2)
  3. Crops to fill using attention-based gravity
  4. Negotiates the output format from the client's Accept header
  5. Encodes at quality 85
  6. Applies a light sharpen (sigma 0.5)
  7. Caches the result for subsequent requests