Skip to Content

How is caching controlled under REST?

Representational state transfer (REST) is an architectural style for building distributed systems and web services. Caching is an important aspect of RESTful systems that can improve performance, scalability, and reliability. This article will provide an overview of how caching is controlled in RESTful APIs.

REST and Statelessness

A key constraint of REST is that it must be stateless. This means that each request from a client to a server must contain all the necessary information to understand and complete the request. The server cannot rely on any stored context about the client. However, many dynamic websites rely heavily on maintaining user session state across requests. This seems contrary to the stateless constraint of REST.

Caching provides a solution to this dilemma. By caching data on the client side, the client can store session state and other contextual data to include in future requests. The server remains stateless while the client handles statefulness. In summary, caching allows RESTful systems to remain stateless on the server side by shifting state handling to the client side.

Benefits of Caching in REST

There are several benefits to caching in RESTful systems:

  • Improves performance by reducing request load on the server. Returning cached data is faster than recalculating a response.
  • Scales well by distributing cache storage across multiple client systems.
  • Reduces latency by localizing data closer to the client.
  • Increases availability when the server fails, as data remains accessible from the cache.
  • Conserves network bandwidth by reducing redundant communication.

In summary, caching is a powerful technique for optimizing RESTful systems. It not only helps maintain the stateless constraint but also improves the efficiency, scalability, and resilience of RESTful APIs.

Client-Side Caching

Client-side caching refers to storing cache data locally on the client system. There are two main approaches for clients to cache data from a REST API:

  1. Browser caching – Browsers automatically cache resources to minimize requests. For example, if a webpage references a CSS file, the browser caches it locally so it doesn’t need to fetch it again when reloading the page.
  2. Programmatic caching – REST client code can also proactively cache resources by storing data in the application code itself. For example, a mobile app could cache user profiles when fetching them from an API to minimize API calls.

Browser caching happens automatically based on headers returned by the server, which we’ll explore more in the next section. Programmatic caching requires custom client-side logic to fetch, store, and reuse cached data appropriately.

Server-Side Caching

Server-side caching enables caching data on intermediate servers between the client and origin server. There are two common types of server-side caching:

  1. Proxy caching – Proxy servers sit between client and origin servers to handle requests. Proxy caches store responses as they pass through to optimize repeated requests.
  2. Reverse proxy caching – Reverse proxies are similar except they sit in front of the origin server. This allows centralized caching shared across many servers in a cluster.

CDNs (content delivery networks) make extensive use of reverse proxy caching. The cache hit rate is higher since reverse proxies consolidate requests from many clients. Server-side caching also reduces load on origin servers and improves response times.

HTTP Caching Headers

The HTTP protocol defines several request and response headers that control caching mechanisms:

Request Headers

  • Cache-Control – Indicates if and how a response can be cached. Values like “no-cache” prevent caching.
  • Pragma – A backwards compatible way to prevent caching not supported by HTTP 1.1.
  • If-Modified-Since – Makes a conditional request if content has changed since the provided timestamp.
  • If-None-Match – Returns full response only if the ETag value does not match, allowing cached versions.

Response Headers

  • Cache-Control – Specifies caching policies for browsers and shared caches to adhere to.
  • Expires – The timestamp after which the content is considered stale and must be refetched.
  • ETag – An arbitrary token representing the current version of the resource.
  • Last-Modified – The last modified timestamp of the resource.

These headers allow servers to dynamically control cacheability for each request. For example, resources containing user-specific data should not be cached by default.

Cache Validation

When using a cached resource, it’s necessary to check if it is still valid or if it has become stale and needs re-fetching. There are two main validation approaches:

  1. Expiry-based – The cache stores the expiry time of each resource. If the current time is past the expiry, it is deemed stale.
  2. Validation-based – The cache stores an ETag or Last-Modified value. It makes a conditional request to check if the value has changed.

Expiry-based validation is simpler but less precise. Cache objects may become stale before their set expiry time. Validation-based checking ensures cached data is reused only if the server confirms it is still current.

Cache Invalidation

