AgentStack
SKILL verified MIT Self-run

Cloudflare Images

skill-jackspace-claudeskillz-cloudflare-images · by jackspace

|

No reviews yet
0 installs
15 views
0.0% view→install

Install

$ agentstack add skill-jackspace-claudeskillz-cloudflare-images

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Cloudflare Images? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Cloudflare Images

Status: Production Ready ✅ Last Updated: 2025-10-26 Dependencies: Cloudflare account with Images enabled Latest Versions: Cloudflare Images API v2


Overview

Cloudflare Images provides two powerful features:

  1. Images API: Upload, store, and serve images with automatic optimization and variants
  2. Image Transformations: Resize, optimize, and transform any publicly accessible image

Key Benefits:

  • Global CDN delivery
  • Automatic WebP/AVIF conversion
  • Variants for different use cases (up to 100)
  • Direct creator upload (user uploads without API keys)
  • Signed URLs for private images
  • Transform any image via URL or Workers

Quick Start (5 Minutes)

1. Enable Cloudflare Images

Log into Cloudflare dashboard → Images → Enable for your account.

Get your Account ID and create an API token with Cloudflare Images: Edit permissions.

Why this matters:

  • Account ID and API token are required for all API operations
  • Images Free plan includes limited transformations

2. Upload Your First Image

curl --request POST \
  --url https://api.cloudflare.com/client/v4/accounts//images/v1 \
  --header 'Authorization: Bearer ' \
  --header 'Content-Type: multipart/form-data' \
  --form 'file=@./image.jpg'

Response includes:

  • id: Image ID for serving
  • variants: Array of delivery URLs

CRITICAL:

  • Use multipart/form-data encoding (NOT application/json)
  • Image ID is automatically generated (or use custom ID)

3. Serve the Image

//public" />

Default public variant serves the image. Replace with your own variant names.

4. Enable Image Transformations

Dashboard → ImagesTransformations → Select your zone → Enable for zone

Now you can transform ANY image:

Why this matters:

  • Works on images stored OUTSIDE Cloudflare Images
  • Automatic caching on Cloudflare's global network
  • No additional storage costs

5. Transform via Workers (Advanced)

export default {
  async fetch(request: Request): Promise {
    const imageURL = "https://example.com/image.jpg";

    return fetch(imageURL, {
      cf: {
        image: {
          width: 800,
          quality: 85,
          format: "auto" // WebP/AVIF for supporting browsers
        }
      }
    });
  }
};

The 3-Feature System

Feature 1: Images API (Upload & Storage)

Store images on Cloudflare's network and serve them globally.

Upload Methods:

  1. File Upload - Upload files directly from your server
  2. Upload via URL - Ingest images from external URLs
  3. Direct Creator Upload - Generate one-time upload URLs for user uploads

Serving Options:

  • Default domain: imagedelivery.net
  • Custom domains: /cdn-cgi/imagedelivery/...
  • Signed URLs: Private images with expiry tokens

See: templates/upload-api-basic.ts, templates/direct-creator-upload-backend.ts

Feature 2: Image Transformations

Optimize and resize ANY image (stored in Images or external).

Two Methods:

  1. URL Transformations - Special URL format
  2. Workers Transformations - Programmatic control via fetch

Common Transformations:

  • Resize: width=800,height=600,fit=cover
  • Optimize: quality=85,format=auto
  • Effects: blur=10,sharpen=3
  • Crop: gravity=face,zoom=0.5

See: templates/transform-via-url.ts, templates/transform-via-workers.ts

Feature 3: Variants

Predefined image sizes for different use cases.

Named Variants (up to 100):

  • Create once, use everywhere
  • Example: thumbnail, avatar, hero
  • Consistent transformations

Flexible Variants (dynamic):

  • Enable per account
  • Use transformation params in URL
  • Example: w=400,sharpen=3
  • Cannot use with signed URLs

See: templates/variants-management.ts, references/variants-guide.md


