Static Content Hosting Pattern: CDN & Blob Storage
TL;DR
Static Content Hosting is a cloud design pattern where unchanging files (HTML, CSS, JavaScript, images, videos) are served directly from object storage services like S3 or Azure Blob Storage, bypassing application servers entirely. This eliminates compute costs for serving static assets, improves performance through CDN integration, and scales effortlessly to millions of requests. Cheat sheet: Store static files in object storage → Enable public access or signed URLs → Add CDN layer → Configure cache headers → Profit from 10-100x cost reduction versus serving from EC2/VMs.
The Analogy
Think of static content hosting like a vending machine versus a restaurant. A restaurant (traditional web server) requires staff, a kitchen, utilities, and overhead to prepare and serve every meal, even if you’re just asking for a bottled water. A vending machine (object storage + CDN) sits there 24/7 with pre-packaged items, requires no staff, costs pennies to operate, and can serve thousands of customers simultaneously. You wouldn’t hire a chef to hand you a sealed bottle of water, and you shouldn’t spin up an EC2 instance to serve a JPEG that never changes. The vending machine model is 100x cheaper and infinitely more scalable for items that don’t need preparation.
Why This Matters in Interviews
Static Content Hosting appears in almost every system design interview because modern applications are typically split into static frontends (React/Vue/Angular SPAs) and dynamic APIs. Interviewers want to see that you understand the fundamental cost and performance difference between serving static files from compute instances versus object storage. This pattern comes up when designing: content-heavy platforms (Netflix thumbnails, Medium articles), SaaS dashboards, e-commerce sites, or any system with a web frontend. Strong candidates immediately separate static and dynamic concerns, propose object storage + CDN, and discuss cache invalidation strategies. Weak candidates try to serve everything through application servers, missing a 10-100x cost optimization opportunity.
Core Concept
Static Content Hosting is the practice of storing and serving unchanging files directly from cloud object storage services (AWS S3, Azure Blob Storage, Google Cloud Storage) rather than from compute instances running web servers. Static content includes HTML files, CSS stylesheets, JavaScript bundles, images, videos, PDFs, fonts—anything that doesn’t require server-side processing for each request. The pattern emerged as cloud providers realized that object storage, originally designed for backup and archival, could serve HTTP requests directly at a fraction of the cost of running EC2 instances with Apache or Nginx.
The economics are compelling: serving 1TB of data from S3 costs about $90/month in bandwidth charges, while serving the same traffic from EC2 instances might require $500-2000/month in instance costs plus bandwidth. Object storage is designed for 99.99% availability and 11 nines of durability, making it more reliable than any single compute instance. When combined with a CDN like CloudFront or Cloudflare, static content is cached at edge locations worldwide, reducing latency from hundreds of milliseconds to single-digit milliseconds for most users.
This pattern is foundational to modern web architecture. Single-page applications (SPAs) built with React, Vue, or Angular compile down to static HTML/CSS/JS bundles that can be hosted entirely on S3. The application makes API calls to backend services for dynamic data, but the application code itself is static. Companies like Netflix, Airbnb, and Stripe use this pattern to serve their web frontends, achieving massive scale while keeping infrastructure costs low. The separation of static and dynamic concerns also enables independent scaling, deployment, and caching strategies for each layer.
How It Works
Step 1: Build and Bundle Static Assets. Your frontend build process (Webpack, Vite, Next.js export) compiles source code into optimized static files. A typical React app produces an index.html entry point, chunked JavaScript bundles with content hashes (main.a3f2b1c.js), CSS files, and assets like images and fonts. Content hashing ensures that when you update code, the filename changes, automatically busting caches. The build output is a directory of files ready for upload.
Step 2: Upload to Object Storage. Use the cloud provider’s CLI or SDK to sync your build directory to an S3 bucket (or equivalent). Set appropriate permissions—typically public-read for truly public content, or private with signed URLs for authenticated access. Configure the bucket for static website hosting, which tells S3 to serve index.html for directory requests and handle 404 errors. Set cache-control headers on objects: long durations (1 year) for hashed assets that never change, short durations (5 minutes) for index.html which references those assets.
Step 3: Add CDN Layer. Point a CloudFront distribution (or Cloudflare, Fastly, Akamai) at your S3 bucket as the origin. The CDN caches content at 200+ edge locations worldwide. When a user in Tokyo requests your site, CloudFront serves it from the Tokyo edge location after the first request, eliminating the round-trip to your S3 bucket in us-east-1. Configure the CDN to respect cache-control headers from S3, add security headers (HSTS, CSP), and enable HTTP/2 or HTTP/3 for faster multiplexing.
Step 4: Configure DNS and SSL. Point your domain (app.example.com) to the CDN distribution using a CNAME record. The CDN handles SSL/TLS termination with a certificate from AWS Certificate Manager or Let’s Encrypt. Users connect via HTTPS to the CDN, which fetches from S3 over AWS’s internal network. This setup provides end-to-end encryption without managing certificates on origin servers.
Step 5: Implement Cache Invalidation. When you deploy a new version, upload the new files to S3 (new hashed filenames for JS/CSS, same filename for index.html). Create a CDN invalidation for index.html and any other files that changed names. Because JS/CSS bundles have new hashes, browsers automatically fetch the new versions when they load the updated index.html. The old versions remain cached until they expire naturally, supporting users with the old index.html still open.
Step 6: Monitor and Optimize. Track CDN cache hit rates (should be >90%), origin request rates, and bandwidth costs. Use CloudWatch or equivalent to monitor S3 request counts and 4xx/5xx errors. Optimize by enabling compression (gzip/brotli) at the CDN, using WebP or AVIF for images, and lazy-loading non-critical assets. For large media files, consider S3 Transfer Acceleration or multi-region replication to reduce latency for global users.
Static Content Hosting Request Flow
graph LR
User["User Browser"]
CDN["CDN Edge Location<br/><i>CloudFront</i>"]
S3[("S3 Bucket<br/><i>Origin Storage</i>")]
Build["Build Process<br/><i>Webpack/Vite</i>"]
Build --"1. Generate hashed assets<br/>main.a3f2b1c.js"--> S3
Build --"2. Upload with<br/>Cache-Control headers"--> S3
User --"3. GET /index.html"--> CDN
CDN --"4. Cache miss<br/>fetch from origin"--> S3
S3 --"5. Return content +<br/>Cache-Control: max-age=300"--> CDN
CDN --"6. Cache and serve"--> User
User --"7. GET /main.a3f2b1c.js"--> CDN
CDN --"8. Cache hit<br/>(90%+ requests)"--> User
The complete request flow showing how static assets move from build process to S3, then through CDN to users. Note that index.html has a short TTL (300s) while hashed JS/CSS files achieve 90%+ cache hit rates with long TTLs, minimizing origin requests.
Key Principles
Principle 1: Separate Static from Dynamic. Static content (HTML, CSS, JS, images) should never be served by application servers. This separation allows independent scaling—your API servers scale based on business logic complexity, while static content scales infinitely through CDN caching. Example: Stripe’s dashboard is a React SPA hosted on S3/CloudFront. The static frontend makes API calls to Stripe’s backend services. When Stripe experiences a traffic spike (Black Friday), the CDN handles the frontend load without touching application servers. The API servers scale independently based on transaction volume, not page views.
Principle 2: Immutable Deployments with Content Hashing. Static assets should be immutable—once deployed, never modified. Use content-based hashing in filenames (main.a3f2b1c.js) so that any code change produces a new filename. This enables aggressive caching (1 year) without fear of serving stale content. Example: Facebook’s JavaScript bundles use content hashes. When they deploy a bug fix, the new bundle gets a new hash. Users with the old page open continue using the old bundle (no mid-session breakage), while new page loads fetch the new bundle. The old bundle remains cached and accessible until its TTL expires.
Principle 3: Cache-Control Headers Drive Behavior. The Cache-Control header determines how long content is cached at the CDN and in browsers. Use long durations (max-age=31536000, immutable) for hashed assets, short durations (max-age=300) for HTML entry points, and no-cache for content that must always be fresh. Example: Medium sets Cache-Control: max-age=31536000 on their CSS/JS bundles (with hashes) but Cache-Control: max-age=60 on article HTML. This means the application shell caches for a year, but article content refreshes every minute, balancing performance and freshness.
Principle 4: CDN as Security Boundary. The CDN should be the only public entry point; the origin (S3 bucket) should be private and accessible only to the CDN. Use Origin Access Identity (OAI) in AWS or equivalent to prevent direct S3 access. This protects against DDoS attacks (CDN absorbs traffic), prevents bandwidth theft, and allows the CDN to enforce security headers. Example: Netflix’s web player assets are in private S3 buckets. CloudFront uses OAI to fetch from S3. If someone discovers the S3 bucket URL, direct access is denied. All traffic flows through CloudFront, which applies rate limiting, geo-blocking, and DDoS protection.
Principle 5: Multi-Region for Global Performance. For truly global applications, replicate static content to multiple regions and use latency-based routing or a multi-CDN strategy. This reduces the CDN-to-origin latency for cache misses and provides disaster recovery. Example: Airbnb replicates their static assets to S3 buckets in us-east-1, eu-west-1, and ap-southeast-1. CloudFront uses S3 Transfer Acceleration and origin failover. If the primary region fails, CloudFront automatically fetches from a secondary region. This setup achieves <100ms TTFB globally, even for cache misses.
Immutable Deployments with Content Hashing
graph TB
subgraph "Version 1.0 Deployment"
V1_HTML["index.html<br/>references main.a3f2b1c.js"]
V1_JS["main.a3f2b1c.js<br/>Cache: 1 year"]
end
subgraph "Version 1.1 Deployment (Bug Fix)"
V2_HTML["index.html<br/>references main.f7e9d2a.js"]
V2_JS["main.f7e9d2a.js<br/>Cache: 1 year"]
V1_JS_Keep["main.a3f2b1c.js<br/>still cached"]
end
User_Old["User with old page<br/>still works"]
User_New["New user<br/>gets latest"]
V1_HTML -."references".-> V1_JS
V2_HTML -."references".-> V2_JS
User_Old --"continues using"--> V1_JS_Keep
User_New --"fetches fresh"--> V2_HTML
User_New --"then fetches"--> V2_JS
Content hashing enables zero-downtime deployments where old and new versions coexist. Users mid-session continue with v1.0 while new visitors get v1.1, eliminating the need for cache invalidation on JS/CSS files and preventing mid-session breakage.
Deep Dive
Types / Variants
Variant 1: Public Static Website Hosting. The simplest form: an S3 bucket configured for static website hosting with public-read access. No authentication required; anyone with the URL can access content. When to use: Marketing sites, documentation, open-source project pages, public blogs. Pros: Simplest setup, lowest cost ($0.023/GB for S3 storage, $0.09/GB for data transfer), no authentication overhead. Cons: No access control, vulnerable to hotlinking (others embedding your images), limited to public content. Example: The React documentation (react.dev) is hosted on Vercel’s edge network, which uses a similar pattern—static files served from a global CDN with no origin servers.
Variant 2: Private Content with Signed URLs. S3 bucket is private; access requires pre-signed URLs generated by your application server. The server authenticates the user, generates a time-limited signed URL, and returns it to the client. The client fetches directly from S3 using the signed URL. When to use: User-uploaded content (profile pictures, documents), premium content (paid courses, e-books), internal tools. Pros: Fine-grained access control, content remains private in S3, offloads bandwidth from application servers. Cons: Application server must generate URLs (adds latency), URLs expire (requires refresh logic), signed URLs can be shared (use short TTLs). Example: Dropbox uses signed URLs for file downloads. When you click a file, Dropbox’s API generates a signed S3 URL valid for 4 hours. Your browser downloads directly from S3, not through Dropbox servers, saving them massive bandwidth costs.
Variant 3: CDN with Origin Shield. A CDN configuration where an additional caching layer (Origin Shield) sits between edge locations and the origin. Edge locations fetch from the shield, which fetches from S3. This collapses multiple edge requests into a single origin request. When to use: High-traffic sites with many edge locations, origins with rate limits, reducing origin costs. Pros: Reduces origin requests by 50-90%, lowers S3 request costs, protects origin from thundering herd. Cons: Adds another hop (slight latency increase on cache misses), additional CDN cost, more complex invalidation. Example: Twitch uses Origin Shield for video thumbnails. When a popular stream starts, thousands of edge locations request the thumbnail. Without Origin Shield, that’s thousands of S3 requests. With Origin Shield, it’s one request from the shield to S3, then cached responses to all edges.
Variant 4: Edge Compute with Static Assets. Combine static hosting with edge functions (CloudFront Functions, Cloudflare Workers) that run at CDN edge locations. Functions can modify responses, perform A/B testing, or add authentication without hitting origin. When to use: Personalization (user-specific content), A/B testing, security (JWT validation), URL rewriting. Pros: Sub-millisecond latency for dynamic logic, no origin server needed for simple logic, scales automatically. Cons: Limited execution time (1-50ms), limited memory, more complex debugging, vendor lock-in. Example: The New York Times uses CloudFront Functions to perform A/B testing on article layouts. The function checks a cookie, selects a variant, and modifies the HTML response—all at the edge, with no origin request. This enables experimentation without deploying new static builds.
Variant 5: Multi-CDN Strategy. Use multiple CDN providers (CloudFront + Cloudflare + Fastly) with DNS-based load balancing. Route users to the best-performing CDN based on geography, health checks, or cost. When to use: Mission-critical applications (finance, healthcare), global enterprises, CDN vendor redundancy. Pros: No single point of failure, leverage best-of-breed features from each CDN, negotiate better pricing. Cons: Complex configuration, higher operational overhead, increased costs, cache fragmentation (lower hit rates). Example: Large gaming companies like Riot Games use multi-CDN strategies for game patches. They route North American users to CloudFront, European users to Fastly, and Asian users to regional CDNs, achieving optimal performance and redundancy for 100GB+ downloads.
Private Content with Signed URLs Architecture
sequenceDiagram
participant User
participant App as App Server<br/>(Authentication)
participant S3 as S3 Bucket<br/>(Private)
User->>App: 1. Request file<br/>(with auth token)
App->>App: 2. Validate user<br/>& permissions
App->>App: 3. Generate signed URL<br/>(expires in 1 hour)
App->>User: 4. Return signed URL<br/>https://s3...?signature=xyz
User->>S3: 5. GET file with<br/>signed URL
S3->>S3: 6. Validate signature<br/>& expiration
S3->>User: 7. Return file content<br/>(direct download)
Note over User,S3: Bandwidth flows directly<br/>from S3 to user, not through app server
Signed URL flow for private content like user uploads or premium files. The app server handles authentication and generates time-limited URLs, but actual file transfer happens directly between S3 and the user, offloading bandwidth from application servers while maintaining access control.
Trade-offs
Tradeoff 1: Cost vs. Performance (CDN vs. Direct S3). Option A (Direct S3): Serve content directly from S3 without a CDN. Costs: $0.09/GB data transfer, $0.0004 per 1000 GET requests. Latency: 50-300ms depending on user location relative to S3 region. Option B (S3 + CDN): Add CloudFront. Costs: $0.085/GB data transfer (slightly cheaper), $0.0075 per 10,000 requests (more expensive), but 90%+ cache hit rate means 10x fewer origin requests. Latency: 5-50ms from edge locations. Decision framework: Use direct S3 only for internal tools or low-traffic sites (<1000 requests/day). For any user-facing application, the CDN pays for itself through improved performance and reduced origin load. The latency improvement alone (250ms → 10ms) is worth the marginal cost increase.
Tradeoff 2: Cache Duration vs. Freshness (Long TTL vs. Short TTL). Option A (Long TTL, 1 year): Set Cache-Control: max-age=31536000 on all assets. Pros: Maximum cache hit rate, minimal origin requests, lowest cost. Cons: Requires content hashing for updates, complex invalidation for non-hashed files, risk of stale content if not managed correctly. Option B (Short TTL, 5 minutes): Set Cache-Control: max-age=300. Pros: Content updates propagate quickly, simpler deployment process, no need for content hashing. Cons: 10x more origin requests, higher costs, higher latency (more cache misses). Decision framework: Use long TTLs for all assets with content hashes (JS, CSS, images). Use short TTLs (5-60 minutes) only for entry points (index.html) that reference hashed assets. Never use short TTLs for large files (videos, binaries) due to cost and performance impact.
Tradeoff 3: Public vs. Private Buckets (Simplicity vs. Security). Option A (Public bucket): Enable public-read on S3 bucket, allow direct access. Pros: Simplest setup, no signed URLs needed, works with any CDN, lowest latency (no auth overhead). Cons: Anyone can access content, vulnerable to hotlinking, no access control, compliance issues for sensitive data. Option B (Private bucket with signed URLs): Keep bucket private, generate signed URLs from application server. Pros: Fine-grained access control, prevents hotlinking, meets compliance requirements, can revoke access. Cons: Application server in the request path (latency), signed URLs expire (UX friction), more complex architecture. Decision framework: Use public buckets for truly public content (marketing sites, open documentation). Use private buckets with signed URLs for user-generated content, premium content, or anything requiring authentication. For hybrid scenarios, use a public bucket for application code and a private bucket for user data.
Tradeoff 4: Single Region vs. Multi-Region (Cost vs. Resilience). Option A (Single region): Store all content in one S3 region (e.g., us-east-1). Pros: Simplest setup, no replication costs, single source of truth, easier to manage. Cons: Higher latency for distant users (CDN helps but cache misses still hit one region), single point of failure, no disaster recovery. Option B (Multi-region replication): Replicate content to 3+ regions with S3 Cross-Region Replication. Pros: Lower latency for cache misses, disaster recovery, compliance (data residency), higher availability. Cons: Replication costs ($0.02/GB), storage costs in multiple regions, more complex invalidation, eventual consistency issues. Decision framework: Use single region for most applications—CDN caching makes origin location less critical. Use multi-region for: global enterprises with strict SLAs, applications with >1M daily users across continents, or compliance requirements (GDPR data residency). The CDN’s global presence usually eliminates the need for multi-region origins.
Tradeoff 5: Versioned Deployments vs. In-Place Updates (Rollback vs. Simplicity). Option A (Versioned deployments): Deploy each version to a new S3 prefix (v1.2.3/), update CDN origin to point to new prefix. Pros: Instant rollback (change origin back), multiple versions coexist, canary deployments (route 10% to new version), audit trail. Cons: More complex deployment scripts, storage costs for old versions, manual cleanup needed. Option B (In-place updates): Overwrite files in the same S3 location, invalidate CDN cache. Pros: Simpler deployment, lower storage costs, no version management. Cons: No easy rollback (must redeploy old code), risk of partial updates (some files new, some old), invalidation takes 5-10 minutes to propagate. Decision framework: Use versioned deployments for production applications where rollback speed matters. Use in-place updates for low-stakes environments (staging, internal tools) where simplicity trumps rollback capability. Many teams use blue-green deployments with versioned S3 prefixes, switching traffic instantly via DNS or CDN configuration changes.
Cache Duration Strategy Decision Tree
flowchart TB
Start["Static Asset"]
HasHash{"Filename has<br/>content hash?"}
IsEntry{"Entry point<br/>(index.html)?"}
IsMedia{"Large media<br/>file (>1MB)?"}
NeedsAuth{"Requires<br/>authentication?"}
Start --> HasHash
HasHash -->|Yes| LongTTL["Cache-Control:<br/>max-age=31536000, immutable<br/><b>1 year cache</b>"]
HasHash -->|No| IsEntry
IsEntry -->|Yes| ShortTTL["Cache-Control:<br/>max-age=300<br/><b>5 minute cache</b>"]
IsEntry -->|No| IsMedia
IsMedia -->|Yes| MediumTTL["Cache-Control:<br/>max-age=86400<br/><b>1 day cache</b>"]
IsMedia -->|No| NeedsAuth
NeedsAuth -->|Yes| NoCache["Cache-Control:<br/>private, no-cache<br/><b>Always validate</b>"]
NeedsAuth -->|No| MediumTTL
Decision tree for setting cache TTLs on static assets. Content-hashed files get aggressive 1-year caching, entry points get short TTLs to enable quick updates, and large media files balance freshness with bandwidth costs. This strategy maximizes cache hit rates while maintaining content freshness.
Common Pitfalls
Pitfall 1: Serving index.html with Long Cache TTL. Developers set Cache-Control: max-age=31536000 on index.html, thinking it’s just another static file. Why it happens: Misunderstanding of the SPA loading process. Index.html is the entry point that references hashed JS/CSS bundles. If index.html is cached for a year, users never fetch the new bundles, even though the bundles have new hashes. How to avoid: Set Cache-Control: max-age=0, must-revalidate (or max-age=300 for some caching) on index.html. Set long TTLs only on content-hashed assets. Use the “cache-busting entry point” pattern: index.html always fresh, everything it references cached forever. Verify by deploying a change and checking that new users see it immediately.
Pitfall 2: Not Using Content Hashing for Assets. Developers deploy main.js and style.css without content hashes, relying on cache invalidation to update users. Why it happens: Default build configurations or unfamiliarity with cache-busting techniques. Without hashing, you must invalidate CDN caches on every deploy, which takes 5-10 minutes and isn’t guaranteed to reach all users (some may have browser caches). How to avoid: Enable content hashing in your build tool (Webpack’s [contenthash], Vite’s default behavior). Verify that production builds produce filenames like main.a3f2b1c.js. This allows aggressive caching (1 year) without invalidation. Only invalidate index.html, which is small and fast to propagate. Test by deploying, checking the new hash in index.html, and confirming the old hash is still accessible (for users mid-session).
Pitfall 3: Making S3 Bucket Publicly Accessible When Using CDN. Developers enable public-read on the S3 bucket and point CloudFront at it, allowing direct S3 access. Why it happens: Following tutorials that skip security hardening or not understanding Origin Access Identity (OAI). This allows users to bypass the CDN (hitting your S3 bandwidth costs directly), enables hotlinking, and exposes your origin URL. How to avoid: Keep the S3 bucket private. Create a CloudFront Origin Access Identity and grant it s3:GetObject permission in the bucket policy. Verify by trying to access the S3 URL directly (should get AccessDenied) while the CloudFront URL works. This forces all traffic through the CDN, enabling rate limiting, DDoS protection, and cost control.
Pitfall 4: Ignoring CORS Configuration for API Calls. The static frontend makes API calls to a different domain (api.example.com), but CORS headers aren’t configured, causing browser errors. Why it happens: Developers test locally (same origin) or don’t understand browser same-origin policy. When the frontend is on app.example.com and the API is on api.example.com, browsers require CORS headers (Access-Control-Allow-Origin) from the API. How to avoid: Configure your API servers to return appropriate CORS headers. For S3-hosted frontends calling APIs, the API must include Access-Control-Allow-Origin: https://app.example.com (or * for public APIs). Test by opening browser DevTools, checking the Network tab for CORS errors, and verifying the preflight OPTIONS request succeeds. Consider using a reverse proxy or API Gateway to handle CORS centrally.
Pitfall 5: Not Compressing Assets. Developers upload uncompressed files to S3, wasting bandwidth and slowing page loads. A 2MB JavaScript bundle could be 500KB with gzip or 400KB with brotli. Why it happens: Assuming the CDN or browser handles compression automatically, or not configuring compression in the build pipeline. While CDNs can compress on-the-fly, pre-compressing at build time is more efficient and reduces CDN CPU costs. How to avoid: Enable compression in your build tool (Webpack’s CompressionPlugin, Vite’s vite-plugin-compression). Upload both uncompressed and compressed versions (.js and .js.gz) to S3. Configure CloudFront to serve the .gz version when the client sends Accept-Encoding: gzip. Verify by checking response headers (Content-Encoding: gzip) and comparing file sizes. This reduces bandwidth costs by 70-80% and improves load times proportionally.
Pitfall 6: Invalidating Too Frequently or Too Broadly. Developers invalidate the entire CDN cache (/) on every deploy, or invalidate dozens of paths unnecessarily. Why it happens: Lack of understanding of cache invalidation costs and propagation time. CloudFront charges $0.005 per invalidation path (first 1000 free per month), and invalidations take 5-15 minutes to propagate globally. How to avoid: Use content hashing to eliminate most invalidations. Only invalidate index.html and any non-hashed files (robots.txt, sitemap.xml). Use specific paths (/index.html) rather than wildcards (/). For high-frequency deployments, consider versioned deployments (switching the CDN origin) instead of invalidations. Monitor invalidation costs and frequency; if you’re invalidating >100 paths per deploy, your caching strategy needs rework.
Math & Calculations
Calculation 1: Cost Comparison (EC2 vs. S3 + CDN). Assume a web application serving 10TB of static content per month to 1 million users. EC2 approach: Run 3x m5.large instances ($0.096/hour = $210/month) behind a load balancer ($16/month) for high availability. Data transfer out: 10TB × $0.09/GB = $921. Total: $210 + $16 + $921 = $1,147/month. S3 + CloudFront approach: S3 storage: 50GB × $0.023/GB = $1.15. S3 requests: 10M GET requests × $0.0004/1000 = $4. CloudFront data transfer: 10TB × $0.085/GB = $870 (assumes 90% cache hit rate, so only 1TB from S3). CloudFront requests: 100M requests × $0.0075/10,000 = $75. Total: $1.15 + $4 + $870 + $75 = $950/month. Savings: $197/month (17%), plus elimination of server management, automatic scaling, and higher availability. The savings increase with traffic—at 100TB/month, EC2 costs $10K+ while S3+CDN costs $8.5K.
Calculation 2: Cache Hit Rate Impact on Costs. With 10TB of user traffic and a 90% cache hit rate, only 1TB hits the origin (S3). At 90% hit rate: Origin requests: 1TB × $0.09/GB = $90. CDN data transfer: 10TB × $0.085/GB = $850. Total: $940. At 70% hit rate: Origin requests: 3TB × $0.09/GB = $270. CDN data transfer: 10TB × $0.085/GB = $850. Total: $1,120. At 95% hit rate: Origin requests: 0.5TB × $0.09/GB = $45. CDN data transfer: 10TB × $0.085/GB = $850. Total: $895. Formula: Total cost = (Traffic × CDN_price) + (Traffic × (1 - hit_rate) × Origin_price). Improving hit rate from 70% to 95% saves $225/month (20%). Optimize hit rate by: setting long cache TTLs, pre-warming cache for popular content, using Origin Shield to collapse requests.
Calculation 3: Latency Improvement from CDN. User in Tokyo accessing content from us-east-1 S3 bucket. Direct S3: Round-trip time (RTT) Tokyo ↔ Virginia: ~180ms. TCP handshake: 180ms. TLS handshake: 180ms. HTTP request/response: 180ms. Total: ~540ms for first byte. With CloudFront: RTT Tokyo ↔ Tokyo edge: ~5ms. TCP handshake: 5ms. TLS handshake: 5ms. HTTP request (cache hit): 5ms. Total: ~20ms for first byte. Improvement: 540ms → 20ms (27× faster). For cache misses, CloudFront uses optimized routes and persistent connections to origin, reducing the miss penalty to ~200ms instead of 540ms. Formula: Latency_with_CDN = Edge_RTT + (Miss_rate × Origin_latency). With 90% hit rate: 0.9 × 20ms + 0.1 × 200ms = 18ms + 20ms = 38ms average, still 14× faster than direct S3.
Calculation 4: Signed URL Expiration Time. Application generates signed URLs for private content. Security vs. UX tradeoff: Short expiration (5 minutes) is more secure but requires frequent regeneration. Long expiration (24 hours) is more convenient but increases risk if URLs leak. Formula: Risk = P(leak) × Exposure_time × Impact. If P(leak) = 0.01% per URL and Impact = $100 (cost of unauthorized access), then: 5-minute expiration: Risk = 0.0001 × (5/1440 days) × $100 = $0.0003 per URL. 24-hour expiration: Risk = 0.0001 × 1 × $100 = $0.01 per URL. Decision: Use 1-hour expiration for most use cases (balance of security and UX). Use 5-minute expiration for highly sensitive content (financial documents, PII). Use 24-hour expiration for low-risk content (profile pictures, public-ish files). Implement refresh logic: when the URL is 80% expired, fetch a new one in the background.
Calculation 5: Multi-Region Replication Costs. Replicate 1TB of static content to 3 regions (us-east-1, eu-west-1, ap-southeast-1). Storage costs: 1TB × 3 regions × $0.023/GB = $70.65/month. Replication costs: 1TB initial replication × 2 destinations × $0.02/GB = $20.48 one-time. Ongoing updates: 10GB/day × 30 days × 2 destinations × $0.02/GB = $12.29/month. Total first month: $70.65 + $20.48 + $12.29 = $103.42. Ongoing monthly: $70.65 + $12.29 = $82.94. Benefit: Reduced latency for cache misses (200ms → 50ms for users in EU/Asia), disaster recovery (if us-east-1 fails, traffic routes to eu-west-1). ROI calculation: If 10% of traffic is cache misses and 50% of users are outside the US, the latency improvement affects 5% of requests. If each 150ms latency reduction increases conversion by 0.1%, and monthly revenue is $100K, the benefit is $100K × 0.001 = $100/month. The $83/month cost is justified if latency impacts business metrics. For most applications, single-region + CDN is sufficient; multi-region is for global enterprises with strict SLAs.
Cost Comparison: EC2 vs S3+CDN at Scale
graph TB
subgraph "EC2 Approach: $1,147/month"
EC2_LB["Load Balancer<br/>$16/mo"]
EC2_1["m5.large #1<br/>$210/mo"]
EC2_2["m5.large #2<br/>$210/mo"]
EC2_3["m5.large #3<br/>$210/mo"]
EC2_BW["Data Transfer<br/>10TB × $0.09<br/>= $921/mo"]
EC2_Total["<b>Total: $1,147/mo</b><br/>+ management overhead"]
EC2_LB --> EC2_1 & EC2_2 & EC2_3
EC2_1 & EC2_2 & EC2_3 --> EC2_BW
EC2_BW --> EC2_Total
end
subgraph "S3 + CDN Approach: $950/month"
S3_Storage["S3 Storage<br/>50GB × $0.023<br/>= $1.15/mo"]
S3_Requests["S3 Requests<br/>10M × $0.0004<br/>= $4/mo"]
CDN_Transfer["CDN Transfer<br/>10TB × $0.085<br/>= $870/mo"]
CDN_Requests["CDN Requests<br/>100M × $0.0075<br/>= $75/mo"]
CDN_Total["<b>Total: $950/mo</b><br/>+ zero management"]
S3_Storage --> CDN_Transfer
S3_Requests --> CDN_Transfer
CDN_Transfer --> CDN_Requests
CDN_Requests --> CDN_Total
end
Savings["<b>Savings: $197/mo (17%)</b><br/>+ automatic scaling<br/>+ higher availability<br/>+ no server management"]
EC2_Total -.-> Savings
CDN_Total -.-> Savings
Cost breakdown comparing traditional EC2-based hosting versus S3+CDN for 10TB/month traffic. The S3+CDN approach saves 17% on direct costs while eliminating server management, providing automatic scaling, and achieving higher availability. Savings increase dramatically at higher traffic volumes (100TB/month: $10K vs $8.5K).