URL API
Deterministic, human-readable URLs for transformed image delivery.
Every image is served from a deterministic, human-readable URL. Add a transform segment to the path to resize, crop, and re-format on the fly — the edge transforms once and caches forever.
URL structure
https://cdn.auraimage.ai/[projectName]/[transform]/[name][.ext]| Segment | Description | Example |
|---|---|---|
projectName | Your project identifier — same as NEXT_PUBLIC_AURA_PROJECT_NAME. | my-app |
transform | Optional comma-separated transform options — one path segment, right after the project. | w=800,q=75 |
name | The extension-less name returned by upload (or whatever you stored). | abc123xyz0-hero |
.ext | Optional format extension. Omit it for automatic format selection (recommended). | .avif |
https://cdn.auraimage.ai/my-app/w=800,q=75/abc123xyz0-heroThe transform segment is optional — https://cdn.auraimage.ai/my-app/abc123xyz0-hero serves the full-size image with automatic format negotiation.
Transform options
Transform options are a single comma-separated path segment placed immediately after the project name — w=1920,fit=face,q=75. Recognized keys are w, h, fit, and q (plus lqip, below). The grammar is strict: an unknown key, a duplicate key, or an invalid value returns 400 naming the offender — options are never silently ignored.
Sizing
| Option | Type | Default | Notes |
|---|---|---|---|
w | integer | — | Width in pixels. Capped at 4096. Height scales proportionally unless h is also set. |
h | integer | — | Height in pixels. Capped at 4096. |
fit | string | cover | How to fit the image into the requested box. Only meaningful when w or h is set. |
Nearby w / h values may be coalesced onto a shared variant to keep the edge cache economical — standardize on a small set of widths and you'll hit cache more often.
fit values:
| Value | Behavior |
|---|---|
cover | Crop to fill the exact box. |
contain | Letterbox — full image visible, no crop. |
face | Cloudflare-native face detection. Crops the most prominent face into the box. |
auto | Cloudflare-native saliency detection. Crops to the most visually important region. |
When fit=face or fit=auto succeeds, the response carries X-Aura-Smart-Crop: ok. When no face / salient region is detected, the result falls back to a centered cover crop (the header is omitted).
Quality
| Option | Type | Default | Notes |
|---|---|---|---|
q | 1–100 | 80 | Compression quality. Ignored for lossless masters served pass-through. |
Format
Output format is chosen by the URL's trailing extension — there is no fmt option.
| Extension | Behavior |
|---|---|
| (none) | Recommended. Automatic — picks the best format the client accepts (AVIF → WebP → JPEG). The extension-less URL is canonical. |
.avif | Force AVIF. |
.webp | Force WebP. |
.jpg / .jpeg | Force JPEG. |
.png | Force PNG. |
Because the extension-less URL negotiates the best modern format automatically, prefer it for <img> tags. Pin an explicit extension only when the consumer can't send an Accept header — most importantly og:image, where you should use an explicit .jpg or .png.
Requesting a known-but-non-servable extension (.gif, .heic, .tiff, .bmp) returns 400 with guidance: request /{name} for automatic format, or one of .jpg, .png, .webp, .avif.
Loading helpers
| Option | Type | Description |
|---|---|---|
lqip | flag | lqip=true returns a low-quality image preview (small, heavily compressed) for placeholder rendering — e.g. /my-app/lqip=true/abc123xyz0-hero. |
For a BlurHash placeholder, fetch it as JSON from GET /v1/blurhash/<projectName>/<name> — { "blurhash": "..." }. The <AuraImage /> component uses this internally.
Build URLs from code
Rather than concatenating the path yourself, the @auraimage/sdk package ships two helpers that serialize these URLs for you — handy when you set an image src manually instead of using the <AuraImage /> component. Both are pure and browser-safe (they take no secret key and make no network calls), and both build public URLs — for a private image, sign one with getSignedUrl instead.
buildServeUrl takes friendly parameter names and emits the transform segment, format extension, and cache-buster in the correct grammar:
import { buildServeUrl } from '@auraimage/sdk';
buildServeUrl({
cdnUrl: 'https://cdn.auraimage.ai',
project: 'my-app',
name: 'blog/hero', // the extension-less name returned by upload
width: 800,
height: 600,
quality: 75,
format: 'auto', // 'auto' | 'jpeg' | 'png' | 'webp' | 'avif'
v: 3 // optional cache-buster
});
// → https://cdn.auraimage.ai/my-app/w=800,h=600,q=75/blog/hero?v=3Only cdnUrl, project, and name are required. The rest map to the grammar above — width→w, height→h, fit→fit, quality→q, lqip→lqip=true, and format to the trailing extension — and any option you omit is left out of the URL. width/height (positive integers) and quality (1–100) are validated: a bad value throws instead of producing a URL the CDN would reject.
buildBlurhashUrl builds the placeholder-metadata URL from Loading helpers:
import { buildBlurhashUrl } from '@auraimage/sdk';
const url = buildBlurhashUrl({ cdnUrl: 'https://cdn.auraimage.ai', project: 'my-app', name: 'blog/hero' });
// → https://cdn.auraimage.ai/v1/blurhash/my-app/blog/hero
const { blurhash, width, height } = await fetch(url).then((r) => r.json());Content negotiation
When the URL has no extension (automatic format), the edge reads the Accept header and serves in priority order:
AVIF → WebP → JPEGThe response carries Vary: Accept so the cache stores one variant per accept-class — no cross-browser cache poisoning.
Query parameters
Only two query parameters are honored:
| Param | Purpose |
|---|---|
v | Cache-buster. Bump it to force a fresh variant after an overwrite. |
token | Serve token for private images. |
Transform options must live in the path segment. A legacy transform query param (?w=800, ?fmt=auto, ?blur=true, …) returns 400 — a tripwire so a half-migrated URL fails loudly instead of silently serving a full-size original. All other query params (utm_*, fbclid, …) are ignored, so link decoration never breaks an image or fragments the cache.
SEO names
Descriptive names improve image rankings in Google Images. An image's name is its extension-less identity — set a meaningful one at upload time (via the upload token's name claim, or let it be auto-generated from a descriptive uploaded file name):
# Good — descriptive
https://cdn.auraimage.ai/my-app/abc123xyz0-golden-gate-bridge-sunset
# Avoid — opaque
https://cdn.auraimage.ai/my-app/abc123xyz0-img-00432Use hyphens as separators. Names may contain / to group images into path segments (e.g. blog/hero).
Project names are immutable
A project name is permanent. Renaming would break every published URL, so the dashboard does not allow it. Pick carefully during aura init or MCP install.
Reserved names (cannot be used as a project name): api, admin, cdn, health, registry, static, test, v1.
Caching behavior
| Visibility | Cache-Control | Edge cache |
|---|---|---|
| Public | public, max-age=31536000, immutable | Yes — first request transforms, every subsequent request hits the edge. |
Private (?token=) | private, max-age=300 | No — every request bypasses the shared edge cache and hits origin. |
Cache tags are set on every public response so you can purge surgically:
| Tag | Applies to |
|---|---|
project-<projectName> | Every public image in the project. |
fit-face | Public images served with fit=face. |
fit-auto | Public images served with fit=auto. |
Purge a single project from the dashboard without affecting any other project, and re-render smart-cropped variants without invalidating exact-size crops.
Response headers
Every successful image response carries:
| Header | Values | Meaning |
|---|---|---|
X-Aura-Cache | HIT | MISS | Whether the edge cache served this response. |
X-Aura-Smart-Crop | ok | Set only when fit=face / fit=auto produced a crop. Absent on fallback. |
Server-Timing | see below | Per-stage timings, set on both hits and misses. |
Vary | Accept | Required for correct format negotiation caching. |
Server-Timing
A standard Server-Timing header lets you measure delivery in DevTools or RUM without any custom instrumentation.
On a cache hit:
Server-Timing: cache;desc="HIT";dur=<ms>On a cache miss:
Server-Timing: cache;desc="MISS";dur=<ms>, r2;dur=<ms>, process;desc="transformed";dur=<ms>Pass-through misses (no transformation needed) report process;desc="passthrough";dur=0.
| Stage | What it measures |
|---|---|
cache | Edge cache lookup. |
r2 | Origin object fetch from R2. |
process | Transformation (resize, format conversion, smart crop). passthrough when the master is served as-is. |
Open Chrome DevTools → Network → click any image response → Timing tab to see these stages plotted alongside browser timings.