How to Serve 1 Million Images
Without Paying Cloud Giants a Dime
AWS, Google Cloud, and Azure want you to believe that serving images at scale requires their infrastructure. The math says otherwise. Here is the full breakdown of how independent storage plus a lightweight CDN delivers a million image requests a month for under $15.
Updated 2026
Scale & Cost Engineering

There is a story the cloud industry tells about scale. The story goes that as soon as your needs grow past a certain point, enterprise infrastructure becomes unavoidable. You need AWS. You need managed services. You need the reliability, the SLAs, the global presence that only a hyperscaler can provide. The bill is just the price of doing business at that level.
For certain use cases, that story is true. But for WordPress image delivery specifically, it is not. Serving images is one of the simplest operations in computing. An image file sits on a server. A browser requests it. The server responds. There is no database query, no PHP execution, no dynamic logic. It is a static file transfer, and static file transfers do not require hyperscaler infrastructure to be fast, reliable, or globally distributed.
This guide builds the actual cost model for serving a million image requests a month using a commodity FTP storage server and a budget-tier CDN. We run the real numbers, compare them against what AWS charges for the same workload, and show exactly how a WordPress site using a proper media offload workflow can achieve scale-level image delivery without a scale-level infrastructure bill.
What 1 million image requests actually looks like in practice
Before getting into the cost comparison, it is worth anchoring the “1 million image requests” figure to real visitor traffic so the scale is clear. This is not an abstract enterprise benchmark. It is within reach of a mid-size content publisher, a busy WooCommerce store, or a photography portfolio with an engaged audience.
One million image requests per month is a realistic and not especially extraordinary volume for a site that has found its audience. It does not require viral traffic or enterprise-scale infrastructure. It is the normal operating load for a content-driven WordPress site with 20,000 to 80,000 monthly visitors, depending on how image-heavy each page is.
For each of these sites, the question of how those image requests get served, and what they cost to serve, is a real operational decision with measurable financial consequences. The difference between serving from AWS and serving from an independent FTP-plus-CDN stack is not trivial at this volume.
What AWS charges for 1 million image requests
To make the comparison concrete, we need to choose a realistic average image size. A well-optimized WordPress site using WebP format with reasonable quality settings serves images averaging 80KB to 120KB per request. We will use 100KB as our baseline, which is conservative for a site with quality photography and realistic for a well-optimized store or blog.
1M requests × 100KB average = 100GB of data transferred per month
S3 storage (50GB media library): 50GB × $0.023 = $1.15/mo
S3 GET requests: 1,000,000 × $0.0004 = $0.40/mo
S3 to CloudFront transfer (intra-AWS, free): $0
CloudFront distribution: 100GB × $0.0085 (US/EU) = $0.85/mo
CloudFront HTTPS requests: 1M × $0.0100/10k = $1.00/mo
WP Offload Media plugin (Developer, amortized): $24.92/mo
Total AWS stack: ~$28.32/month
Note that CloudFront is actually more cost-effective than serving directly from S3 to the internet in this case, because S3-to-CloudFront transfer within AWS is free. The CloudFront delivery cost is lower than S3 direct egress. This is the correct AWS architecture for WordPress image delivery at scale, and still the plugin license dominates the monthly cost.
If you are using S3 without CloudFront, the cost is worse. Direct S3 egress of 100GB at $0.09/GB after the free tier = approximately $8.10/mo in egress alone, bringing the total without a CDN to around $34/mo. The plugin license is unavoidable either way.
What the FTP-plus-CDN stack charges for the same workload
The independent infrastructure stack for this workload combines a commodity FTP storage server with a budget-tier CDN. BunnyCDN is the most commonly used option for this purpose and will serve as the CDN in this calculation, though Cloudflare or other providers would produce comparable or lower costs.
Same workload: 1M requests × 100KB = 100GB transferred, 50GB storage
FTP storage server (Hetzner Storage Box 1TB): $3.81/mo flat
BunnyCDN delivery: 100GB × $0.01 (North America/Europe) = $1.00/mo
BunnyCDN requests: 1M requests included in delivery pricing: $0
WP FTP Media plugin: ~$4–5/mo amortized
Total independent stack: ~$8.81–$9.81/month
The CDN cache hit rate: the variable that changes everything
The calculations above assume that 100% of image requests result in CDN delivery charges because every request results in a CDN data transfer. In practice, CDN cache behavior significantly affects real-world costs and performance, and it is worth understanding how it works at scale.
A CDN caches files at edge nodes globally. When a visitor requests an image, the CDN checks its nearest edge node first. If the file is cached there, it is served immediately from cache. No data is pulled from your origin storage server, and in some CDN pricing models, cached deliveries count differently or at lower rates than origin pulls. This is called the cache hit rate: the percentage of requests served from cache rather than from your origin.
A WordPress site where the same product images appear across dozens of pages, or a blog where header and thumbnail images repeat across archives and listings, will have naturally high cache hit rates because the same files are requested repeatedly. A CDN edge node that has already cached your homepage header image serves that image to every visitor instantly, at low or zero marginal data transfer cost depending on the CDN. For BunnyCDN specifically, cached pulls from edge do not incur additional origin pull charges. The more your images repeat across your site, the lower your effective per-request cost becomes.
For AWS CloudFront, the cache behavior also applies but the savings from high hit rates are lower because CloudFront’s pricing structure charges for edge delivery regardless of cache status. For BunnyCDN, origin pulls from your FTP server are charged at $0.01 per GB, while cached edge delivery is included in the zone pricing with no additional origin pull charge once cached. For image-heavy WordPress sites with consistent URL patterns, real-world effective CDN costs are often 20% to 40% lower than the theoretical 100% delivery calculation above.
What the complete WordPress infrastructure stack looks like
Implementing this at scale is not a complex engineering project. The stack has four components, each with a clear and simple role.
Your WordPress hosting server handles PHP execution, database queries, and dynamic page generation. It does not serve image files. This separation means your hosting server’s resources are focused entirely on the work only WordPress can do. At one million image requests per month, removing that static file load from your hosting server is the difference between comfortable headroom and resource exhaustion.
A commodity FTP or SFTP storage server holds all your media files. The CDN pulls from this origin when files are not cached. The server’s own bandwidth is rarely under significant pressure because the CDN absorbs the vast majority of direct delivery. A 1TB storage box from Hetzner, Contabo, or a comparable provider handles this role reliably at $4 to $8 per month.
BunnyCDN, Cloudflare, or a comparable provider delivers files from edge nodes close to your visitors. For a global audience, this dramatically improves load times compared to serving from a single origin server. According to BunnyCDN’s performance documentation, serving from a geographically proximate edge node typically reduces image load latency by 40% to 70% compared to serving from a single-region origin. The CDN is where your LCP improvements actually come from.
A plugin like WP FTP Media connects your WordPress installation to the FTP storage server. It handles the initial bulk sync of your existing library, intercepts new uploads and routes them to FTP automatically, and rewrites all image output URLs to point at your CDN domain. Your WordPress admin works exactly as before. Your visitors receive images from the CDN edge. The plugin is the operational layer that keeps everything synchronized and consistent.

