The Problem: Multi-CDN Chaos and Skyrocketing Costs

DEV's image pipeline was a patchwork of different CDNs, an image proxying service, and raw assets in AWS S3. The setup was expensive and complex.

The scraper tax: Aggressive scrapers and crawlers repeatedly requested uncached image variants, forcing the pipeline to re-fetch and re-process assets, skyrocketing compute costs.

Egress fees: Every cache miss meant fetching the raw image from cloud storage, incurring steep egress charges.

Transformation pricing: Premium image CDNs charge per thousand images processed. With millions of posts and avatars, those counts exploded.

Operational friction: Running separate providers for HTML caching and image delivery created overhead with complex configs and routing rules.

The Solution: bunny.net Optimizer + Perma-Cache

DEV switched to bunny.net for image delivery and optimization. The key components:

Bunny Optimizer

A managed dynamic image transformation API. Append query parameters like ?width=600&height=300&crop=1:1 and it handles resizing, cropping, and auto-compression. It negotiates WebP/AVIF based on browser Accept headers, reducing file sizes up to 80%.

Perma-Cache

The game-changer. Normally, infrequently accessed image variants get evicted from CDN edge servers, causing the next request to go back to origin. Perma-Cache permanently replicates optimized variants to Bunny Storage. Once processed, an image never hits AWS again, virtually eliminating egress fees.

Edge Scripting: TypeScript at the Edge

DEV used bunny.net's Edge Scripting (built on Deno and V8) to write middleware that runs in milliseconds at the CDN layer. This replaced custom image proxies and complex Rails routing.

Example: Smart downsizing for avatars

export default async function handleRequest(request: Request) {
  const url = new URL(request.url);
  if (url.pathname.startsWith('/uploads/')) {
    const isAvatar = url.pathname.includes('/avatars/');
    const isThumbnail = url.pathname.includes('/thumbnails/');
    const hasWidth = url.searchParams.has('width');
    if (isAvatar && !hasWidth) {
      url.searchParams.set('width', '150');
      url.searchParams.set('height', '150');
      url.searchParams.set('crop', '1:1');
    } else if (isThumbnail && !hasWidth) {
      url.searchParams.set('width', '400');
    }
    url.searchParams.set('auto', 'format');
    return fetch(url.toString(), request);
  }
  return fetch(request);
}

This script intercepts requests to /uploads/, enforces max dimensions for avatars (150px) and thumbnails (400px), and enables automatic format negotiation. The Rails app doesn't need to manage responsive breakpoints.

Pluggable Architecture in Forem

DEV's open-source Forem platform uses a strategy pattern for image optimization. The Images::Optimizer service routes to the configured provider:

# app/services/images/optimizer.rb
module Images
  class Optimizer
    def self.call(url, options = {})
      return url if url.blank?
      case provider
      when :bunny
        BunnyProvider.call(url, options)
      when :cloudflare
        CloudflareProvider.call(url, options)
      when :fastly
        FastlyProvider.call(url, options)
      else
        url
      end
    end

    def self.provider
      ENV.fetch("IMAGE_OPTIMIZATION_PROVIDER", "bunny").to_sym
    end
  end
end

Each provider implements its own URL rewriting. The bunny provider builds standard query strings:

# app/services/images/bunny_provider.rb
module Images
  class BunnyProvider
    def self.call(url, options = {})
      uri = URI.parse(url)
      query_params = []
      query_params << "width=#{options[:width]}" if options[:width]
      query_params << "height=#{options[:height]}" if options[:height]
      query_params << "crop=#{options[:crop]}" if options[:crop]
      query_params << "auto=format"
      uri.query = [uri.query, query_params.join("&")].compact.join("&")
      uri.to_s
    end
  end
end

This decoupled design lets self-hosted Forem communities choose their CDN by setting the IMAGE_OPTIMIZATION_PROVIDER env var.

Results

  • Bandwidth bills slashed to a fraction of previous enterprise CDN costs.
  • Egress fees virtually eliminated thanks to Perma-Cache.
  • Edge scripts deployed via GitHub Actions — push a commit, CI runs, new edge logic is live globally in seconds.
  • Reduced operational complexity — single provider for image optimization and delivery.

What You Should Do

If you're running a high-traffic site with user-uploaded images, audit your CDN costs and egress fees. Consider a solution like bunny.net with Perma-Cache and edge scripting to cache optimized variants permanently and absorb scraper traffic without origin hits. The code examples above show how to integrate it cleanly into a Rails app.

Check out the full source article on DEV for more details on their migration.