Skip to content

Caching Strategies

Optimize performance and reduce API calls with smart caching.

Server-Side Caching

Built-in Cache Headers

All generated images include cache headers:

Cache-Control: public, max-age=86400

This allows CDNs and browsers to cache images for 24 hours.

CDN Caching

Cloudflare

javascript
// Cloudflare Worker
export default {
  async fetch(request, env) {
    const cacheKey = new Request(request.url, request);
    const cache = caches.default;
    
    // Check cache first
    let response = await cache.match(cacheKey);
    if (response) return response;
    
    // Generate new image
    response = await generateOGImage(request, env);
    
    // Cache for 7 days
    response = new Response(response.body, response);
    response.headers.set('Cache-Control', 'public, max-age=604800');
    
    // Store in cache
    event.waitUntil(cache.put(cacheKey, response.clone()));
    
    return response;
  }
};

Vercel Edge

javascript
// Next.js API route
export const config = {
  runtime: 'edge',
};

export default async function handler(request) {
  // ...generate image
  
  return new Response(imageBuffer, {
    headers: {
      'Content-Type': 'image/png',
      'Cache-Control': 'public, s-maxage=86400, stale-while-revalidate=604800'
    }
  });
}

Redis Caching

javascript
// Node.js with Redis
import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL);
const CACHE_TTL = 86400; // 24 hours

async function getOGImage(params) {
  const cacheKey = `og:${JSON.stringify(params)}`;
  
  // Check cache
  const cached = await redis.getBuffer(cacheKey);
  if (cached) return cached;
  
  // Generate new image
  const response = await fetch('https://ogimageapi.io/api/generate', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.OG_IMAGE_API_KEY
    },
    body: JSON.stringify(params)
  });
  
  const buffer = Buffer.from(await response.arrayBuffer());
  
  // Cache result
  await redis.setex(cacheKey, CACHE_TTL, buffer);
  
  return buffer;
}

File System Caching

Build-Time Generation

javascript
// Generate once at build time
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';

async function generateWithCache(params) {
  const hash = crypto
    .createHash('md5')
    .update(JSON.stringify(params))
    .digest('hex');
  
  const cachePath = path.join('./cache/og', `${hash}.png`);
  
  // Return cached if exists
  if (fs.existsSync(cachePath)) {
    return fs.readFileSync(cachePath);
  }
  
  // Generate new
  const response = await fetch('https://ogimageapi.io/api/generate', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.OG_IMAGE_API_KEY
    },
    body: JSON.stringify(params)
  });
  
  const buffer = Buffer.from(await response.arrayBuffer());
  
  // Save to cache
  fs.mkdirSync(path.dirname(cachePath), { recursive: true });
  fs.writeFileSync(cachePath, buffer);
  
  return buffer;
}

Client-Side Caching

Browser Cache

Images are automatically cached by browsers. Use versioned URLs for cache busting:

html
<!-- Version in URL for cache control -->
<meta property="og:image" content="/api/og?title=Hello&v=2">

Service Worker

javascript
// sw.js
self.addEventListener('fetch', (event) => {
  if (event.request.url.includes('/api/og')) {
    event.respondWith(
      caches.open('og-images').then((cache) => {
        return cache.match(event.request).then((response) => {
          if (response) return response;
          
          return fetch(event.request).then((networkResponse) => {
            cache.put(event.request, networkResponse.clone());
            return networkResponse;
          });
        });
      })
    );
  }
});

Cache Invalidation

Content-Based Keys

javascript
function getCacheKey(content) {
  const hash = crypto
    .createHash('md5')
    .update(content.title + content.updatedAt)
    .digest('hex');
  return `og-${hash}`;
}

Time-Based Invalidation

javascript
// Add timestamp to URL
const imageUrl = `/api/og?title=${title}&t=${Math.floor(Date.now() / 86400000)}`;

Best Practices

StrategyWhen to Use
CDN cachingProduction, high traffic
RedisDynamic content, multi-server
File systemBuild-time, static sites
Signed URLsContent that rarely changes

Cache Duration Guidelines

Content TypeRecommended TTL
Static pages7 days
Blog posts24 hours
Products1-6 hours
User content1 hour
Dynamic dataNo cache or very short

Generate stunning Open Graph images programmatically.