Cloud/ Google Cloud / Networking / Cloud CDN: Caching Right at the Edge, in Front of Your Load Balancer

GCP Google Cloud Platform Guide 3 of 10 54 guides ยท updated 2026

Guides to BigQuery, Vertex AI, GKE, Dataflow, and the rest of Google's data- and AI-first cloud โ€” written for engineers shipping real workloads.

Cloud CDN: Caching Right at the Edge, in Front of Your Load Balancer

Every request your backend doesnโ€™t have to serve is a request that canโ€™t be slow, canโ€™t add load, and canโ€™t fail because your backend happened to be under pressure at that moment. Thatโ€™s the entire value proposition of a CDN, and Cloud CDNโ€™s specific implementation detail worth understanding is that itโ€™s not a bolted-on separate product โ€” itโ€™s a caching layer built directly into GCPโ€™s Global External Application Load Balancer, sitting at Googleโ€™s edge points of presence around the world, caching responses close to where users actually are.

Because itโ€™s this tightly integrated, enabling it is close to a checkbox โ€” but getting real value from it, and avoiding the genuinely bad failure mode (serving stale or, worse, wrong content to the wrong users), requires understanding cache keys, cache modes, and invalidation deliberately rather than flipping the switch and hoping for the best.


How Cloud CDN Sits in the Request Path

User request
โ”‚
โ–ผ
Nearest Google edge point of presence
โ”‚
โ–ผ
Cache lookup (based on cache key)
โ”‚
โ”Œโ”€โ”€โ”ดโ”€โ”€โ”
HIT MISS
โ”‚ โ”‚
โ”‚ โ–ผ
โ”‚ Forward to origin (your backend service)
โ”‚ โ”‚
โ”‚ โ–ผ
โ”‚ Origin responds, response cached per policy
โ”‚ โ”‚
โ–ผ โ–ผ
Serve response directly from edge โ€” no origin round trip

A cache hit is served entirely from Googleโ€™s edge infrastructure without your backend ever seeing the request โ€” this is the actual mechanism behind both the latency improvement (edge is physically closer to the user than your origin) and the load reduction (your origin only handles cache misses, not every single request).


Cache Modes: Who Decides What Gets Cached

Terminal window
# CACHE_ALL_STATIC โ€” cache static content automatically based on file extension/content-type
gcloud compute backend-services update my-backend-service \
--global \
--cache-mode=CACHE_ALL_STATIC
# USE_ORIGIN_HEADERS โ€” respect your origin's Cache-Control headers exactly
gcloud compute backend-services update my-backend-service \
--global \
--cache-mode=USE_ORIGIN_HEADERS
# FORCE_CACHE_ALL โ€” cache everything regardless of headers (use with real caution)
gcloud compute backend-services update my-backend-service \
--global \
--cache-mode=FORCE_CACHE_ALL

CACHE_ALL_STATIC is the sensible default for content thatโ€™s obviously cacheable (images, CSS, JS, fonts) without requiring your application to set explicit headers. USE_ORIGIN_HEADERS puts your backend in full control, honoring Cache-Control, Expires, and validators your application already sets โ€” the right choice once your application has deliberate caching headers and you want the CDN to respect them exactly rather than guess. FORCE_CACHE_ALL overrides everything, including responses your application explicitly marked as private or no-cache, and is genuinely dangerous for any backend serving personalized or authenticated content โ€” this mode has caused real incidents where one userโ€™s personalized response got cached and served to a different user entirely.


Cache Keys: The Setting That Determines What Counts as โ€œThe Same Requestโ€

The cache key is what the CDN uses to decide whether two requests should return the same cached response โ€” get this wrong and you either fragment your cache uselessly or, more dangerously, serve one userโ€™s response to another.

Terminal window
gcloud compute backend-services update my-backend-service \
--global \
--cache-key-include-protocol \
--cache-key-include-host \
--cache-key-query-string-whitelist=category,page

By default, the full query string is part of the cache key, which means /products?utm_source=twitter and /products?utm_source=email are cached as entirely separate entries despite returning identical content โ€” a common and entirely avoidable cache-fragmentation problem. Whitelisting only the query parameters that actually affect the response (category, page in this example) while excluding tracking parameters (utm_source, fbclid) dramatically improves cache hit rate for pages where marketing tracking parameters are common but irrelevant to the actual content served.


Cookies present a similar, often overlooked trap: if a response varies by a cookie value (a session ID, an A/B test bucket) but the cache key doesnโ€™t account for it, one userโ€™s cookie-personalized response can end up cached and served to a different user entirely โ€” precisely the kind of subtle misconfiguration that doesnโ€™t show up in testing but becomes a real problem the first time itโ€™s triggered by two users hitting the same edge cache in close succession.


Signed URLs: Caching Content That Still Needs Access Control

A common misconception is that caching and access control are incompatible โ€” Cloud CDN supports signed URLs specifically to cache content that still requires time-limited, per-user authorization:

from google.cloud.compute_v1 import UrlMapsClient
import datetime, base64, hashlib, hmac
def sign_url(url, key_name, key_value, expiration):
expiration_ts = int((datetime.datetime.now() + expiration).timestamp())
url_to_sign = f"{url}?Expires={expiration_ts}&KeyName={key_name}"
decoded_key = base64.urlsafe_b64decode(key_value)
signature = hmac.new(decoded_key, url_to_sign.encode(), hashlib.sha1).digest()
encoded_sig = base64.urlsafe_b64encode(signature).decode()
return f"{url_to_sign}&Signature={encoded_sig}"

This lets you cache a video file or a licensed asset at the edge while still requiring a valid, time-limited signature on every request โ€” the CDN caches the underlying content once, but each user still needs their own valid signed URL to actually retrieve it, and an expired or invalid signature is rejected regardless of whether the content is cached.