What reliability actually looks like outside of AWS
The reliability argument for AWS is the most persistent one, and it deserves an honest response. AWS S3 has an SLA of 99.9% uptime for standard storage. Hetzner’s storage boxes are not backed by the same formally documented SLA, though their practical uptime track record for basic storage services is strong. BunnyCDN publishes a 99.9% uptime SLA for CDN delivery.
The important nuance here is that CDN uptime and origin storage uptime are separate concerns. If your FTP origin server experiences a brief outage, your CDN continues to serve cached copies of all your images to visitors. The CDN edge does not immediately stop serving files because the origin is temporarily unreachable. Files already cached at edge nodes continue to serve normally. For a WordPress site where images are static and change infrequently, CDN cache coverage means that brief origin outages are often completely invisible to visitors.
The scenario where origin storage uptime becomes critically important is when a visitor requests an image that has never been cached by the CDN, typically a brand-new upload or a rarely-visited page. In this case, a CDN cache miss results in an origin pull, and if the origin is down, the image fails to load. For most WordPress sites serving established content, this edge case affects a small percentage of total requests.
If your site requires formally documented uptime guarantees for storage, AWS or Cloudflare R2 with their enterprise SLAs is the appropriate choice. For the vast majority of WordPress sites, content sites, WooCommerce stores, portfolios, and agency client work, the practical reliability of a commodity storage server behind a CDN is more than adequate, and the cost difference of $220 or more per year represents a meaningful saving at no material performance or reliability cost.
Moving your existing library to this stack
Whether you are setting up this infrastructure from scratch or migrating from an existing S3 setup, the process follows the same sequence. The sync screen in a properly built FTP media plugin gives you visible, controllable progress during the initial library transfer, which is where most of the setup time is concentrated.


The economics of image delivery at scale do not require hyperscaler infrastructure to be favorable. They require the right architecture: static files on purpose-built storage, delivered through a CDN designed for that job, connected to WordPress through a plugin that handles the sync and URL rewriting reliably. That architecture is available today, it costs a fraction of what AWS charges for the same workload, and it delivers comparable or better performance for the vast majority of WordPress sites that fall into the one million image requests per month range.
The cloud giants built their billing models on the assumption that convenience justifies a premium. For WordPress image delivery specifically, the independent stack has reached a point where it is not just cheaper. It is simpler to set up, easier to understand, and produces a completely predictable monthly bill regardless of what your traffic does.
Serve images at scale. Pay like it is still 2012.
WP FTP Media connects your WordPress site to commodity FTP storage and your CDN of choice, delivering images at scale for a fraction of what AWS charges — with no egress fees, no per-request billing, and no surprise invoices.

Finally someone calling out those cloud giants! This guide proves you don't need AWS to handle a million images a month. Already saved me a ton of cash.
Dude, this just saved me thousands. no AWS needed
Hey folks, this guide just saved my department's site $200/month
Okay so I get the math on the storage/CDN side, but what about WordPress itself? If I'm running a well optimized site with WebP images at 80 120KB each, does the PHP processing or database calls add enough overhead to mess with these cost savings? Or is the image delivery really that isolated from the rest of the stack?