CDN and Serving
Every uploaded image gets a permanent CDN URL. Images are served from Cloudflare's edge network with on-demand transformations and aggressive caching.
URL Formats
Two URL formats are supported. Both work identically.
Full Path Format
https://cdn.example.com/{owner}/{album}/{filename}
https://cdn.example.com/{owner}/{album}/{filename}/{variant}Example:
https://cdn.example.com/alice/vacation/sunset.jpg
https://cdn.example.com/alice/vacation/sunset.jpg/w1024Short ID Format
https://cdn.example.com/i/{id}
https://cdn.example.com/i/{id}/{variant}Example:
https://cdn.example.com/i/abc123
https://cdn.example.com/i/abc123/w1024Short IDs are 6 characters. The /i/ prefix distinguishes them from album paths.
Custom Domains
If you have a custom domain configured, URLs are simplified:
https://cdn.yourdomain.com/{album}/{filename}
https://cdn.yourdomain.com/{id}No owner prefix needed since the domain is tied to your account. See Custom Domains for setup.
Variants
Variants are pre-defined transformations. Request them by appending to the URL path.
Preset Variants
| Variant | Description |
|---|---|
original | No transformation (default) |
w128, w256, w512, w1024, w1536, w2048 | Width-constrained, height scales proportionally |
thumb | 128x128 square thumbnail |
og-image | 1200x630 social media card |
Example:
<img src="https://cdn.example.com/i/abc123/w1024" alt="Photo" />Preset variants are generated on first request and cached permanently. No rate limiting.
Config: packages/config/src/image-variants.ts
Custom Transformations
Beyond presets, you can apply custom transformations via query parameters.
Basic Usage
<img src="https://cdn.example.com/i/abc123?w=400&h=300&fit=cover&q=85" alt="Custom" />Available Parameters
Dimensions:
| Param | Description | Range |
|---|---|---|
w | Width in pixels | 1-9999 |
h | Height in pixels | 1-9999 |
fit | Resize mode | scale-down, contain, cover, crop, pad |
Quality and Format:
| Param | Description | Values |
|---|---|---|
q | Quality | 1-100 (default 85) |
f | Output format | auto, avif, webp, jpeg, png |
Positioning:
| Param | Description | Values |
|---|---|---|
g | Gravity/focus point | auto, center, face, left, right, top, bottom |
Effects:
| Param | Description | Range |
|---|---|---|
blur | Blur radius | 1-250 |
sharpen | Sharpen amount | 1-10 |
br | Brightness | 0.5-2.0 |
co | Contrast | 0.5-2.0 |
sat | Saturation | 0-2.0 |
gam | Gamma | 0.5-2.0 |
Transformations:
| Param | Description | Values |
|---|---|---|
r | Rotation | 0, 90, 180, 270 |
flip | Flip | h, v, hv |
bg | Background color | 6-digit hex without # |
Examples
Square thumbnail with blur:
<img src="https://cdn.example.com/i/abc123?w=200&h=200&fit=cover&blur=10" />High quality WebP:
<img src="https://cdn.example.com/i/abc123?w=1200&f=webp&q=95" />Enhanced portrait:
<img src="https://cdn.example.com/i/abc123?w=800&sharpen=5&sat=1.2&br=1.1" />Rate Limiting
Custom transformations are rate-limited to prevent abuse:
- 10 new custom variants per minute per IP
- Max 100 custom variants per image total
- Once cached, unlimited access to that exact variant
Config: packages/config/src/custom-transform-params.ts
Embed Codes
The UI generates embed codes in many formats. Go to any image and click "Embed" to get copy-paste snippets.
Supported Formats
| Format | Use Case |
|---|---|
| Direct URL | Anywhere |
| Markdown | GitHub, Reddit, docs |
HTML <img> | Basic embedding |
HTML <picture> | Responsive with srcset |
HTML <figure> | With caption |
| BBCode | Forums |
| JSX/React | React apps |
Next.js <Image> | Next.js apps |
| Hugo, Jekyll | Static site generators |
| AMP | AMP pages |
| LaTeX | Documents |
| Confluence, MediaWiki | Wikis |
Responsive Images
For responsive layouts, use srcset:
<img
src="https://cdn.example.com/i/abc123/w800"
srcset="
https://cdn.example.com/i/abc123/w512 512w,
https://cdn.example.com/i/abc123/w800 800w,
https://cdn.example.com/i/abc123/w1600 1600w
"
sizes="(max-width: 768px) 100vw, 800px"
alt="Responsive image"
loading="lazy"
/>Or use <picture> for format negotiation:
<picture>
<source type="image/avif" srcset="https://cdn.example.com/i/abc123?f=avif&w=800" />
<source type="image/webp" srcset="https://cdn.example.com/i/abc123?f=webp&w=800" />
<img src="https://cdn.example.com/i/abc123/w800" alt="Image with format fallback" />
</picture>Custom Domain in Embeds
You can configure whether embed codes use your custom domain or the default CDN host. Toggle this in Settings > Embed Preferences.
Frontend: packages/frontend/src/lib/components/image-details/EmbedCodesSection.svelte
Caching
All images are cached aggressively at the edge.
| Content | Cache-Control |
|---|---|
| Original images | public, max-age=31536000, immutable (1 year) |
| Generated variants | public, max-age=31536000, immutable (1 year) |
| Encrypted images | private, max-age=300 (5 minutes) |
| 404 responses | public, max-age=300 (5 minutes) |
Variants are generated on first request, stored in R2, then served from cache on subsequent requests.
Config: packages/config/src/headers.ts
CORS
The CDN allows cross-origin requests from any domain:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400This means you can embed images on any website and fetch them via JavaScript without CORS errors.
Private Images
Images marked as private require authentication. The CDN checks for a valid session before serving.
For programmatic access to private images, use an API token with read scope:
curl -H "Authorization: Bearer your-token" \
https://cdn.example.com/i/abc123Encrypted Images
End-to-end encrypted images require an access token in the URL:
https://cdn.example.com/i/abc123?token=eyJ...The token contains the decryption key. Without it, the server returns encrypted bytes that are useless without the key.
Encrypted images are cached with private cache control to prevent CDN from serving decrypted content to unauthorized users.
See Encryption for details.
SVG Handling
SVG images are served as-is without transformation. Variants are ignored since SVGs are vector graphics that scale natively.
Analytics
Every image serve is logged with:
- Country (from CF-IPCountry header)
- Bytes transferred
- Variant requested
- Timestamp
View stats in the dashboard or via API. See Analytics.
File Reference
| Purpose | File |
|---|---|
| CDN routes | packages/api/src/routes/cdn.ts |
| Core serving logic | packages/api/src/routes/cdn/core-serving.ts |
| Custom transforms | packages/api/src/routes/cdn/custom-transforms.ts |
| Variant config | packages/config/src/image-variants.ts |
| Transform params | packages/config/src/custom-transform-params.ts |
| Cache headers | packages/config/src/headers.ts |
| Embed code utils | packages/frontend/src/lib/utils/embedCodes.ts |