Cache Invalidation: The Hard Part Every CDN Shares

Terminal window
# Invalidate a specific cached path
gcloud compute url-maps invalidate-cdn-cache my-url-map \
--path="/images/logo.png"
# Invalidate everything under a path prefix
gcloud compute url-maps invalidate-cdn-cache my-url-map \
--path="/api/*"

Invalidation is the operation every caching system struggles with, and Cloud CDN is no exception โ€” a wildcard invalidation of a broad path is a blunt instrument that also evicts content that didnโ€™t actually change, temporarily increasing origin load as everything under that path gets re-fetched. The better long-term pattern, once traffic and content volume justify the engineering effort, is versioned asset URLs (app.a3f92c.js rather than app.js) where a content change produces a genuinely new URL โ€” this sidesteps invalidation entirely for versioned assets, since the old URL simply stops being referenced rather than needing active cache eviction.


Cache Hit vs Cache Miss: The Latency Difference Made Concrete

Abstract descriptions of โ€œfasterโ€ donโ€™t stick the way a concrete timing comparison does.

Origin (us-central1)Google Edge (Tokyo PoP)User (Tokyo)Origin (us-central1)Google Edge (Tokyo PoP)User (Tokyo)Cache MISS (first request)~150-200ms round tripTotal: origin round trip + edge deliveryCache HIT (subsequent requests, any user near this edge)Total: ~10-30ms, no origin round trip at allGET /images/hero.jpgForward (cache miss) โ€” crosses oceanResponse + Cache-Control headerResponse (cached at edge for future requests)GET /images/hero.jpgServed directly from edge cache

The first request from any user near a given edge location pays the full origin round-trip cost โ€” thereโ€™s no way around that for genuinely new content. Every subsequent request from any user served by that same edge location, though, gets the cached response with no origin involvement at all, which is both the latency win (tens of milliseconds instead of hundreds) and the load-reduction win (the origin literally never sees these requests). This is why cache hit ratio is the single most important CDN metric to track โ€” a low hit ratio means most requests are still paying the full origin round-trip cost, and the CDN isnโ€™t delivering the benefit itโ€™s theoretically capable of.


Real-World Use Case: E-Commerce Product Catalog

A retail site with a large product catalog is a strong Cloud CDN case, with a real nuance worth calling out: product images and static assets cache aggressively and benefit enormously, while product pricing and inventory data โ€” often rendered on the same page โ€” must never be served stale from cache. The correct architecture separates these concerns: static assets served with long cache TTLs and versioned URLs, while pricing/inventory data is either excluded from caching entirely (marked no-cache at the origin) or fetched via a separate, uncached API call from client-side JavaScript after the cached page shell loads. Conflating these โ€” caching a page that embeds both static and dynamic data as a single cached unit โ€” is exactly how a stale-price incident happens.


Best Practices


Common Mistakes

Caching personalized content by accident. A page that includes a logged-in userโ€™s name or account-specific data, cached without careful cache-key configuration, can serve one userโ€™s personalized page to a completely different user โ€” this is a genuine security incident, not just a bug.

Not whitelisting query parameters, leading to cache fragmentation. Every unique combination of query parameters, including irrelevant tracking parameters, becomes a separate cache entry by default, silently tanking effective hit rate.

Treating invalidation as instantaneous and universally propagated. Invalidation takes a short but real amount of time to propagate across all edge locations โ€” assuming itโ€™s instant everywhere can cause confusing โ€œitโ€™s still showing the old versionโ€ reports immediately after invalidating.


Frequently Asked Questions

Does Cloud CDN work with Cloud Storage-backed backends, not just Compute Engine or GKE? Yes โ€” a Cloud Storage bucket can be a backend behind the load balancer, and Cloud CDN caches its content the same way, which is a common pattern for serving static websites or large media files efficiently.

How do I know if Cloud CDN is actually improving performance? Cloud CDN provides cache hit ratio metrics in Cloud Monitoring โ€” tracking this over time, and specifically after cache-key tuning changes, is the concrete way to verify whether configuration changes are actually working rather than assuming they are.

Can Cloud CDN cache POST requests? By default, caching applies to GET (and sometimes HEAD) requests, since POST typically represents a state-changing operation that shouldnโ€™t be cached. Some configurations allow caching specific POST responses, but this requires deliberate, careful configuration rather than being a default behavior.

Is Cloud CDN a separate cost from the load balancer? Yes, Cloud CDN has its own pricing dimensions (cache egress, cache fill, cache lookup requests) distinct from the load balancerโ€™s own charges โ€” worth modeling both together when estimating total cost, particularly for very high-traffic sites.

Whatโ€™s the maximum time content can stay cached at the edge? This is controlled entirely by your Cache-Control max-age (or the cache modeโ€™s default behavior for CACHE_ALL_STATIC), not a platform-imposed ceiling โ€” content can be cached for seconds or for months depending entirely on what your headers or configuration specify.

Does enabling Cloud CDN change how my application needs to handle HTTPS? No additional application-side changes are needed โ€” Cloud CDN operates transparently behind the same load balancer already terminating TLS, so the caching layer doesnโ€™t require your backend to handle encryption differently.


Summary

Cloud CDNโ€™s integration directly into the load balancer makes it deceptively easy to enable and genuinely easy to misconfigure into either uselessness (fragmented cache keys tanking hit rate) or an actual security incident (personalized content cached and served across users). The real work is in cache-key design and cache-mode selection matched deliberately to what your application actually serves โ€” static assets and dynamic personalized content need fundamentally different caching treatment, and the biggest wins come from being explicit about that distinction rather than trusting default heuristics to sort it out correctly on their own โ€” and from actually watching the cache hit ratio metric afterward to confirm the configuration is doing what you intended.