Images API - Upload Methods

Method 1: File Upload (Basic)

curl --request POST \
  https://api.cloudflare.com/client/v4/accounts/{account_id}/images/v1 \
  --header "Authorization: Bearer " \
  --header "Content-Type: multipart/form-data" \
  --form 'file=@./image.jpg' \
  --form 'requireSignedURLs=false' \
  --form 'metadata={"key":"value"}'

Key Options:

  • file: Image file (required)
  • id: Custom ID (optional, default auto-generated)
  • requireSignedURLs: true for private images (default: false)
  • metadata: JSON object (max 1024 bytes, not visible to end users)

Response:

{
  "result": {
    "id": "2cdc28f0-017a-49c4-9ed7-87056c83901",
    "filename": "image.jpg",
    "uploaded": "2022-01-31T16:39:28.458Z",
    "requireSignedURLs": false,
    "variants": [
      "https://imagedelivery.net/Vi7wi5KSItxGFsWRG2Us6Q/2cdc28f0.../public"
    ]
  }
}

See: templates/upload-api-basic.ts

Method 2: Upload via URL

Ingest images from external sources without downloading first.

curl --request POST \
  https://api.cloudflare.com/client/v4/accounts/{account_id}/images/v1 \
  --header "Authorization: Bearer " \
  --form 'url=https://example.com/image.jpg' \
  --form 'metadata={"source":"external"}'

When to use:

  • Migrating images from another service
  • Ingesting user-provided URLs
  • Backing up images from external sources

CRITICAL:

  • URL must be publicly accessible or authenticated
  • Supports HTTP basic auth: https://user:password@example.com/image.jpg
  • Cannot use both file and url in same request

See: templates/upload-via-url.ts

Method 3: Direct Creator Upload ⭐

Generate one-time upload URLs for users to upload directly to Cloudflare (no API key exposure).

Backend Endpoint (generate upload URL):

const response = await fetch(
  `https://api.cloudflare.com/client/v4/accounts/${accountId}/images/v2/direct_upload`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      requireSignedURLs: true,
      metadata: { userId: '12345' },
      expiry: '2025-10-26T18:00:00Z' // Optional: default 30min, max 6hr
    })
  }
);

const { uploadURL, id } = await response.json();
// Return uploadURL to frontend

Frontend Upload (HTML + JavaScript):


  
  Upload

document.getElementById('upload-form').addEventListener('submit', async (e) => {
  e.preventDefault();

  const fileInput = document.getElementById('file-input');
  const formData = new FormData();
  formData.append('file', fileInput.files[0]); // MUST be named 'file'

  const uploadURL = 'UPLOAD_URL_FROM_BACKEND'; // Get from backend

  const response = await fetch(uploadURL, {
    method: 'POST',
    body: formData // NO Content-Type header, browser sets multipart/form-data
  });

  if (response.ok) {
    console.log('Upload successful!');
  }
});

Why this matters:

  • No API key exposure to browser
  • Users upload directly to Cloudflare (faster, no intermediary server)
  • One-time URL expires after use or timeout
  • Webhooks available for upload success/failure notifications

CRITICAL CORS FIX:

  • DO: Use multipart/form-data encoding (let browser set header)
  • DO: Name field file (NOT image or other names)
  • DO: Call /direct_upload API from backend only
  • DON'T: Set Content-Type: application/json or image/jpeg
  • DON'T: Call /direct_upload from browser (CORS will fail)

See: templates/direct-creator-upload-backend.ts, templates/direct-creator-upload-frontend.html, references/direct-upload-complete-workflow.md


Image Transformations

URL Transformations

Transform images using a special URL format.

URL Pattern:

https:///cdn-cgi/image//

Example:

