How to get Google Cloud CDN working properly for a Singapore-hosted site
How to get Google Cloud CDN working properly for a Singapore-hosted site
The first attempt looked promising. The Cloud CDN status dashboard showed everything green. The origin was a standard Compute Engine instance in the asia-southeast1 region — the Google Cloud Singapore data center. Cache hits were supposed to go up. Page load times for a visitor in Jakarta or Manila were supposed to drop by hundreds of milliseconds.
Instead, the site’s bounce rate climbed. The admin panel, which should have been excluded from caching entirely, started returning stale data. A promotional banner that was supposed to disappear at midnight on a Friday was still showing on Saturday afternoon in Kuala Lumpur. And the worst part — no obvious error messages in the Cloud Logging console. Just green lights and a site that felt slower than before.
This is the gap between theory and practice when using Google Cloud CDN with a Singapore origin. The documentation is thorough. The pricing calculator gives plausible numbers. But the actual setup involves a series of decisions that the quickstart guides tend to gloss over, and each one can break the experience for users across Southeast Asia.
What Cloud CDN actually caches — and what it doesn’t
The most common mistake — the one that probably broke that promotional banner — is assuming Cloud CDN caches everything by default. It doesn’t. Cloud CDN is a pass-through cache that relies entirely on the origin server’s HTTP response headers. If the origin doesn’t explicitly tell Cloud CDN to cache a resource, Cloud CDN will forward the request to origin every single time, serving no caching benefit at all.
For a typical Singapore-hosted WordPress site behind a Google Cloud HTTP(S) Load Balancer, this means checking the Cache-Control and Expires headers that WordPress and any caching plugin generate. A common default is Cache-Control: no-cache on dynamically generated pages, which is correct for logged-in admin sessions but wrong for public pages. The fix is configuring the WordPress caching layer — WP Super Cache, W3 Total Cache, or a similar plugin — to emit Cache-Control: public, max-age=3600 on pages that should be cached for an hour.
Without that header, Cloud CDN sees every page request as uncacheable. The load balancer forwards it to the origin instance. The user in Bangkok gets the same latency as if no CDN existed.
The backend bucket setting that trips up first-time users
Google Cloud CDN operates at the backend bucket or backend service level, not at the load balancer level. This is a detail that trips up first-time users: configuring the load balancer’s frontend rules does nothing for caching. The cache settings live one layer deeper.
In the Cloud Console, under Network Services > Load Balancing, selecting the backend bucket or backend service that serves static assets reveals a “Cloud CDN” toggle. Enabling it is the first step. The second is setting the Cache Mode. The options are:
- Use origin headers — Cloud CDN respects whatever
Cache-ControlandExpiresheaders the origin sends. This is the safest option for mixed content, but requires the origin to send correct headers. - Force cache all — Overrides the origin headers and caches everything for a specified TTL. Risky for dynamic content but useful for a bucket of versioned static assets where the user controls the filenames.
- Set cache TTL based on content type — A hybrid approach, not commonly used for most Singapore setups.
For a site with both static assets (images, CSS, JavaScript) and dynamic pages, “Use origin headers” is the better starting point. It allows the WordPress caching layer to decide what gets cached and for how long, while letting the admin panel remain uncached.
The TTL should reflect the content’s update frequency. A news site publishing every hour might set a one-hour TTL. An e-commerce site with frequent inventory changes might use 15 minutes. A blog that updates weekly could safely set 24 hours. The trade-off is between freshness and origin load — a shorter TTL means more requests hit the Singapore origin; a longer TTL means stale content for users who visit just after an update.
The stale banner problem — and how to stop it
That stale promotional banner problem happened because the checkout page was cached. The origin sent Cache-Control: public for a page that should have been private. The result: a user in Jakarta completed a purchase, but the confirmation page showed old pricing.
The solution is cache invalidation rules at the load balancer level. Cloud CDN allows creating “cache key” policies that include or exclude specific URL patterns. For a Singapore WordPress site, the common exclusions are:
/wp-admin/*— Never cache the admin panel./wp-login.php— Session-based, must be dynamic./checkout/*— Payment and cart states are user-specific./cart/*— Same reason./my-account/*— User dashboards should be dynamic.
The exclusion is done by creating a “Custom header” or “Cache key” policy that strips cookies from the cache key for public pages, but the cleaner approach is to use Google Cloud Armor or a URL redirect rule to bypass the CDN entirely for those paths. Alternatively, the origin can send Cache-Control: private or Cache-Control: no-store for those URLs. If the origin header says private, Cloud CDN will not cache it.
Testing from Manila — not from your desk
A developer testing from an office in Singapore with fiber internet will see fast load times whether or not Cloud CDN is working. The latency difference between hitting a local CDN edge and hitting the origin a few kilometers away is imperceptible. The real test is from locations where the CDN matters: Manila, Bangkok, Jakarta, Ho Chi Minh City.
Google Cloud CDN has points of presence in those cities, but only if the cache is actually populated. The first request from a new location incurs a cold start — the CDN forwards the request to the Singapore origin, caches the response, and serves it. Subsequent requests from that same city hit the edge cache.
To test whether caching is working, use curl with a location-aware proxy or a service like WebPageTest, selecting a test location in Manila. Look for the X-Cache header in the response. A value of HIT means the CDN served the content from its edge. MISS means it forwarded to origin. If MISS persists for the same URL across multiple requests from the same city, the cache headers or TTL need adjustment.
A mismatch between the Cache-Control max-age and the Cloud CDN TTL can also cause early eviction. Cloud CDN will respect the lower of the two values. If the origin says max-age=3600 (one hour) but the CDN is set to three minutes, the CDN evicts the content after three minutes, forcing a re-fetch from Singapore.
Egress costs — where the savings disappear
Cloud CDN is not free. Google Cloud charges a per-gigabyte rate for cache egress, and those rates vary by region. Serving a user in Manila through Cloud CDN is cheaper than serving them directly from the Singapore origin, but it’s not as cheap as serving from a free third-party CDN.
The approximate breakdown for a Singapore-hosted site:
- Cloud CDN egress to Asia (excluding China): around $0.08 per GB
- Direct egress from Singapore Compute Engine to Asia: around $0.12 per GB
- Cloud CDN cache fill (origin to edge, internal network): $0.00
The savings per gigabyte is about 30%, but only if the cache hit ratio is above 70%. A poorly configured CDN with a low hit ratio — say, 30% — costs more than no CDN at all, because every cache miss still incurs the full egress cost from the origin, plus the CDN’s own ingress and processing fees.
Monitoring this requires the Cloud CDN metrics dashboard. The “Cache Hit Ratio” metric should be above 80% for static assets. If it’s lower, the TTL is too short, or the cache key includes too many unique variables (like session IDs or query strings) that fragment the cache.
Invalidation — the wrong way and the slightly less wrong way
The last piece of the puzzle is cache invalidation — how to force Cloud CDN to discard old versions of a page after a content update. This is critical for a Singapore news site or blog that publishes frequently.
Cloud CDN offers two methods:
- Manual invalidation — In the Cloud Console, under Cloud CDN > Invalidation, entering a URL pattern like
/2024/09/*clears all cached responses under that path. This takes 5 to 15 minutes to propagate globally. - API-based invalidation — Using the Cloud CDN API or the
gcloudCLI to invalidate specific URLs. This is automatable from a WordPress publishing workflow or a CI/CD pipeline.
The mistake is invalidating too broadly. Running a wildcard invalidation on the entire site every time a single post is published clears the cache for every page, forcing the next visitor from every Asian city to wait for a re-fetch from Singapore. This makes the CDN worse than useless — it adds latency for everyone while the cache refills.
A better approach: invalidate only the URL of the updated page and the homepage. WordPress can trigger this programmatically. A simple shell script running after a wp post update can call gcloud compute url-maps invalidate-cdn-cache with the specific path. The rest of the cache stays warm.
The Singapore origin will still handle the first request for that page from each city. But within minutes, the edge cache repopulates, and subsequent visitors get the fast response. The pattern is the same every time: publish, invalidate, wait, serve.

📷 Photos: Bojána Noémi Molnár (Pexels), Cyrill (Pexels)