How the Internet Actually Works: Networks, Protocols, and the Web Under the Hood
Section 14 of 17

How Web Caching Works to Speed Up Websites

Caching: Making the Web Fast

We've spent a lot of time tracing the journey data takes from server to browser. Now let's talk about something even better: eliminating that journey entirely when possible. That's what caching does.

Caching is one of the most powerful performance optimizations available to web developers, and HTTP has a sophisticated built-in caching mechanism that most developers barely scratch the surface of.

The Caching Hierarchy: Where Content Lives

Here's the thing about caching: it's not just one place. When you request a resource, the browser and network check multiple "layers" of caches, one after another, before ever bothering your origin server:

  1. Browser cache — Your local machine's memory and disk
  2. Proxy/gateway caches — Corporate firewalls, ISP proxies
  3. CDN edge cache — Geographically distributed servers
  4. Origin server — Your actual web server, where the buck stops

Each layer is like a relay race of speed. A resource stuck only on your origin server gets slower the farther away the user is. A resource cached at the CDN edge? That's accessible in 5–50ms from basically anywhere in the world.

Browser Cache: Your Local Copy

The browser cache stores copies of recently fetched resources — CSS, JavaScript, images, even HTML. When a resource is requested again, the browser does a quick check: "Do I have a fresh copy?" If the answer is yes, it serves from cache. Zero network round trips. Instant response.

The browser cache is local and private — only you see it. It sticks around across page navigations and browser restarts (until it fills up or you manually nuke it). For a typical user, the browser cache is often the difference between a website feeling snappy on the second visit versus sluggish if every page load had to download all its CSS and JavaScript from scratch.

Cache-Control: The Core Directive

The Cache-Control header is how servers talk to browsers and proxies about caching. It's the primary tool you've got to control where things get cached and for how long.

Cache-Control: max-age=31536000, immutable

This says: cache this resource for 1 year (31,536,000 seconds) and never re-validate it. The immutable flag is a promise — "This URL will never change; if you've got it, use it forever." You'd use this for versioned static files where the URL changes when the content does (like /assets/app-abc123def456.js). This is the dream scenario for static assets. Zero revalidation overhead.

Cache-Control: no-cache

