Suppose a library changes today’s opening hours on its website, yet some visitors still see the old hours. Start by separating possible causes: was the original on the server not updated, did the browser reuse a stored response, or did an intermediate server supply an earlier version? Refreshing the page several times does not, by itself, reveal which stage caused the problem.
An HTTP cache stores received responses and reuses them when the conditions allow. This article connects Cache-Control instructions about storage and revalidation with ETag and 304 responses through a fictional public opening-hours schedule. The exchanges below are shortened illustrations, not measurements of a real library or a particular server. The aim is to explain which responses should be reusable and when, rather than prescribe removing every cache.
1. Separate storage, checking before reuse, and sharing
When reading a cache policy, write down three separate questions: may the response be stored, may the stored copy be used immediately for the next request, and may it be shared among users? Permission to store does not mean unrestricted reuse, and checking freshness does not grant permission to share with other users. The HTTP caching standard, RFC 9111, defines these conditions through separate directives. The explanations below concern response directives without an additional field-name argument.
| Question to decide | Relevant response directive | Meaning to distinguish |
|---|---|---|
| Must the response not be stored? | no-store | Prohibits storage for subsequent reuse |
| Must it be checked before reuse? | no-cache | Storage is possible, but successful validation is required before reuse |
| May it be shared among users? | private | Excludes storage by shared caches without also prohibiting storage by a private cache |
For a public schedule frequently edited at the same address, for example, consider whether a stored copy must be checked before use. A personalized page differs from that schedule in its sharing conditions from the outset. The private directive concerns shared caches; it does not replace login or authorization checks. Combining settings with different purposes into one statement can lead to choosing the wrong audience for information while trying to improve speed.
Record the reason for each directive in a policy table, not just the directive itself. Note whether the schedule is public information whose changes need checking, whether a logo is a file with fixed contents, and whether the same address carries personalized information. This is a starting point for classifying actual responses, not a ready-made configuration for every website. Also distinguish which store is concerned, such as a browser or a CDN.
2. Do not choose no-cache and no-store by their names alone
It is easy to read no-cache as a ban on storage. As a response directive, however, it requires validation before a stored response is reused. If the server confirms that the response has not changed, its stored body can potentially be used again. By contrast, no-store instructs caches not to store this response. The two settings differ in what may be kept and what must be checked on the next request, so do not expect them to behave identically.
In caching, fresh means that policy permits reuse without additional validation. It does not guarantee that the operator has left the original content unchanged in the meantime. If max-age defines a period for reuse, a previously stored response may continue to be selected even after the original changes during that period. Decide the acceptable update delay first, then relate that requirement to directives. Do not copy an arbitrary long storage duration as a universal performance solution.
MDN’s HTTP caching guide explains that adding no-store to a new response does not delete older responses already stored for the same URL. If an older copy is still usable and no request for the new response is made, the new header might not arrive either. Do not treat no-store as a complete cache purge or a sufficient condition for privacy. Check the lifetime of existing responses and the update procedures for caches you can manage.
3. Follow one return visit with ETag and 304
Suppose the fictional schedule at /hours.json initially reports a closing time of 18:00. The following explanatory HTTP/1.1 exchange retains only the necessary parts; other headers needed in a real response are omitted. Here, hours-v1 is an identifier chosen by the server for this representation, not a formula for calculating time or an actual file hash.
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache
ETag: "hours-v1"
{"closes":"18:00"}
If the browser stored this response, it can send the ETag value in If-None-Match on a subsequent conditional request. If the condition matches the current representation selected by the server and it is considered unchanged, the server responds with 304 Not Modified. The next block is a shortened record placing a request and its response one after the other. It is not an example of sending fresh schedule JSON after the 304.
GET /hours.json HTTP/1.1
Host: library.example
If-None-Match: "hours-v1"
HTTP/1.1 304 Not Modified
Cache-Control: no-cache
ETag: "hours-v1"
As MDN’s ETag explanation describes, the client uses the stored body in this case. A 304 has no new body, but the request and validation still involve communication. If the operator changes closing time to 19:00 and the server’s representation also changes, a 200 response containing a new body and a new identifier is the expected flow for the old condition. Using no-cache does not mean every request produces 304 or that database queries disappear.
Check the correspondence between content and identifiers rather than a status code alone. If content changes but the server continues using the old ETag, validation can produce an incorrect result. Conversely, identifiers that keep changing for identical content can cause more repeated body transfers. Inspect how the identifier is generated in relation to the selected representation on the server. The mere presence of an ETag does not establish a correct implementation.
4. Distinguish changing documents from assets with new filenames
A document whose content changes at the same URL, such as the schedule, can require a different update strategy from logos or scripts assigned new filenames whenever their content changes. If a logo changes from logo.a1.png to logo.b2.png, for example, the new document must point to the new address. Adding an identifier to a filename but overwriting the old file’s contents breaks the promise to manage content and address together.
Uploading a new file alone does not make visitors request its new address. If the HTML they receive still points to the old logo address, they can continue reusing the old asset. Examine the update path of the referencing document as well as the asset’s cache settings. Instead of giving documents and assets the same storage duration, be able to explain how long each may remain out of date.
Old and new documents may also coexist briefly during deployment. Publishing a new document before its assets are ready, or deleting old assets too early, creates different failures. In this fictional case, check both that files referenced by the new document exist and that files referenced by existing documents remain accessible. Filename changes are a tool for cache design; they do not automatically solve every deployment-order or rollback problem.
5. Test one hypothesis at a time in the Network panel
For a direct investigation, first record a baseline with caching allowed. Look together at the exact URL and query, the schedule version shown on screen, response status, If-None-Match in the request, and Cache-Control and ETag in the response. Request and response headers express conditions from different parties even when names coincide, so note which side you read. The Chrome DevTools Network reference explains how to distinguish these items in Headers.
| Fictional test situation | Records to inspect together | Premature conclusion to avoid |
|---|---|---|
| Request the same schedule again | Conditional request, status, and displayed hours | A 304 means there was no communication at all |
| Request after editing the original hours | New body, ETag, and actual request URL | A successful-save indication means every user has received the update |
| Request with browser caching disabled | Difference between the initial download and an ordinary return visit | This result equals normal return-visit performance |
| Return with the Back button | Whether a new request occurred and which screen was restored | Seeing a screen means a new HTTP validation necessarily occurred |
Then test disabling the browser cache as a separate condition. Chrome’s Disable cache setting applies while DevTools is open, so record whether the tools are open together with the setting. This is useful for examining an initial download, but it is different from testing how an ordinary return visit uses stored responses. Without recording the setting, repeatedly fresh downloads can be misread as evidence that caching does not work. Keep the experimental conditions so you can return to a cache-enabled comparison.
While observing, it is easier to narrow the cause if you avoid making many edits and requests at once. First inspect a return visit with unchanged content, then change one hours value and see how the response differs. Changing the document URL, server settings and filenames simultaneously makes it hard to identify which change caused the result. If the expected 200 or 304 does not appear, reread the actual request and response before adding more settings.
6. Check other storage paths and the definition of completion
Disabling the browser’s HTTP cache does not imply that a CDN in front of the server or other application storage paths have disappeared. Separately inspect whether a service worker handles the request, which response an intermediate server selects, and whether the application keeps displaying data already received. Seeing the same URL does not mean every layer uses the same copy.
Also distinguish going back from a new HTTP request. When the browser restores a screen from an earlier session, the revalidation request you expected may not appear even with no-cache. Taking that immediately as proof that the server ignored the header can misidentify the cause. Record separately whether a request actually occurred during a new navigation, a reload and a Back action, so that different phenomena do not become one supposed problem.
The schedule example is not complete merely because its cache-hit rate looks good. Check whether stored content is used correctly when unchanged, whether new information appears within the allowed time after a change, and whether the response is shared with the right audience. Recording the change time, observation time, conditions and actual body helps someone else reproduce the situation. Better speed figures do not meet the original requirement if incorrect opening hours remain visible for too long.
For production use, review policies by response type together with update and rollback procedures. Do not copy public-document cache instructions to personalized responses, or interpret successful validation as a completed authorization check. Headers communicate rules for storage and reuse. Explaining what was stored and what checks led to the current screen makes it possible to diagnose stale screens more accurately.