CDN vs Local Caching: 10 Questions [Solved]
Compare CDN and local caching to fix slow sites and avoid double-caching bugs. Practical answers for static content, dynamic pages, and cache invalidation.

On this page
- What does CDN caching actually cache?
- What does local server caching store?
- How do I decide which content belongs in which cache?
- Why does cache invalidation matter more with CDN?
- What is the double-caching bug?
- So what if my CDN is caching private data?
- How do I test if caching is working?
- Quick reference table
- Common pitfalls and how to avoid them
- When to skip CDN caching entirely
TL;DR — Key takeaways
- CDN caching delivers static assets from edge locations near users; local caching stores computed results on your origin server to reduce database load.
- Use CDN for images, CSS, JavaScript, and public files; use local caching for database queries, API responses, and session data.
- Combining both requires careful cache-key design to prevent stale content—purge CDN when origin cache updates, never cache private data at the edge.
- Cache invalidation is harder with CDN because you must purge multiple edge locations; local cache clears instantly but doesn't reduce bandwidth costs.
A customer once complained their homepage loaded fast but the blog was slow, even though both sat behind the same CDN. The issue was simple: images and stylesheets were cached at the edge with year-long TTLs, but every blog request hit the database because no local caching layer existed. CDN helps with delivery, not computation.
Understanding when to cache at the edge versus on your server prevents performance bottlenecks and the double-caching bugs that make stale content stick around after deployments. This FAQ answers the ten questions I see most often from hosting customers and junior engineers trying to configure cache layers correctly.
What does CDN caching actually cache?
CDN caching stores complete HTTP responses at edge servers—usually static files with predictable content. When a request arrives, the CDN checks if it already has a valid cached copy. If yes, it returns that copy without contacting your origin. If no, it fetches from origin, stores the response, then serves it.
The CDN decides what to cache based on HTTP headers you send: Cache-Control, Expires, and sometimes custom headers like Surrogate-Control. A response with Cache-Control: public, max-age=31536000 tells the CDN to store it for a year. A response with Cache-Control: private or no-cache tells the CDN not to cache it at all.
In support tickets I handled, the most common mistake was letting the CDN cache HTML pages that included logged-in user names or CSRF tokens. Always mark dynamic or personalized responses as private.
What does local server caching store?
Local caching on your origin server stores intermediate computation results—database query outputs, rendered HTML fragments, serialized objects, or expensive API calls. Technologies like Redis, Memcached, or even file-based caches sit between your application and the database.
When your app needs a blog post, it checks the local cache first. Cache hit means the post HTML is already in memory; return it instantly. Cache miss means query the database, render the template, store the result in cache with a TTL, then return it. The next hundred requests skip the database entirely.
- Redis or Memcached for key-value storage with millisecond lookup times
- Application-level caching in frameworks like Django, Rails, or WordPress with object cache plugins
- Opcode caches like OPcache that store compiled PHP bytecode to skip parsing on every request
How do I decide which content belongs in which cache?
Start with content type and update frequency. Static files that never change per user go in the CDN: images, CSS, JavaScript libraries, fonts, PDFs. These benefit most from edge delivery because they're large, requested often, and identical for everyone.
Dynamic content that varies by user or changes frequently stays in local cache: session data, shopping cart contents, personalized recommendations, real-time inventory counts. Putting these in the CDN either leaks private data or causes stale responses because you can't purge fast enough.
Gray area items like blog posts or product pages can use both. Cache the rendered HTML locally for ten minutes to handle traffic spikes, and let the CDN cache it for an hour with a public header. When you publish an update, purge both layers.
Why does cache invalidation matter more with CDN?
Local cache invalidation is instant—you clear Redis or restart the app server and the stale data is gone. CDN invalidation requires API calls to each edge location, and propagation can take seconds to minutes depending on the provider.
The delay creates a window where some users see old content and others see new content, which confuses QA teams and creates duplicate support tickets. Worse, if your purge request fails or you forget an edge location, stale copies persist until the TTL expires.
Three patterns reduce this pain. One, use versioned URLs like style.v2.css so each deploy creates a new cache key. Two, set shorter TTLs on content that changes often, accepting the cache-miss cost. Three, configure your CDN to revalidate with If-Modified-Since so it can confirm freshness without a full refetch.
What is the double-caching bug?
Double-caching happens when both your local cache and the CDN store the same response with independent TTLs and no coordination. You update the local cache, but the CDN still serves the old version for another hour. Or you purge the CDN, but the origin cache still returns stale data, which the CDN immediately re-caches.
I've seen this break checkout pages where the local cache stored a cart total, the CDN cached the entire checkout HTML, and when inventory changed the local cache updated but users kept seeing the old price until the CDN TTL expired.
- Fix it by using different cache keys: version strings in asset URLs for CDN, database record timestamps for local cache
- Chain purges: when local cache invalidates, trigger a CDN purge via API in the same transaction
- Never cache the same response with overlapping lifetimes unless you control both expiration triggers
So what if my CDN is caching private data?
Check the response headers your app sends. Run curl -I https://yoursite.com/account and look for Cache-Control. If you see public or no explicit cache directive, the CDN might store it.
Add Cache-Control: private, no-store to any response containing user-specific information—account pages, order history, API endpoints that return personal data. The private directive tells the CDN to skip caching; the no-store directive tells browsers not to cache either.
For APIs, also check the Vary header. If responses differ based on cookies or authorization tokens, include Vary: Cookie or Vary: Authorization so the CDN creates separate cache entries per user. Better yet, don't cache authenticated endpoints at the edge at all—serve them from origin with local caching only.
How do I test if caching is working?
Use curl to inspect headers and timing. First request shows a cache miss:
curl -I https://yoursite.com/image.jpg | grep -E 'X-Cache|Age|Cache-Control'
You'll see X-Cache: MISS or Age: 0. Second request should show X-Cache: HIT and Age: 5 (seconds since cached). If Age keeps resetting to zero, the CDN isn't caching.
For local cache, add debug logging to your app that prints whether each request hit or missed the cache layer. In production, monitor cache hit ratio—above 80 percent means your cache keys and TTLs are well-tuned. Below 50 percent means too many unique keys or TTLs set too short.
Quick reference table
Here's a comparison table for common scenarios:
- Static assets (images, JS, CSS): CDN with 1-year TTL, versioned URLs to bust cache on deploy
- Blog posts and marketing pages: CDN with 1-hour TTL + local cache with 10-minute TTL, purge both on publish
- Product catalog pages: CDN with 5-minute TTL + local cache with 1-minute TTL for inventory updates, Vary: Cookie if prices differ per user
- User dashboards and account pages: No CDN caching (Cache-Control: private), local cache with session-specific keys
- API responses for public data: CDN with short TTL (1-5 minutes), include ETag for revalidation
- API responses for authenticated users: No CDN caching, local cache keyed by user ID with 30-second TTL
- Database query results: Local cache only with TTL matching update frequency (1-60 minutes depending on table)
- Session data and shopping carts: Local cache only with TTL matching session timeout, never send to CDN
Common pitfalls and how to avoid them
Forgetting to set Vary headers when content differs by user agent or geography causes mobile users to see desktop assets or vice versa. Add Vary: User-Agent if you serve different HTML to mobile, or let the CDN normalize it.
Caching error pages with long TTLs makes your site look broken even after you fix the issue. A 500 error cached for an hour means users see failures long after your database comes back online. Set Cache-Control: no-cache, no-store on all 4xx and 5xx responses.
Not monitoring cache hit ratios means you won't notice when a code change breaks caching. I've seen a single extra query parameter added to URLs drop hit rates from 90 percent to 10 percent because each URL became unique. Track metrics and alert on sudden drops.
When to skip CDN caching entirely
If your traffic is regional and your origin server sits in the same data center as most users, CDN edge caching adds latency instead of removing it. A server in Frankfurt serving mostly German traffic doesn't need edge locations in Tokyo and São Paulo.
Highly dynamic sites where every response is personalized also see little benefit from edge caching. A social feed that differs for each user can't be cached at the edge—local caching of the database queries and rendered components gives better results.
Content behind authentication should stay on origin unless you use a CDN that supports signed URLs or token-based access control. Putting a login-walled document on a public CDN edge without access checks is a data leak waiting to happen.
Quick troubleshooting checklist
- Identify which content is static (CDN candidate) versus dynamically generated (local cache candidate)
- Configure Cache-Control headers to prevent CDN from caching private or user-specific responses
- Set shorter TTLs for frequently updated content, longer TTLs for stable assets like logos and libraries
- Implement cache-key strategies that include version strings or content hashes to avoid serving stale files
- Test cache behavior with curl -I to verify headers before rolling out to production
- Document your purge workflow so support staff know how to clear cache after deployments
- Monitor cache hit ratios to confirm both layers are reducing origin load as expected
FAQ
When should I use CDN caching instead of local server caching?
Use CDN caching for static assets like images, CSS, JavaScript, fonts, and downloadable files that don't change per user. CDN edge servers deliver these files from locations geographically closer to visitors, cutting latency and bandwidth costs. Use local server caching for dynamic content like database query results, computed HTML fragments, or API responses that vary by user or update frequently. Local cache sits on your origin server and reduces CPU and database load without the propagation delay of purging a distributed CDN.
Can I use both CDN and local caching together without bugs?
Yes, but you need to coordinate cache keys and purge logic. Serve static assets through the CDN with long TTLs, and cache dynamic content locally with shorter expiration times. The common mistake is caching the same response twice with different invalidation triggers—when your local cache updates, stale CDN copies persist until TTL expires. Fix this by appending version strings or content hashes to asset URLs so the CDN treats each version as a new object, or by triggering CDN purges via API when the origin cache clears.
What is the difference between edge caching and origin caching?
Edge caching stores copies at CDN points of presence distributed globally, serving requests without hitting your origin server. Origin caching stores data in memory or disk on your own server to avoid expensive operations like database queries or template rendering. Edge cache reduces latency and bandwidth; origin cache reduces CPU and database load. A request that misses edge cache still benefits from origin cache when it reaches your server, so both layers work together to minimize resource usage at different points in the delivery path.
Related articles
- Hosting OperationsSelf-Hosted App Deployment Fails? Check DNS, SSL, Reverse Proxy, and Logs FirstTroubleshoot failed self-hosted app deployments by checking DNS, SSL, reverse proxy routing, container status, logs, and ports.
- Hosting OperationsSelf-Hosted PaaS on a VPS: What to Check Before Installing Coolify, Dokploy, or CapRoverA hosting support checklist for preparing a VPS before installing self-hosted PaaS tools like Coolify, Dokploy, or CapRover.
- Hosting OperationsLinux Server Security Lessons from the Arch Linux Malware Package IncidentPractical Linux server security checklist for VPS admins after package malware concerns, with safe checks, rollback steps, and support guidance.
- Hosting OperationsAWS Lightsail Hong Kong VPS Latency: Practical Hosting Guide for IndonesiaLearn how to test AWS Lightsail Hong Kong VPS latency, compare regions, migrate safely, and troubleshoot hosting performance.
- Hosting OperationsCloudflare Tomorrow Watchlist: A Practical Hosting Operations GuidePractical Cloudflare troubleshooting checklist for DNS, SSL, caching, WAF, origin health, safe testing, and rollback planning.
- Hosting OperationsNetwork Safety Checklist for AI Agent Skills in Hosting OperationsAudit AI agent skills safely with network checks, secret protection, sandbox testing, rollback steps, and hosting support troubleshooting guidance.