When data changes on the server, any stored cached versions need to be invalidated so that new requests fetch updated data. Some approaches for cache invalidation include:

  • Short expiry times to minimize stale data.
  • Versioned resource URLs to indicate when cached data is outdated.
  • Revving ETag values on each update to explicitly invalidate old cached versions.
  • Actively purging cache when deletions or updates occur.

Setting short expiry times is simple to implement but reduces overall cache effectiveness. The other approaches aim to maximize cache reuse by only selectively invalidating changed data.

Caching and REST Maturity Model

Leonard Richardson proposed a maturity model to rate REST APIs on a scale from 0 to 3 based on adherence to REST principles. The higher levels align more closely with key REST constraints:

  • Level 0 – Uses HTTP as a transport protocol but ignores it otherwise.
  • Level 1 – Resources with proper URIs. Interactions use HTTP methods meaningfully.
  • Level 2 – Level 1 plus proper use of HTTP status codes and headers.
  • Level 3 – Full adherence to REST constraints including stateless server and proper caching.

Effective caching mechanisms are a defining characteristic of the most mature REST APIs. Proper implementation of caching helps satisfy the stateless and performance constraints that are central to REST.

Common REST Caching Challenges

Despite its benefits, caching in REST can also lead to challenges including:

  • Stale data when caches aren’t invalidated properly after updates.
  • Cache coherence issues when multiple cache locations don’t synchronize.
  • Difficulty caching for authenticated users due to dynamically generated data.
  • Determining optimal expiry times for different types of data.
  • Additional logic and complexity required to implement caching.

Careful testing is required to ensure caches remain coherent and handling authentication correctly. Caching may not always be beneficial depending on the data being served.

Caching REST Requests

In addition to caching responses, REST clients can also cache the results of request operations. This may include:

  • GET requests to retrieve resources.
  • PUT requests to update resources.
  • POST requests to create resources.
  • DELETE requests to delete resources.

By caching the effects of writes like PUT, POST and DELETE, reads via GET can be served from cache without issuing unnecessary requests. However, these optimizations require carefully managing cache coherence.

Alternative Caching Approaches

In addition to the standard caching methods already discussed, some alternative approaches include:

  • Client query caching – Caching the results of parameterized API queries, not just singular resources.
  • Async caching – Populating the cache asynchronously to optimize request performance.
  • Prefetching – Predictively fetching and caching resources the client is likely to need in the future.
  • Cache meshes – Decoupling caching from application code into a sidecar proxy pattern.

These approaches require more custom implementation but can yield further benefits like avoiding cache stampedes and increasing cache hit ratios.

Caching and CDNs

Content delivery networks (CDNs) are a popular way to improve performance by caching content in edge locations closer to users. Key caching techniques used by CDNs include:

  • Reverse proxy caching to consolidate duplicate requests.
  • Edge caching to distribute content globally.
  • Cache prefetching based on usage analytics.
  • Purging stale cache objects when origin servers are updated.

CDNs essentially implement a distributed caching infrastructure at a global scale. Large web properties serving significant traffic are prime candidates for CDN caching.

Client Cache Control

In addition to server headers, clients can also influence caching behavior through various Cache-Control request headers:

  • no-cache – Always fetch a fresh response, but cache validators if present.
  • no-store – Never cache the response in any way.
  • max-age – Overrides the expiry age specified by the server.
  • min-fresh – Specifies how recent a cached response must be.
  • only-if-cached – Only return a cached response, fail if none exists.

These directives allow clients to customize caching policies for specific use cases. For example, clients can force a full refresh of sensitive data using no-cache and no-store.

Security and Caching

Caching introduces security considerations that must be accounted for:

  • Sensitive data should not be cached unless adequate access controls are in place.
  • Authorization headers should not be cached to prevent credentials leakage.
  • Caches should not return stale responses when better authorization is present.
  • Untrusted clients and intermediaries must not be allowed to poison shared caches.

Cache access controls, encryption, and integrity checks help mitigate caching-related security risks. Security is also a reason to limit caching of authenticated user data.

Conclusion

Caching is a vital part of optimizing performance and scalability in RESTful systems. It balances the stateless constraint against practical considerations of working with web-based clients and data. Proper implementation of caching improves efficiency and reliability while remaining true to REST principles. When applied appropriately, caching can be one of the most impactful design choices for building robust, mature REST APIs.