Here's where things get confusing: this doesn't mean "don't cache." It means "cache it, but always check with the server before using your cached copy." The browser stores the resource but asks the server every time: "Is my copy still good?" using ETags or Last-Modified headers (we'll get to those in a moment). If your copy is still fresh, the server responds with 304 Not Modified, and the browser uses what it has. You save the bandwidth of re-downloading, but you pay the cost of that round-trip.

Cache-Control: no-store

This one actually means don't cache. The resource never gets stored on disk or in memory. You use this for highly sensitive data — authentication tokens, bank account details, medical records — anything that shouldn't sit around on someone's machine.

Cache-Control: private, max-age=300

Cache this only in the browser (that's the private part) for 5 minutes. Shared caches — CDNs, proxies, all of that — are forbidden from touching it. You'd use this for pages with per-user content: a logged-in user's profile, their shopping cart, saved articles. The public directive does the opposite — it tells CDNs and proxies "You can cache this too."

Cache-Control: max-age=300, must-revalidate

Cache for 5 minutes. After that, the cache is stale and must be revalidated before serving. No exceptions. Without must-revalidate, a cache might serve you slightly stale content if the server is unreachable. This adds a safety valve.

A Practical Example: Static Assets vs. Dynamic Pages

Let's say you're building a news website. Different parts need different strategies:

  • Homepage HTML — Changes hourly as new articles get posted → Cache-Control: no-cache (revalidate every load)
  • CSS stylesheet — Versioned filename like /css/main-v2.cssCache-Control: max-age=31536000, immutable (cache forever)
  • User's saved articles page — Personal, unique to each user → Cache-Control: private, no-cache (revalidate every time, and definitely don't let a CDN cache it)
  • The login formCache-Control: no-store (don't cache this anywhere)

The pattern: long-lived immutable caches for versioned assets, revalidatable caches for content that might change, no caching for sensitive information.

ETags and Conditional Requests: Smart Revalidation

Here's where it gets elegant. ETags let the browser be smart about revalidation. Instead of downloading the entire file again, the server gives the browser a fingerprint. The browser can then ask "Is it still the same?" without pulling down all the data.

The server sends an ETag header with a unique identifier for the current version of a resource (often a hash of the content):

HTTP/1.1 200 OK
ETag: "686897696a7c876b7e"
Cache-Control: no-cache
Content-Length: 8324

[8324 bytes of JSON data]

Next time the browser wants this resource, it includes the ETag in an If-None-Match header:

GET /data.json HTTP/1.1
If-None-Match: "686897696a7c876b7e"

If nothing's changed, the server just says:

HTTP/1.1 304 Not Modified

No body, no data. The browser uses its cached copy. For a 100KB file, you've just saved 100KB of bandwidth and kept only ~200 bytes of headers. Scale that across thousands of requests and you're looking at real savings.

If the resource has changed, the server responds normally with 200 OK and sends the new content.

There's also Last-Modified and If-Modified-Since, which work the same way but with timestamps instead of hashes. The server includes Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT, and the browser includes If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT on revalidation. ETags are generally better though — they work even if a server's clock is off or content gets generated at different times but happens to be identical.

Common Misconception: The Cost of Revalidation

A lot of developers think: "If I use no-cache, my assets get revalidated constantly, which must be slow." But that's not quite right.

Yes, a revalidation request needs a round-trip to the server. But — and this is the important part — if nothing's changed, the response is tiny (just a 304 header). It's orders of magnitude cheaper than re-downloading the whole file. For resources that don't change often (like a homepage), this trade-off is excellent. You get freshness without bleeding bandwidth.

For resources that change very frequently, revalidating on every request could get expensive. That's where longer max-age values come in, or architectural changes like moving to an API that only sends changes instead of the full content.

CDNs: Edge Caching for Everyone

A Content Delivery Network (CDN) is a geographically distributed bunch of caching servers operated by a third party. Services like Cloudflare, AWS CloudFront, Fastly, Akamai — they all operate CDNs. When you put your website "behind" one:

  1. A user in Tokyo requests your home page
  2. Instead of their request traveling all the way to your data center in Virginia (150ms round-trip), it hits a CDN edge server in Tokyo (5–20ms)
  3. If that edge server has a cached copy of the page, boom — instant response. Your Virginia server never gets bothered.
  4. If not, the CDN fetches from your origin server, caches it according to your Cache-Control headers, and serves it. Next Tokyo request is instant.

This is transformational for global web performance. A page that loads in 1 second for someone in your server's city might take 5+ seconds for someone on the other side of the world without a CDN. Physics makes this hard to avoid: there's only so fast light travels, and routing isn't perfect. But with a CDN, performance is consistently fast everywhere. Users in Sydney, São Paulo, and Stockholm all get your content from nearby servers instead of from a single central location.

Beyond Caching: What CDNs Actually Do

Modern CDNs do way more than just cache files:

  • DDoS protection — CDN edge servers absorb attack traffic, filtering out malicious requests before they even reach your origin
  • TLS/SSL termination — Encryption happens between the user and the nearby CDN edge, not across an ocean, which slashes latency for secure connections
  • Automatic compression — Gzip, Brotli, and other compression applied right at the edge
  • Image optimization — Automatic resizing, format conversion (WebP for modern browsers), quality adjustment based on connection speed
  • Minification — Automatic minification of CSS, JavaScript, HTML
  • Request routing — Route requests to your healthiest origin server if you've got multiple data centers
  • URL-based caching rules — Cache /images/* forever, /api/* for 60 seconds, /admin/* not at all

Caching Rules for Different Content Types

Different parts of your application need different cache strategies. There's no one-size-fits-all answer:

Static assets (CSS, JavaScript, images with versioned URLs) — Cache-Control: max-age=31536000, immutable

  • Cached at browser and CDN indefinitely
  • The risk: stale code. You mitigate this by versioning URLs — when content changes, the filename changes, so old and new versions coexist peacefully

API responses that aren't user-specific — Cache-Control: public, max-age=60

  • Cached at browser and CDN for 60 seconds
  • Great for data like product catalogs, news feeds, weather
  • Trade-off: users see 60-second-old data. Balance freshness against server load

Per-user content (logged-in profile, shopping cart, saved items) — Cache-Control: private, max-age=300

  • Cached only in the browser, never at the CDN
  • Nobody else can see your data
  • CDNs respect the private directive and won't cache it

Authentication tokens and sensitive dataCache-Control: no-store

  • Never cached anywhere
  • Maximum security; you pay for it with every round-trip

HTML pages that change frequentlyCache-Control: no-cache

  • Revalidated every load using ETags
  • Users see new content quickly
  • Minimal bandwidth waste if the content hasn't actually changed
Decision tree for choosing cache headers based on content type
Cache strategy decision tree: Different content types demand different Cache-Control policies

For Django Apps Specifically

Django developers often wonder where caching fits into their world:

  • Static files (served by Django during development, by CDN or nginx in production) — Use max-age=31536000, immutable with versioned filenames. Django's ManifestStaticFilesStorage does this automatically
  • Template-rendered HTML — Usually no-cache with ETags. Django's @cache_page decorator helps, or use conditional decorators like @condition(etag_func=...) to generate ETags based on model state
  • API responses (JSON) — Slap Cache-Control headers on the response. Libraries like django-rest-framework make this straightforward with @api_view and response metadata
  • User sessions — Django's session middleware handles this. Sessions aren't cached by CDNs (they're typically in a private cookie or stored in a database)

Here's a practical example — a Django view returning JSON for product data that doesn't depend on the user:

from django.views.decorators.cache import cache_page

@cache_page(60)  # Cache for 60 seconds
def api_products(request):
    products = Product.objects.all().values()
    return JsonResponse(list(products), safe=False)

This automatically sets the right Cache-Control headers so both the browser and CDN cache for 60 seconds.


Summary: The Caching Mindset

The trick to effective caching is matching cache behavior to what the content actually is:

  • Content that never changes → Go aggressive with long-lived caching (max-age=31536000)
  • Content that changes but isn't user-specific → Moderate caching with revalidation (max-age=300, no-cache)
  • Per-user content → Private browser caching only (private, no-cache)
  • Sensitive data → No caching (no-store)

Get this right, and your site scales to millions of users with minimal server load. Get it wrong, and you're either serving stale content or burning CPU regenerating the same response for every single request.

This is why understanding HTTP caching is such a valuable skill across any web role — whether you're a frontend developer trying to optimize page load times, a backend engineer reducing database queries, or an infrastructure engineer designing CDN rules. It's foundational stuff that compounds over time.