HTTP caching is a fundamental strategy for improving performance and reducing server load by storing copies of resources closer to the client. As a backend developer, you control this process primarily through HTTP response headers. The Cache-Control header is your primary directive, telling clients (browsers, proxies) how to cache your responses. Directives like max-age=3600 instruct clients to cache a resource for 3600 seconds, avoiding re-fetching it during that period. Other important directives include no-cache (revalidate before using), no-store (never cache), public (cacheable by any cache), and private (cacheable only by private caches, like a browser's own cache). Properly setting Cache-Control can significantly reduce the number of requests hitting your backend for static or infrequently changing content.
While Cache-Control: max-age handles initial freshness, resources eventually expire or might become stale. This is where conditional requests come into play, allowing clients to ask, "Is my cached version still valid?" without downloading the entire resource again. The server facilitates this using ETags (Entity Tags) or Last-Modified headers. When your server sends a response, it can include an ETag (a unique identifier for that specific version of the resource, often a hash) or Last-Modified (the timestamp of the resource's last modification). The client stores this with its cached copy.
On subsequent requests for the same resource, the client sends these identifiers back to your server using If-None-Match (for ETag) or If-Modified-Since (for Last-Modified). Your backend then checks if the resource has changed. If it hasn't, you respond with a 304 Not Modified status code, indicating that the client's cached version is still good. This saves significant bandwidth and processing power, as you avoid sending the full response body. By mastering these headers, backend developers can offload substantial work from their servers and provide a snappier experience to users.
Key Takeaways
Cache-Controldictates if and how clients can cache responses (e.g.,max-agefor expiration).ETagsandLast-Modifiedprovide unique identifiers for resource versions and modification times.- Clients use
If-None-MatchandIf-Modified-Sincefor conditional requests to revalidate cached content. - A
304 Not Modifiedresponse tells clients to use their existing cached copy, saving bandwidth and server load. - Backend developers are responsible for setting these HTTP headers to optimize performance and reduce infrastructure costs.
Code Example
How this code works
This code sets up a simple web server using express to demonstrate how to implement HTTP caching for an API endpoint returning product data. Its main purpose is to show how a server can instruct clients (like browsers) to cache data, and then efficiently verify if their cached copy is still fresh, preventing unnecessary data transfers. It accomplishes this using standard HTTP headers: Cache-Control for general caching rules and ETag for content validation.
When a client makes a GET request to /api/product/:id, the server first simulates fetching product data and generates a unique etag – a content fingerprint – using crypto.createHash('md5'). It then sets Cache-Control: public, max-age=60, telling the client to cache the response for 60 seconds, and includes the ETag header. A crucial step follows: the server checks req.headers['if-none-match']. If the client included this header with an etag that matches the server's current one, it means the client's cached data is still valid. The server then responds with res.status(304).send(), signaling "Not Modified," and uses return to immediately stop processing, avoiding sending the full product data and saving bandwidth. Otherwise, it sends the complete res.json(product).