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.


