Cloud CDN
Cloud CDN caches responses of an external Application Load Balancer at Google's edge locations, so repeat requests are answered without reaching the backend. The GCP Web App Blueprint turns it on for the single-page application (SPA) of every stage, in front of the Cloud Run service that serves the built files.
What it does
Cloud CDN is a setting of a backend service. Its cache mode decides what is cacheable: CACHE_ALL_STATIC caches static content types and anything the origin marks cacheable, and honors the origin's Cache-Control headers. A default TTL applies to static content that arrives without caching headers, a max TTL caps any TTL, and serve while stale keeps answering from cache while the origin is unreachable. Negative caching caches error responses briefly. An invalidation removes cached objects by path ahead of their expiry.
How BuiltForProd uses it
The frontend-service unit enables Cloud CDN on the backend service <prefix>-frontend-be of the SPA's load balancer:
resource "google_compute_backend_service" "frontend" {
project = var.project_id
name = "${var.name_prefix}-frontend-be"
description = "SPA ${local.host} (Cloud Run ${google_cloud_run_v2_service.frontend.name})"
load_balancing_scheme = "EXTERNAL_MANAGED"
protocol = "HTTPS"
compression_mode = "AUTOMATIC"
security_policy = google_compute_security_policy.this.id
enable_cdn = true
cdn_policy {
cache_mode = "CACHE_ALL_STATIC"
default_ttl = var.cdn_default_ttl
client_ttl = var.cdn_default_ttl
max_ttl = 86400
serve_while_stale = 86400
negative_caching = true
# The provider default, set explicitly: the provider requires one of this or cache_key_policy.
signed_url_cache_max_age_sec = 3600
}
backend {
group = google_compute_region_network_endpoint_group.frontend.id
}
log_config {
enable = true
sample_rate = 1.0
}
}
cdn_default_ttl is 3600 seconds by default; the max TTL and serve-while-stale windows are a day. Freshness is decided by the origin: the nginx image of the Cloud Run service sends public, max-age=31536000, immutable for the hashed build assets and no-cache for index.html and the client-side routes, so a new release shows on the next page load while the assets stay cached for a year.
Invalidation after a deploy. The code pipeline (scripts/deploy-frontend.sh, run by cd-frontend.yml for dev and cd-release.yml for staging and prod) deploys the new revision, records its tag, then invalidates /* on the URL map <prefix>-frontend-lb. The pipeline's identity holds a custom role made for that one job, acme_frontend_cdn_invalidator, with only compute.urlMaps.get and compute.urlMaps.invalidateCache in the stage project. The pipeline requests the invalidation asynchronously, because that role cannot read the operation it starts.
Protection. Cached and uncached requests both pass the stage's Google Cloud Armor policy, attached to the same backend service. Cost in the code comment: cache egress and lookups, on top of the load balancer's forwarding rules.
Terms you will see
| Term | Meaning |
|---|---|
CACHE_ALL_STATIC | Cache static content types and whatever the origin marks cacheable. |
| Default TTL | How long content without caching headers stays cached (3600 seconds). |
| Serve while stale | Answer from cache for up to a day when the origin cannot be reached. |
| Invalidation | Removing cached paths early; the pipeline invalidates /* after deploys. |
| Hashed assets | Build files whose names change with their content, safe to cache a year. |
Where to read more
- GCP Web App Blueprint overview for the SPA's path.
- Cloud Load Balancing for the load balancer the CDN sits on.
- Cloud Run for the origin service.