477 lines
12 KiB
Markdown
477 lines
12 KiB
Markdown
# Top Errors and Solutions
|
|
|
|
Complete troubleshooting guide for all documented Cloudflare Images errors.
|
|
|
|
---
|
|
|
|
## Direct Creator Upload Errors
|
|
|
|
### 1. CORS Error - Content-Type Not Allowed
|
|
|
|
**Error**:
|
|
```
|
|
Access to XMLHttpRequest blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers
|
|
```
|
|
|
|
**Source**: [Cloudflare Community #345739](https://community.cloudflare.com/t/direct-image-upload-cors-error/345739), [#368114](https://community.cloudflare.com/t/cloudflare-images-direct-upload-cors-problem/368114)
|
|
|
|
**Why It Happens**:
|
|
Server CORS settings only allow `multipart/form-data` for Content-Type header.
|
|
|
|
**Solution**:
|
|
```javascript
|
|
// ✅ CORRECT
|
|
const formData = new FormData();
|
|
formData.append('file', fileInput.files[0]);
|
|
await fetch(uploadURL, {
|
|
method: 'POST',
|
|
body: formData // Browser sets multipart/form-data automatically
|
|
});
|
|
|
|
// ❌ WRONG
|
|
await fetch(uploadURL, {
|
|
headers: { 'Content-Type': 'application/json' }, // CORS error
|
|
body: JSON.stringify({ file: base64Image })
|
|
});
|
|
```
|
|
|
|
**Prevention**:
|
|
- Use FormData API
|
|
- Let browser set Content-Type header (don't set manually)
|
|
- Name field `file` (not `image` or other)
|
|
|
|
---
|
|
|
|
### 2. Error 5408 - Upload Timeout
|
|
|
|
**Error**: `Error 5408` after ~15 seconds
|
|
|
|
**Source**: [Cloudflare Community #571336](https://community.cloudflare.com/t/images-direct-creator-upload-error-5408/571336)
|
|
|
|
**Why It Happens**:
|
|
Cloudflare has 30-second request timeout. Slow uploads or large files exceed limit.
|
|
|
|
**Solution**:
|
|
```javascript
|
|
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
|
|
|
if (file.size > MAX_FILE_SIZE) {
|
|
alert('File too large. Please select an image under 10MB.');
|
|
return;
|
|
}
|
|
|
|
// Compress image before upload (optional)
|
|
async function compressImage(file) {
|
|
const canvas = document.createElement('canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
const img = await createImageBitmap(file);
|
|
|
|
const maxWidth = 4000;
|
|
const scale = Math.min(1, maxWidth / img.width);
|
|
|
|
canvas.width = img.width * scale;
|
|
canvas.height = img.height * scale;
|
|
|
|
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
|
|
return new Promise((resolve) => {
|
|
canvas.toBlob((blob) => resolve(blob), 'image/jpeg', 0.9);
|
|
});
|
|
}
|
|
```
|
|
|
|
**Prevention**:
|
|
- Limit file size (10MB max recommended)
|
|
- Compress images client-side if needed
|
|
- Show upload progress to user
|
|
- Handle timeout errors gracefully
|
|
|
|
---
|
|
|
|
### 3. Error 400 - Invalid File Parameter
|
|
|
|
**Error**: `400 Bad Request` with unhelpful message
|
|
|
|
**Source**: [Cloudflare Community #487629](https://community.cloudflare.com/t/direct-creator-upload-returning-400/487629)
|
|
|
|
**Why It Happens**:
|
|
File field must be named `file` (not `image`, `photo`, etc.).
|
|
|
|
**Solution**:
|
|
```javascript
|
|
// ✅ CORRECT
|
|
formData.append('file', imageFile);
|
|
|
|
// ❌ WRONG
|
|
formData.append('image', imageFile); // 400 error
|
|
formData.append('photo', imageFile); // 400 error
|
|
formData.append('upload', imageFile); // 400 error
|
|
```
|
|
|
|
**Prevention**:
|
|
- Always name the field `file`
|
|
- Check FormData contents before sending
|
|
|
|
---
|
|
|
|
### 4. CORS Preflight Failures
|
|
|
|
**Error**: Preflight OPTIONS request blocked
|
|
|
|
**Source**: [Cloudflare Community #306805](https://community.cloudflare.com/t/cors-error-when-using-direct-creator-upload/306805)
|
|
|
|
**Why It Happens**:
|
|
Calling `/direct_upload` API directly from browser (should be backend-only).
|
|
|
|
**Solution**:
|
|
```
|
|
CORRECT ARCHITECTURE:
|
|
|
|
Browser → POST /api/upload-url → Backend
|
|
↓
|
|
POST /direct_upload → Cloudflare API
|
|
↓
|
|
Backend ← Returns uploadURL ← Cloudflare API
|
|
↓
|
|
Browser receives uploadURL
|
|
↓
|
|
Browser → Uploads to uploadURL → Cloudflare (direct upload)
|
|
```
|
|
|
|
**Prevention**:
|
|
- Never expose API token to browser
|
|
- Generate upload URL on backend
|
|
- Return uploadURL to frontend
|
|
- Frontend uploads to uploadURL (not /direct_upload)
|
|
|
|
---
|
|
|
|
## Image Transformation Errors
|
|
|
|
### 5. Error 9401 - Invalid Arguments
|
|
|
|
**Error**: `Cf-Resized: err=9401` in response headers
|
|
|
|
**Source**: [Cloudflare Docs - Troubleshooting](https://developers.cloudflare.com/images/reference/troubleshooting/)
|
|
|
|
**Why It Happens**:
|
|
Missing required `cf.image` parameters or invalid values.
|
|
|
|
**Solution**:
|
|
```typescript
|
|
// ✅ CORRECT
|
|
fetch(imageURL, {
|
|
cf: {
|
|
image: {
|
|
width: 800,
|
|
quality: 85,
|
|
format: 'auto'
|
|
}
|
|
}
|
|
});
|
|
|
|
// ❌ WRONG
|
|
fetch(imageURL, {
|
|
cf: {
|
|
image: {
|
|
width: 'large', // Must be number
|
|
quality: 150, // Max 100
|
|
format: 'invalid' // Must be valid format
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
**Prevention**:
|
|
- Validate all parameters
|
|
- Use TypeScript for type checking
|
|
- Check official docs for valid ranges
|
|
|
|
---
|
|
|
|
### 6. Error 9402 - Image Too Large
|
|
|
|
**Error**: `Cf-Resized: err=9402`
|
|
|
|
**Source**: [Cloudflare Docs - Troubleshooting](https://developers.cloudflare.com/images/reference/troubleshooting/)
|
|
|
|
**Why It Happens**:
|
|
Image exceeds maximum area or download fails.
|
|
|
|
**Solution**:
|
|
```typescript
|
|
// Check image dimensions before transforming
|
|
const response = await fetch(imageURL, { method: 'HEAD' });
|
|
// Or fetch and check after
|
|
const img = await fetch(imageURL);
|
|
// Validate size
|
|
```
|
|
|
|
**Prevention**:
|
|
- Validate source image dimensions
|
|
- Max 100 megapixels (e.g., 10000x10000px)
|
|
- Use reasonable source images
|
|
|
|
---
|
|
|
|
### 7. Error 9403 - Request Loop
|
|
|
|
**Error**: `Cf-Resized: err=9403`
|
|
|
|
**Source**: [Cloudflare Docs - Troubleshooting](https://developers.cloudflare.com/images/reference/troubleshooting/)
|
|
|
|
**Why It Happens**:
|
|
Worker fetching its own URL or already-resized image.
|
|
|
|
**Solution**:
|
|
```typescript
|
|
// ✅ CORRECT - Fetch external origin
|
|
export default {
|
|
async fetch(request: Request): Promise<Response> {
|
|
const url = new URL(request.url);
|
|
|
|
if (url.pathname.startsWith('/images/')) {
|
|
const imagePath = url.pathname.replace('/images/', '');
|
|
const originURL = `https://storage.example.com/${imagePath}`;
|
|
|
|
return fetch(originURL, {
|
|
cf: { image: { width: 800 } }
|
|
});
|
|
}
|
|
|
|
return new Response('Not found', { status: 404 });
|
|
}
|
|
};
|
|
|
|
// ❌ WRONG - Fetches worker's own URL (loop)
|
|
export default {
|
|
async fetch(request: Request): Promise<Response> {
|
|
return fetch(request, { // Fetches self
|
|
cf: { image: { width: 800 } }
|
|
});
|
|
}
|
|
};
|
|
```
|
|
|
|
**Prevention**:
|
|
- Always fetch external origin
|
|
- Don't transform already-transformed images
|
|
- Check URL routing logic
|
|
|
|
---
|
|
|
|
### 8. Error 9406/9419 - Invalid URL Format
|
|
|
|
**Error**: `Cf-Resized: err=9406` or `err=9419`
|
|
|
|
**Source**: [Cloudflare Docs - Troubleshooting](https://developers.cloudflare.com/images/reference/troubleshooting/)
|
|
|
|
**Why It Happens**:
|
|
Image URL uses HTTP (not HTTPS) or contains spaces/unescaped Unicode.
|
|
|
|
**Solution**:
|
|
```typescript
|
|
// ✅ CORRECT
|
|
const filename = "photo name.jpg";
|
|
const imageURL = `https://example.com/images/${encodeURIComponent(filename)}`;
|
|
|
|
// ❌ WRONG
|
|
const imageURL = "http://example.com/image.jpg"; // HTTP not allowed
|
|
const imageURL = "https://example.com/photo name.jpg"; // Space not encoded
|
|
```
|
|
|
|
**Prevention**:
|
|
- Always use HTTPS (HTTP not supported)
|
|
- URL-encode all paths with `encodeURIComponent()`
|
|
- No spaces or unescaped Unicode in URLs
|
|
|
|
---
|
|
|
|
### 9. Error 9412 - Non-Image Response
|
|
|
|
**Error**: `Cf-Resized: err=9412`
|
|
|
|
**Source**: [Cloudflare Docs - Troubleshooting](https://developers.cloudflare.com/images/reference/troubleshooting/)
|
|
|
|
**Why It Happens**:
|
|
Origin server returns HTML (e.g., 404 page) instead of image.
|
|
|
|
**Solution**:
|
|
```typescript
|
|
// Verify URL before transforming
|
|
const originResponse = await fetch(imageURL, { method: 'HEAD' });
|
|
const contentType = originResponse.headers.get('content-type');
|
|
|
|
if (!contentType?.startsWith('image/')) {
|
|
return new Response('Not an image', { status: 400 });
|
|
}
|
|
|
|
return fetch(imageURL, {
|
|
cf: { image: { width: 800 } }
|
|
});
|
|
```
|
|
|
|
**Prevention**:
|
|
- Verify origin returns image (check Content-Type)
|
|
- Handle 404s before transforming
|
|
- Validate image URLs
|
|
|
|
---
|
|
|
|
### 10. Error 9413 - Max Image Area Exceeded
|
|
|
|
**Error**: `Cf-Resized: err=9413`
|
|
|
|
**Source**: [Cloudflare Docs - Troubleshooting](https://developers.cloudflare.com/images/reference/troubleshooting/)
|
|
|
|
**Why It Happens**:
|
|
Source image exceeds 100 megapixels (e.g., 10000x10000px).
|
|
|
|
**Solution**:
|
|
```typescript
|
|
const MAX_MEGAPIXELS = 100;
|
|
|
|
if (width * height > MAX_MEGAPIXELS * 1_000_000) {
|
|
return new Response('Image too large', { status: 413 });
|
|
}
|
|
```
|
|
|
|
**Prevention**:
|
|
- Validate image dimensions before transforming
|
|
- Pre-process oversized images
|
|
- Reject images above threshold (100 megapixels)
|
|
|
|
---
|
|
|
|
## Configuration Errors
|
|
|
|
### 11. Flexible Variants + Signed URLs Incompatibility
|
|
|
|
**Error**: Flexible variants don't work with private images
|
|
|
|
**Source**: [Cloudflare Docs - Enable flexible variants](https://developers.cloudflare.com/images/manage-images/enable-flexible-variants/)
|
|
|
|
**Why It Happens**:
|
|
Flexible variants cannot be used with `requireSignedURLs=true`.
|
|
|
|
**Solution**:
|
|
```typescript
|
|
// ✅ CORRECT - Use named variants for private images
|
|
await uploadImage({
|
|
file: imageFile,
|
|
requireSignedURLs: true // Use named variants: /public, /avatar, etc.
|
|
});
|
|
|
|
// ❌ WRONG - Flexible variants don't support signed URLs
|
|
// Cannot use: /w=400,sharpen=3 with requireSignedURLs=true
|
|
```
|
|
|
|
**Prevention**:
|
|
- Use named variants for private images
|
|
- Use flexible variants for public images only
|
|
|
|
---
|
|
|
|
### 12. SVG Resizing Limitation
|
|
|
|
**Error**: SVG files don't resize via transformations
|
|
|
|
**Source**: [Cloudflare Docs - SVG files](https://developers.cloudflare.com/images/transform-images/#svg-files)
|
|
|
|
**Why It Happens**:
|
|
SVG is vector format (inherently scalable), resizing not applicable.
|
|
|
|
**Solution**:
|
|
```typescript
|
|
// SVGs can be served but not resized
|
|
// Use any variant name as placeholder
|
|
// https://imagedelivery.net/<HASH>/<SVG_ID>/public
|
|
|
|
// SVG will be served at original size regardless of variant settings
|
|
```
|
|
|
|
**Prevention**:
|
|
- Don't try to resize SVGs
|
|
- Serve SVGs as-is
|
|
- Use variants as placeholders
|
|
|
|
---
|
|
|
|
### 13. EXIF Metadata Stripped by Default
|
|
|
|
**Error**: GPS data, camera settings removed from uploaded JPEGs
|
|
|
|
**Source**: [Cloudflare Docs - Transform via URL](https://developers.cloudflare.com/images/transform-images/transform-via-url/#metadata)
|
|
|
|
**Why It Happens**:
|
|
Default behavior strips all metadata except copyright.
|
|
|
|
**Solution**:
|
|
```typescript
|
|
// Preserve metadata
|
|
fetch(imageURL, {
|
|
cf: {
|
|
image: {
|
|
width: 800,
|
|
metadata: 'keep' // Options: 'none', 'copyright', 'keep'
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
**Prevention**:
|
|
- Use `metadata=keep` if preservation needed
|
|
- Default `copyright` for JPEG
|
|
- Color profiles and EXIF rotation always applied
|
|
|
|
---
|
|
|
|
## General Troubleshooting
|
|
|
|
### Images not transforming
|
|
|
|
**Symptoms**: `/cdn-cgi/image/...` returns original or 404
|
|
|
|
**Solutions**:
|
|
1. Enable transformations: Dashboard → Images → Transformations → Enable
|
|
2. Verify zone proxied (orange cloud)
|
|
3. Check source image accessible
|
|
4. Wait 5-10 minutes for propagation
|
|
|
|
### Signed URLs returning 403
|
|
|
|
**Symptoms**: 403 Forbidden with signed URL
|
|
|
|
**Solutions**:
|
|
1. Verify image uploaded with `requireSignedURLs=true`
|
|
2. Check signature generation (HMAC-SHA256)
|
|
3. Ensure expiry in future
|
|
4. Verify signing key matches dashboard
|
|
5. Cannot use flexible variants (use named variants)
|
|
|
|
---
|
|
|
|
## Checking for Errors
|
|
|
|
**Response Headers**:
|
|
```javascript
|
|
const response = await fetch(transformedImageURL);
|
|
const cfResized = response.headers.get('Cf-Resized');
|
|
|
|
if (cfResized?.includes('err=')) {
|
|
console.error('Transformation error:', cfResized);
|
|
}
|
|
```
|
|
|
|
**Common patterns**:
|
|
- `Cf-Resized: err=9401` - Invalid arguments
|
|
- `Cf-Resized: err=9403` - Request loop
|
|
- `Cf-Resized: err=9412` - Non-image response
|
|
|
|
---
|
|
|
|
## Official Documentation
|
|
|
|
- **Troubleshooting**: https://developers.cloudflare.com/images/reference/troubleshooting/
|
|
- **Transform via Workers**: https://developers.cloudflare.com/images/transform-images/transform-via-workers/
|