Common Options:

  • Sizing: width=800, height=600, fit=cover
  • Quality: quality=85 (1-100)
  • Format: format=auto (WebP/AVIF auto-detection), format=webp, format=jpeg
  • Cropping: gravity=auto (smart crop), gravity=face, trim=10
  • Effects: blur=10, sharpen=3, brightness=1.2, contrast=1.1
  • Rotation: rotate=90, flip=h (horizontal), flip=v (vertical)

Fit Options:

  • scale-down: Shrink to fit (never enlarge)
  • contain: Resize to fit within dimensions (preserve aspect ratio)
  • cover: Resize to fill dimensions (may crop)
  • crop: Crop to exact dimensions
  • pad: Resize and add padding (use with background option)

Format Auto-Detection:

Cloudflare serves:

  • AVIF to browsers that support it (Chrome, Edge)
  • WebP to browsers without AVIF support (Safari, Firefox)
  • Original format (JPEG) as fallback

See: templates/transform-via-url.ts, references/transformation-options.md

Workers Transformations

Programmatic image transformations with custom URL schemes.

Basic Pattern:

export default {
  async fetch(request: Request): Promise {
    const url = new URL(request.url);

    // Custom URL scheme: /images/thumbnail/photo.jpg
    if (url.pathname.startsWith('/images/thumbnail/')) {
      const imagePath = url.pathname.replace('/images/thumbnail/', '');
      const imageURL = `https://storage.example.com/${imagePath}`;

      return fetch(imageURL, {
        cf: {
          image: {
            width: 300,
            height: 300,
            fit: 'cover',
            quality: 85
          }
        }
      });
    }

    return new Response('Not found', { status: 404 });
  }
};

Advanced: Content Negotiation:

const accept = request.headers.get('accept') || '';

let format: 'avif' | 'webp' | 'auto' = 'auto';
if (/image\/avif/.test(accept)) {
  format = 'avif';
} else if (/image\/webp/.test(accept)) {
  format = 'webp';
}

return fetch(imageURL, {
  cf: {
    image: {
      format,
      width: 800,
      quality: 85
    }
  }
});

Why Workers Transformations:

  • Custom URL schemes: Hide image storage location
  • Preset names: Use thumbnail, avatar, large instead of pixel values
  • Content negotiation: Serve optimal format based on browser
  • Access control: Check authentication before serving
  • Dynamic sizing: Calculate dimensions based on device type

See: templates/transform-via-workers.ts, references/transformation-options.md


Variants Management

Named Variants (Up to 100)

Create predefined transformations for different use cases.

Create via API:

curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/images/v1/variants" \
  --header "Authorization: Bearer " \
  --header "Content-Type: application/json" \
  --data '{
    "id": "avatar",
    "options": {
      "fit": "cover",
      "width": 200,
      "height": 200,
      "metadata": "none"
    },
    "neverRequireSignedURLs": false
  }'

Use in URLs:

//avatar" />

When to use:

  • Consistent image sizes across your app
  • Private images (works with signed URLs)
  • Simple, predictable URLs

See: templates/variants-management.ts

Flexible Variants

Dynamic transformations using params in URL.

Enable (per account, one-time):

curl --request PATCH \
  https://api.cloudflare.com/client/v4/accounts/{account_id}/images/v1/config \
  --header "Authorization: Bearer " \
  --header "Content-Type: application/json" \
  --data '{"flexible_variants": true}'

Use in URLs:

//w=400,sharpen=3" />

When to use:

  • Dynamic sizing needs
  • Public images only (cannot use with signed URLs)
  • Rapid prototyping

CRITICAL:

  • Cannot use with requireSignedURLs=true
  • Use named variants for private images

See: references/variants-guide.md


Signed URLs (Private Images)

Generate time-limited URLs for private images using HMAC-SHA256 tokens.

URL Format:

https://imagedelivery.net///?exp=&sig=

Generate Signature (Workers example):

