A Developer’s Guide to Optimizing and Debugging Edge Caching

How to significantly improve WordPress or Node.js application performance on the WordPress VIP platform.

Edge caching is vital for WordPress sites. By storing content closer to users, it reduces latency and accelerates page load times, enhancing performance and user experience.  

The WordPress VIP Platform’s page cache is also known as the “edge cache.” It’s the first level of caching that each request encounters for both WordPress and Node.js environments. The edge caches content in various geographical locations worldwide, with traffic primarily served from the edge location nearest your visitors, bypassing PHP or Node.js entirely.

How edge caching works

By default, the VIP Platform caches idempotent requests such as GET and HEAD methods. Regular page content from WordPress or Node.js (non-static resources with 200 OK response codes, such as WordPress posts, pages, archives, the home page, /graphql, etc.) is cached by default for up to 30 minutes. 

The edge cache also sets Cache-Control to max-age=300, must-revalidate, instructing browsers to cache the content for five minutes. In addition, the edge cache can serve a Stale response for up to 12 hours after the cached response expiration time. 

Stale responses serve previously cached content when the content reaches its expiration date, while the edge fetches new content in the background. When the content expires, your visitors get faster responses from the edge instead of waiting for your application to respond.

While the default caching settings are effective for most WordPress and Node.js applications, we know that some customers may have unique requirements. That’s why we’ve made the system flexible, allowing you to adjust the cache TTL, change the Stale behavior, or bypass the cache as needed.

Diagram showing the flow of Cache-Control header from application to browser.

How to use Cache-Control header to change edge cache behavior

If the default caching behavior doesn’t suit your needs, change it by sending a Cache-Control response header from your application. You can do that globally, but we recommend only changing it for specific endpoints/resources where you need different behavior.

By default, when your application passes a Cache-Control header to the VIP edge cache, it will be sent unchanged to the client—unless the request bypasses the cache or you use the VIP Cache personalization API.

Note: As outlined in our documentation, the Cache-Control header sent from your application can’t be applied to status codes 301, 302, 307, 404, and 410 or files stored in the VIP File System.

Change the Cache Validity Period (TTL) for a resource

Increasing the TTL can improve the edge cache HIT ratio, improve your application response times, and decrease the number of requests that must be served directly from your application. Changing this value is rarely needed because we serve stale responses by default for all VIP applications. Stale responses increase the HIT ratio while minimizing the requests reaching the origin.

With stale responses disabled, decreasing the edge cache TTL can lead to low edge cache HIT ratios and degrade application performance. We don’t recommend lowering the TTL for sites with high traffic. Instead, consider reducing the TTL only for resources where the content is updated more often than every 30 minutes (the default cache validity period for the edge cache). Reducing the TTL is unnecessary for WordPress posts and pages, as the cache gets purged automatically when content is modified and saved. You can also programmatically purge the cache for other resources outside WordPress site content.

Cache-Control s-maxage and max-age directives

Being able to increase or decrease the cache TTL from the default 30 minutes gives you complete control over how long resources are cached. 

Depending on your needs, you can use the max-age or s-maxage directives in the Cache-Control header to apply the TTL to both shared (edge) cache and private (browser) cache or only to shared caches. If both max-age and s-maxage are present, VIP’s edge cache will set the TTL according to the s-maxage directive, while browsers will only use max-age. If only s-maxage is present, the TTL will be set only on the Edge, not the browser. When only max-age is set, both Edge and the browser will use it.

Here are examples along with their behavior:

  • Cache-Control: max-age=300 -> This will make both the edge cache and browser cache the resources for up to five minutes
  • Cache-Control: s-maxage=300 -> This will make only the edge cache cache the resource for up to five minutes
  • Cache-Control: max-age=300, s-maxage=600 -> This will make the browser cache the resource for five minutes and edge to cache it for up to 10 minutes.
Diagram showing the effect of s-maxage and max-age directives on TTL in private and shared caches.

Change stale behavior

VIP’s edge cache configures stale behavior via the stale-while-validate Cache-Control directive. By default, VIP injects stale-while-revalidate=60 for /wp-json endpoints and stale-while-revalidate=43200 (12 hours) for everything else.

When an item in the cache expires in a particular geographic location, the edge cache will keep serving old content to visitors. The cache will update in the background with a single request to your application. This behavior prevents flooding your application with requests when the item expires in the cache, decreasing the response times seen by visitors. 

Other safeguards prevent flooding your application with requests even if stale behavior is turned off. Still, ‌response times will increase as visitors must wait for the application to generate and send a response.

Stale behavior with caching reverse proxies

In most cases, we don’t recommend turning off or changing stale behavior. However, if you’re utilizing a third-party reverse proxy as an additional caching layer, you can turn off stale responses so the reverse proxy doesn’t cache outdated content for longer than expected. In this case, configure stale responses on your third-party reverse proxy to prevent increasing response times and sending too many unnecessary requests to your application.

Stale behavior for rarely-accessed endpoints 

You can also turn stale off for rarely-accessed endpoints, such as the ones used in automations. One example: an endpoint used to provide data for newsletters or weather reports regularly, such as hourly newsletter updates. Refreshing the content requires a single request to the endpoint. Because rarely-accessed or internal endpoints won’t have visitors keeping the response fresh, the endpoint serves old content on the subsequent request. 

