November 19, 2025

Imagine launching a major redesign of your website, everything seems perfect during deployment—only to discover your users are still seeing outdated styles. This seemingly contradictory situation happened partly due to how content delivery networks (CDNs) like Fastly utilize modern caching strategies like stale-while-revalidate. While it’s a powerful technique for performance, it can sometimes undermine the freshness of critical assets such as CSS files when purging strategies aren’t properly aligned. In this article, we’ll dive into an intriguing real-world scenario involving Fastly, unexpected stale CSS, and how implementing a surrogate-key strategy provided an elegant and robust solution.

TL;DR

Even after purging CSS files on Fastly’s CDN, users were served outdated versions due to Fastly’s use of the stale-while-revalidate cache directive. This behavior, meant to improve resilience and performance, led to time gaps where stale assets were delivered post-purge. The solution involved developing a granular surrogate-key strategy that let developers invalidate groups of assets more effectively and eliminate race conditions during revalidation. As a result, asset freshness became deterministic, and the rollout of new site styles became far more reliable.

The Unexpected Aftermath of a CDN Purge

When developers at a mid-sized SaaS company purged CSS files via Fastly’s CDN expecting an instant cache eviction, they were met with an unexpected behavior: users visiting the site immediately post-deployment still saw outdated styles for several seconds—or even minutes. Upon further analysis, the culprit was Fastly’s intelligent caching mechanism, particularly its support for the stale-while-revalidate directive.

This directive allows Fastly (and browsers) to serve stale content temporarily while asynchronously fetching fresh versions. While this behavior is ideal for avoiding latency or downtime—especially in high-traffic situations—it can become problematic when precision and freshness matter more than performance, especially for critical frontend resources like CSS or JavaScript bundles.

To make things worse, a traditional purge request—whether soft or hard—wasn’t enough to ensure users were instantly served the updated CSS. Why? Because the cache may still contain older versions marked as “stale” but still present long enough to be unintentionally served during the revalidation race window.

Image not found in postmeta

Understanding Fastly’s Stale-While-Revalidate Mechanism

The stale-while-revalidate caching model is part of HTTP’s Cache-Control header standard. It tells caching systems they are allowed to deliver stale content to end-users while silently updating that content in the background. Here’s a simplified sequence of what happens when a purge collides with this mechanism:

  1. User requests /styles.css during a deployment.
  2. Fastly serves the cached version of /styles.css, marking it as stale.
  3. Fastly triggers a background fetch to update the cached version.
  4. There’s a small delay before the fresh version replaces the stale one in the cache.
  5. Any user request during this brief interval still receives the old CSS.

In practice, this means that even after issuing a purge command via Fastly’s API, users may continue to experience the old look of the site until the revalidation completes and the updated CSS is cached.

The Anatomy of the Problem

Initially, the development team attempted multiple workarounds:

  • Manual purges via CLI and Fastly dashboard
  • Switching to hard purges instead of soft ones
  • Disabling stale configurations temporarily

None of these tactics achieved consistent and reliable results. Furthermore, hard purges negatively impacted latency metrics, causing avoidable spikes as the edge servers had to refetch resources for every user immediately.

The Missing Link: Surrogate Keys

The key insight came when the team revisited Fastly’s surrogate key system. Surrogate-keys enable developers to tag content with arbitrary keys representing logical groupings. For example, all assets related to “version-42” of a site could be tagged with a key like assets_v42. This allows cache items to be purged by key rather than by URL or path.

More importantly, Fastly ensures purges by surrogate-key are deterministic and can be configured to bypass stale-while-revalidate behavior by fine-tuning the caching headers.

Surrogate-Key Strategy in Action

The new strategy involved several steps:

  1. Tag each deployable asset (CSS, JS, fonts) with a versioned surrogate key during the build process.
  2. Embed surrogate-key headers in the response via backend services or edge middleware.
  3. Configure automated deployment scripts to purge the old surrogate-key and upload new assets tagged with a new versioned key.
  4. Update HTML references to include content hashes (e.g., styles.abc123.css) ensuring cache-busting at the browser level.

This not only eliminated reliance on path-based purging but also guaranteed that all assets tied to a release were invalidated in a controlled and atomic fashion. When the deployment pipeline tagged and purged the appropriate keys, Fastly’s cache no longer relied on timing or revalidation lag to update content.

Benefits Realized

Once the surrogate-key solution was live, the dev team observed a suite of improvements:

  • Instant freshness: Users immediately received the correct CSS after new deploys.
  • Reduced error surface: Elimination of stale content removed styling bugs from post-deploy QA tests.
  • Faster rollback and deploy cycles: Teams could version and roll back patches quickly without worrying about cache propagation delays.
  • Improved observability: Logging surrogate keys made it easier to trace the lifecycle of each asset version.

Most importantly, frontend consistency improved dramatically across geographies and browsers. Mobile users, in particular, benefited from the improved cache control—essential for responsive CSS displays that had previously been distorted due to style mismatches.

Lessons Learned

This experience reinforced several caching and deployment best practices:

  • CDNs operate with asynchronous systems designed for performance, not always accuracy. Their behavior must be directed carefully for content freshness.
  • Explicit is better than implicit: Assigning clear version identities to assets (and tagging them via surrogate-keys) makes cache control declarative and deterministic.
  • Revalidating large objects like CSS at global scale introduces jitter; elimination of reliance on revalidation minimizes unexpected behavior.
  • Combining edge-wide cache tagging with hashed asset URLs ensures fast delivery and cache-busting at all layers—from CDN to browser.

Future Improvements on the Horizon

While the surrogate-key strategy now guarantees consistent delivery, the team is exploring additional tooling such as:

  • Integrating CDN state monitoring into CI/CD dashboards
  • Using Fastly’s edge dictionaries to track current active asset versions
  • Scripted rollbacks that automatically re-purge previous versions if necessary

Ultimately, the goal isn’t just about getting fresh assets to users—it’s about doing so deterministically, transparently, and at scale.

Conclusion

Fastly’s stale-while-revalidate is a powerful feature—when used for the right use cases. But during critical content updates, especially for visual assets like CSS, it introduces just enough uncertainty to be a problem. Adopting a surrogate-key-based cache invalidation strategy restored control to the developers, ensuring that users would experience nothing but the most current version of the application upon every update.

As web apps grow in complexity and deployment frequencies increase, understanding and mastering your CDN’s behavior becomes not an optimization—but an essential practice.