async function generateSignedURL(
  imageId: string,
  variant: string,
  expirySeconds: number = 3600
): Promise {
  const accountHash = 'YOUR_ACCOUNT_HASH';
  const signingKey = 'YOUR_SIGNING_KEY'; // Dashboard → Images → Keys

  const expiry = Math.floor(Date.now() / 1000) + expirySeconds;
  const stringToSign = `${imageId}${variant}${expiry}`;

  const encoder = new TextEncoder();
  const keyData = encoder.encode(signingKey);
  const messageData = encoder.encode(stringToSign);

  const cryptoKey = await crypto.subtle.importKey(
    'raw',
    keyData,
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign']
  );

  const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData);
  const sig = Array.from(new Uint8Array(signature))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');

  return `https://imagedelivery.net/${accountHash}/${imageId}/${variant}?exp=${expiry}&sig=${sig}`;
}

Usage:

const signedURL = await generateSignedURL('image-id', 'public', 3600);
// Returns URL valid for 1 hour

When to use:

  • User profile photos (private until shared)
  • Paid content (time-limited access)
  • Temporary downloads
  • Secure image delivery

See: templates/signed-urls-generation.ts, references/signed-urls-guide.md


Responsive Images

Serve optimal image sizes for different screen sizes.

Using Named Variants:

//mobile 480w,
    https://imagedelivery.net///tablet 768w,
    https://imagedelivery.net///desktop 1920w
  "
  sizes="(max-width: 480px) 480px, (max-width: 768px) 768px, 1920px"
  src="https://imagedelivery.net///desktop"
  alt="Responsive image"
/>

Using Flexible Variants:

//w=480,f=auto 480w,
    https://imagedelivery.net///w=768,f=auto 768w,
    https://imagedelivery.net///w=1920,f=auto 1920w
  "
  sizes="(max-width: 480px) 480px, (max-width: 768px) 768px, 1920px"
  src="https://imagedelivery.net///w=1920,f=auto"
  alt="Responsive image"
/>

Art Direction (different crops for mobile vs desktop):


  //mobile-square"
  />
  //desktop-wide"
  />
  //desktop-wide" alt="Hero image" />

See: templates/responsive-images-srcset.html, references/responsive-images-patterns.md


Critical Rules

Always Do

✅ Use multipart/form-data for Direct Creator Upload ✅ Name the file field file (not image or other names) ✅ Call /direct_upload API from backend only (NOT browser) ✅ Use HTTPS URLs for transformations (HTTP not supported) ✅ URL-encode special characters in image paths ✅ Enable transformations on zone before using /cdn-cgi/image/ ✅ Use named variants for private images (signed URLs) ✅ Check Cf-Resized header for transformation errors ✅ Set format=auto for automatic WebP/AVIF conversion ✅ Use fit=scale-down to prevent unwanted enlargement

Never Do

❌ Use application/json Content-Type for file uploads ❌ Call /direct_upload from browser (CORS will fail) ❌ Use flexible variants with requireSignedURLs=true ❌ Resize SVG files (they're inherently scalable) ❌ Use HTTP URLs for transformations (HTTPS only) ❌ Put spaces or unescaped Unicode in URLs ❌ Transform the same image multiple times in Workers (causes 9403 loop) ❌ Exceed 100 megapixels image size ❌ Use /cdn-cgi/image/ endpoint in Workers (use cf.image instead) ❌ Forget to enable transformations on zone before use


Known Issues Prevention

This skill prevents 13+ documented issues.

Issue #1: Direct Creator Upload CORS Error

Error: Access to XMLHttpRequest blocked by CORS policy: Request header field content-type is not allowed

Source: Cloudflare Community #345739, #368114

Why It Happens: Server CORS settings only allow multipart/form-data for Content-Type header

Prevention:

// ✅ CORRECT
const formData = new FormData();
formData.append('file', fileInput.files[0]);
await f

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [jackspace](https://github.com/jackspace)
- **Source:** [jackspace/ClaudeSkillz](https://github.com/jackspace/ClaudeSkillz)
- **License:** MIT
- **Homepage:** http://claudeskillz.jackspace.com/

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.