Here’s an example of how stale responses served on rarely-accessed endpoints behave. In the example, automation makes a request to /newsletter endpoints every three hours. 

  • 12:00 -> Automation requests the /newsletter endpoint. The response gets cached on the edge and sent to the client
  • 15:00 -> The content in the backend is fresh, but the edge will serve a stale response because the previously cached response expired at 12:30 (default 30 minutes TTL). The edge updates the cache in the background with fresh content. 
  • 18:00 -> The edge will again serve a stale response, this time the one cached at 15:00. It updates the cache in the background with fresh content.

    Note: This doesn’t happen on endpoints that are accessed more frequently because they are kept fresh in the cache because of their frequency of access.

Changing stale behaviour configuration

Passing stale-while-revalidate=<ttl-in-seconds> in the Cache-Control header lets you increase or decrease the stale TTL and overwrite the default edge cache stale validity period.

To prevent the edge cache from serving stale content, you can send Cache-Control: stale-while-revalidate=0 from your application. Please note that this will disable stale resources altogether, and users will have to wait for your application to respond to the request when the item expires in the edge cache.

Bypassing the cache and serving responses directly from your application

VIP’s edge cache already bypasses the cache for non-cacheable resources, such as non-idempotent HTTP POST and PUT methods, requests with HTTP Authorization header, and other responses that aren’t suitable for storage in a shared cache.

To bypass the edge cache for resources outside the above defaults, use Cache-Control. But we recommend using this sparingly across your application, as it can severely impact performance.

For example, if you have an endpoint where content updates often, you don’t want to bypass the cache to serve up-to-date content to users. While that would ensure users are always served fresh data, it comes with an application performance hit. 

Instead, lower the cache TTL and combine that with programmatic purges to achieve the desired effect. Even a short TTL can dramatically improve application performance and response times, even for time-sensitive content.

We also don’t recommend bypassing the cache if you use a caching third-party reverse proxy/CDN in addition to VIP’s edge cache. Instead, you should consider the implications of multi-tiered caching on cache TTL and modify the TTL on the reverse proxy and VIP edge accordingly.

Several directives bypass VIP’s edge cache and have different meanings for browser private caches, such as max-age=0, private, no-store, and no-cache. 

However, when you bypass the VIP edge cache, VIP will send Cache-Control: no-cache, must-revalidate, max-age=0, no-store to prevent browsers from caching the resource, preventing unintended behavior. As such, it doesn’t matter which of the above cache-control directives you use to bypass the cache—they all have the same consistent behavior.

Combining Cache-Control directives

You can always combine different cache-control directives. For example, Cache-Control: private, max-age=300 will bypass the VIP cache and allow browsers to cache the resources for five minutes. Cache-Control: s-maxage=300, stale-while-revalidate=60 will change the edge cache TTL to five minutes while also changing the stale TTL to one minute.

Debugging edge cache behavior

By default, VIP provides response headers that let you identify specific cache behavior. The most useful ones are:

  • x-rq: includes information about the geographical location of the VIP edge cache server that served the request
  • x-cache: provides information about the request’s cache status. 

Note: Learn more about the value x-cache can hold in our documentation.

Edge cache debugging headers

You can expose additional debugging headers by sending X-Cache-Debug: 1 request header. The most essential headers this exposes are X-Original-Cache-Control and X-Applied-Cache-Control.

X-Original-Cache-Control holds the value of the Cache-Control header that your application sent. If this header isn’t present, it means your application didn’t send the Cache-Control header.

X-Applied-Cache-Control holds the value of Cache-Control that VIP edge used to apply for edge caching. Usually, this will be the same as X-Original-Cache-Control, with the addition of stale-while-revalidate, which VIP injects to configure serving stale responses for up to 12h. For status codes 301, 302, 307, 404, and 410, X-Applied-Cache-Control will contain only the stale-while-revalidate directive because we don’t allow overwriting cache behavior for those status codes.

Here’s an example:

Copy Code
curl -s -H "X-Cache-Debug: 1" -o /dev/null -D - "https://edge-cache-tests-wp.go-vip.net"

x-applied-cache-control: stale-while-revalidate=43200
cache-control: max-age=300, must-revalidate

Here, this site doesn’t send the Cache-Control header; thus, you can only see the X-Applied-Cache-Control applying the stale configuration. The edge cache returns the default Cache-Control: max-age=300, must-revalidate to the client. In this example, the edge cache will cache the resource for up to 30 minutes, and browsers will cache it for up to five.

Copy Code
curl -s -H "X-Cache-Debug: 1" -o /dev/null -D - "https://edge-cache-tests-wp.go-vip.net?edge-test-cache-control=max-age=600"

x-original-cache-control: max-age=600
x-applied-cache-control: max-age=600, stale-while-revalidate=43200
cache-control: max-age=600


This site sends Cache-Control:max-age=600, as seen in the X-Original-Cache-Control header. X-Applied-Cache-Control holds the same value with the addition of stale-while-revalidate, and the Cache-Control returned to the client is the same as the one sent from the application.

A final word about edge caching

Edge caching significantly improves your WordPress or Node.js application performance on the WordPress VIP Platform. While the default settings work for the majority of our customers, Cache-Control lets you modify edge caching behavior to align with your needs. And X-Cache-Debug header lets you effectively troubleshoot that behavior precisely. You can read more about edge caching in our documentation.

Author

Nejc Lovrenčič

Nejc Lovrenčič, Systems Engineer, Automattic

Get the latest content updates

Want to be notified about new content?

Leave your email address and we’ll make sure you stay updated.