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
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
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.
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
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
:::info
Perceptual quality strings map to high=90, medium-high=80, medium-low=60, low=40 — a spec-derived mapping (Cloudflare documents the strings but not exact integer values). See docs/CLOUDFLARE_PARITY.md gap 4.
:::
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
:::info
Because the effective quality changes based on the requesting client's headers, imgx caches the two variants separately — a request with the override applied and one without are different cache entries, even for the same URL. See docs/CLOUDFLARE_PARITY.md gap 8.
:::
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
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,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
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).
Controls how the image fits the target dimensions.
| Value | Aliases | Behavior |
|---|---|---|
contain |
scale-down |
Scale 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. |
fill |
squeeze |
Stretch to exactly fill the dimensions. Ignores aspect ratio. |
inside |
— | Same as contain. |
outside |
scale-up |
Scale 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.
When fit=cover crops the image, this controls which region to keep.
| Value | Aliases | Behavior |
|---|---|---|
center |
centre |
Crop from center (default) |
north |
n, top |
Keep the top edge |
south |
s, bottom |
Keep the bottom edge |
east |
e, right |
Keep the right edge |
west |
w, left |
Keep the left edge |
northeast |
ne |
Keep the top-right corner |
northwest |
nw |
Keep the top-left corner |
southeast |
se |
Keep the bottom-right corner |
southwest |
sw |
Keep the bottom-left corner |
smart |
auto |
Entropy-based detection (keeps high-detail areas) |
attention |
att |
Attention-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.
:::info
For content-aware cropping, use smart/auto or attention. Directional values (north, south, and others) fall back to center cropping.
:::
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
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
- Range: 0.0–2.0 (1.0 is normal)
/image/brightness=1.2/photo.jpg
- Range: 0.0–2.0 (1.0 is normal)
/image/contrast=1.3/photo.jpg
- Range: 0.0–2.0 (1.0 is normal)
/image/saturation=0.0/photo.jpg
Gamma correction.
- Range: 0.1–10.0
/image/gamma=2.2/photo.jpg
- Values: 0, 90, 180, 270
/image/rotate=90/photo.jpg
- Values:
h(horizontal),v(vertical),hv(both)
/image/flip=h/photo.jpg
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
Draws a solid-color border around the image, applied after resizing. Border widths scale with dpr.
border: uniform width in pixels on all four sidesborder.color: 6-character hex, no#prefix (default:000000)border.top/.right/.bottom/.left: per-side width, overridingborderfor that side
/image/border=10/photo.jpg
/image/border=4,border.color=ff0000/photo.jpg
/image/border.top=2,border.left=8/photo.jpg
:::info
Cloudflare's border is available only through its Workers API, with no published URL-interface syntax — the key names above are spec-derived to match imgx's existing dotted-key conventions. See docs/CLOUDFLARE_PARITY.md gap 10.
:::
Detect and crop uniform borders around the image (border-color-aware).
- Range: 1.0–100.0 (threshold)
/image/trim=10/photo.jpg
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.0fraction 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).
Controls EXIF and ICC metadata in the output.
- Values:
strip(default),keep,copyright
/image/metadata=keep/photo.jpg
Controls whether animated images (GIF, animated WebP) preserve animation in the output.
| Value | Aliases | Behavior |
|---|---|---|
auto |
true |
Preserve animation when the input is animated and the output format supports it. (default) |
static |
false |
Strip 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.gifWhen 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.
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.
| Limit | Default | Environment variable |
|---|---|---|
| Max frames | 100 | IMGX_TRANSFORM_MAX_FRAMES |
| Max total pixels (width x height x frames) | 50,000,000 | IMGX_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.
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:
- Explicit format wins. If you set
f=webp, imgx always outputs WebP regardless ofAccept. - Animation preservation. For animated sources, imgx prefers animated-capable formats (WebP, GIF).
- Alpha channel handling. When the source has transparency, JPEG is deprioritized because it cannot represent alpha.
- Client support. Only formats the client advertises in
Acceptare considered. - 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.
imgx caches transform results using a deterministic key built from the image path, transform parameters, and resolved output format.
Key format: <image-path>|<transform-string>|<format>
Example: photos/hero.jpg|w=400,h=300|webp
Parameter order does not matter. w=400,h=300 and h=300,w=400 produce the same cache key.
Invalid transforms return structured JSON errors:
| Condition | Status | Example |
|---|---|---|
| Unknown parameter | 400 | banana=42 |
| Empty value | 400 | w= |
| Invalid format | 400 | f=bmp |
| Out of range | 422 | w=0, w=9000, q=101 |
{"error":{"status":400,"message":"Bad Request","detail":"invalid transform parameters"}}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)
/image/w=400,onerror=redirect/photo.jpg
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
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:
- Fetches
products/shoe-red.pngfrom the origin - Resizes to 1600x1200 effective pixels (800x600 at dpr 2)
- Crops to fill using attention-based gravity
- Negotiates the output format from the client's
Acceptheader - Encodes at quality 85
- Applies a light sharpen (sigma 0.5)
- Caches the result for subsequent requests