Initial commit
This commit is contained in:
359
references/format-optimization.md
Normal file
359
references/format-optimization.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# Format Optimization
|
||||
|
||||
Complete guide to automatic WebP/AVIF conversion and format selection.
|
||||
|
||||
---
|
||||
|
||||
## format=auto (Recommended)
|
||||
|
||||
Automatically serve optimal format based on browser support.
|
||||
|
||||
**Priority**:
|
||||
1. **AVIF** - Best compression (Chrome, Edge)
|
||||
2. **WebP** - Good compression (Safari, Firefox)
|
||||
3. **Original format** - Fallback (older browsers)
|
||||
|
||||
**Usage**:
|
||||
```typescript
|
||||
// URL format
|
||||
/cdn-cgi/image/width=800,quality=85,format=auto/image.jpg
|
||||
|
||||
// Workers format
|
||||
fetch(imageURL, {
|
||||
cf: {
|
||||
image: {
|
||||
width: 800,
|
||||
quality: 85,
|
||||
format: 'auto'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Cloudflare Images
|
||||
https://imagedelivery.net/HASH/ID/w=800,q=85,f=auto
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Browser Support Detection
|
||||
|
||||
Cloudflare automatically checks the `Accept` header.
|
||||
|
||||
**Chrome/Edge**:
|
||||
```
|
||||
Accept: image/avif,image/webp,image/apng,image/*,*/*
|
||||
```
|
||||
→ Serves AVIF
|
||||
|
||||
**Safari**:
|
||||
```
|
||||
Accept: image/webp,image/apng,image/*,*/*
|
||||
```
|
||||
→ Serves WebP
|
||||
|
||||
**Older browsers**:
|
||||
```
|
||||
Accept: image/jpeg,image/png,image/*,*/*
|
||||
```
|
||||
→ Serves original format (JPEG)
|
||||
|
||||
---
|
||||
|
||||
## Manual Format Selection
|
||||
|
||||
### In URL Transformations
|
||||
|
||||
```html
|
||||
<!-- AVIF (best compression) -->
|
||||
<img src="/cdn-cgi/image/format=avif/image.jpg" />
|
||||
|
||||
<!-- WebP (good compression, wide support) -->
|
||||
<img src="/cdn-cgi/image/format=webp/image.jpg" />
|
||||
|
||||
<!-- JPEG (progressive) -->
|
||||
<img src="/cdn-cgi/image/format=jpeg/image.jpg" />
|
||||
|
||||
<!-- Baseline JPEG (older devices) -->
|
||||
<img src="/cdn-cgi/image/format=baseline-jpeg/image.jpg" />
|
||||
```
|
||||
|
||||
### In Workers
|
||||
|
||||
```typescript
|
||||
// Get optimal format from Accept header
|
||||
function getOptimalFormat(request: Request): 'avif' | 'webp' | 'auto' {
|
||||
const accept = request.headers.get('accept') || '';
|
||||
|
||||
if (/image\/avif/.test(accept)) {
|
||||
return 'avif';
|
||||
} else if (/image\/webp/.test(accept)) {
|
||||
return 'webp';
|
||||
}
|
||||
|
||||
return 'auto'; // Cloudflare decides
|
||||
}
|
||||
|
||||
return fetch(imageURL, {
|
||||
cf: {
|
||||
image: {
|
||||
format: getOptimalFormat(request)
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Format Comparison
|
||||
|
||||
| Format | Compression | Quality | Support | Use Case |
|
||||
|--------|-------------|---------|---------|----------|
|
||||
| **AVIF** | Best (~50% smaller) | Excellent | Modern browsers | First choice (auto) |
|
||||
| **WebP** | Good (~30% smaller) | Excellent | Wide support | Fallback from AVIF |
|
||||
| **JPEG** | Standard | Good | Universal | Fallback, photos |
|
||||
| **PNG** | Lossless | Lossless | Universal | Graphics, transparency |
|
||||
|
||||
**File Size Example** (1920x1080 photo):
|
||||
- Original JPEG: 500 KB
|
||||
- WebP: ~350 KB (30% smaller)
|
||||
- AVIF: ~250 KB (50% smaller)
|
||||
|
||||
---
|
||||
|
||||
## Progressive vs Baseline JPEG
|
||||
|
||||
**Progressive JPEG** (default):
|
||||
- Loads in multiple passes (low→high quality)
|
||||
- Better for slow connections
|
||||
- Slightly larger file size
|
||||
|
||||
**Baseline JPEG**:
|
||||
- Loads top-to-bottom
|
||||
- Better for older devices
|
||||
- Slightly smaller file size
|
||||
|
||||
**Usage**:
|
||||
```
|
||||
format=jpeg → Progressive JPEG
|
||||
format=baseline-jpeg → Baseline JPEG
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WebP Compression Modes
|
||||
|
||||
```typescript
|
||||
// Fast compression (faster encoding, larger file)
|
||||
fetch(imageURL, {
|
||||
cf: {
|
||||
image: {
|
||||
format: 'webp',
|
||||
compression: 'fast'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Lossless WebP (no quality loss, larger file)
|
||||
fetch(imageURL, {
|
||||
cf: {
|
||||
image: {
|
||||
format: 'webp',
|
||||
compression: 'lossless'
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Responsive Images with format=auto
|
||||
|
||||
```html
|
||||
<picture>
|
||||
<!-- Explicit AVIF for modern browsers -->
|
||||
<source
|
||||
type="image/avif"
|
||||
srcset="
|
||||
https://imagedelivery.net/HASH/ID/w=480,f=avif 480w,
|
||||
https://imagedelivery.net/HASH/ID/w=1920,f=avif 1920w
|
||||
"
|
||||
/>
|
||||
|
||||
<!-- WebP fallback -->
|
||||
<source
|
||||
type="image/webp"
|
||||
srcset="
|
||||
https://imagedelivery.net/HASH/ID/w=480,f=webp 480w,
|
||||
https://imagedelivery.net/HASH/ID/w=1920,f=webp 1920w
|
||||
"
|
||||
/>
|
||||
|
||||
<!-- JPEG fallback -->
|
||||
<img
|
||||
srcset="
|
||||
https://imagedelivery.net/HASH/ID/w=480,f=jpeg 480w,
|
||||
https://imagedelivery.net/HASH/ID/w=1920,f=jpeg 1920w
|
||||
"
|
||||
src="https://imagedelivery.net/HASH/ID/w=1920,f=jpeg"
|
||||
alt="Responsive image with format fallbacks"
|
||||
/>
|
||||
</picture>
|
||||
|
||||
<!-- OR: Let format=auto handle it -->
|
||||
<img
|
||||
srcset="
|
||||
https://imagedelivery.net/HASH/ID/w=480,f=auto 480w,
|
||||
https://imagedelivery.net/HASH/ID/w=1920,f=auto 1920w
|
||||
"
|
||||
src="https://imagedelivery.net/HASH/ID/w=1920,f=auto"
|
||||
alt="Auto-format responsive image"
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quality Recommendations by Format
|
||||
|
||||
```typescript
|
||||
const qualitySettings = {
|
||||
jpeg: 85, // Standard for photos
|
||||
webp: 85, // Same as JPEG
|
||||
avif: 85, // AVIF efficient at same quality
|
||||
png: undefined, // Lossless (quality N/A)
|
||||
graphics: 95 // High quality for logos/text
|
||||
};
|
||||
|
||||
// Photos
|
||||
/cdn-cgi/image/width=800,quality=85,format=auto/photo.jpg
|
||||
|
||||
// Graphics with text
|
||||
/cdn-cgi/image/width=800,quality=95,format=auto/logo.png
|
||||
|
||||
// Thumbnails (lower quality acceptable)
|
||||
/cdn-cgi/image/width=300,quality=75,format=auto/thumb.jpg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Support
|
||||
|
||||
**GIF**:
|
||||
```
|
||||
format=auto → Still GIF or first frame
|
||||
anim=true → Preserve animation
|
||||
```
|
||||
|
||||
**Animated WebP**:
|
||||
```typescript
|
||||
fetch(animatedGif, {
|
||||
cf: {
|
||||
image: {
|
||||
format: 'webp',
|
||||
anim: true // Preserve animation
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Metadata Handling
|
||||
|
||||
**Strip metadata** (smaller file size):
|
||||
```
|
||||
metadata=none
|
||||
```
|
||||
|
||||
**Keep copyright** (default for JPEG):
|
||||
```
|
||||
metadata=copyright
|
||||
```
|
||||
|
||||
**Keep all EXIF** (GPS, camera settings):
|
||||
```
|
||||
metadata=keep
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```
|
||||
/cdn-cgi/image/width=800,format=auto,metadata=none/photo.jpg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cost Optimization
|
||||
|
||||
1. **Use format=auto**: Smallest files = less bandwidth
|
||||
2. **Reasonable quality**: 80-85 for photos, 90-95 for graphics
|
||||
3. **Strip metadata**: `metadata=none` for public images
|
||||
4. **Cache at edge**: First transformation billable, subsequent free
|
||||
5. **WebP animations**: Convert GIF to animated WebP (smaller)
|
||||
|
||||
---
|
||||
|
||||
## Testing Format Support
|
||||
|
||||
```html
|
||||
<script>
|
||||
// Check AVIF support
|
||||
const avifSupport = document.createElement('canvas')
|
||||
.toDataURL('image/avif').indexOf('data:image/avif') === 0;
|
||||
|
||||
// Check WebP support
|
||||
const webpSupport = document.createElement('canvas')
|
||||
.toDataURL('image/webp').indexOf('data:image/webp') === 0;
|
||||
|
||||
console.log('AVIF:', avifSupport); // true in Chrome/Edge
|
||||
console.log('WebP:', webpSupport); // true in modern browsers
|
||||
</script>
|
||||
```
|
||||
|
||||
**But**: Let Cloudflare handle this with `format=auto`!
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Hero Image
|
||||
```
|
||||
width=1920,height=1080,fit=cover,quality=85,format=auto,metadata=none
|
||||
```
|
||||
|
||||
### Thumbnail
|
||||
```
|
||||
width=300,height=300,fit=cover,quality=75,format=auto,metadata=none
|
||||
```
|
||||
|
||||
### Avatar
|
||||
```
|
||||
width=200,height=200,fit=cover,gravity=face,quality=90,format=auto
|
||||
```
|
||||
|
||||
### Product Photo
|
||||
```
|
||||
width=800,height=800,fit=contain,quality=90,sharpen=2,format=auto
|
||||
```
|
||||
|
||||
### Blur Placeholder (LQIP)
|
||||
```
|
||||
width=50,quality=10,blur=20,format=webp,metadata=none
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always use format=auto**: Let Cloudflare optimize
|
||||
2. **Quality 80-90**: Balance file size and quality
|
||||
3. **Strip unnecessary metadata**: Smaller files
|
||||
4. **Test on real devices**: Verify format delivery
|
||||
5. **Monitor bandwidth**: Check Cloudflare Analytics
|
||||
6. **Use WebP for animations**: Smaller than GIF
|
||||
7. **Progressive JPEG for photos**: Better perceived load time
|
||||
|
||||
---
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- **Transform via URL**: https://developers.cloudflare.com/images/transform-images/transform-via-url/
|
||||
- **Supported Formats**: https://developers.cloudflare.com/images/transform-images/#supported-formats-and-limitations
|
||||
Reference in New Issue
Block a user