When an API returns 304 Not Modified at the wrong time, the first question is not “which cache should I clear?” It is “which validator did the client send, and what representation did the server compare it against?” Save one complete request and response pair, including ETag, Last-Modified, If-None-Match, If-Modified-Since, Vary, and Cache-Control.
An ETag belongs to a representation, not necessarily to a database row. If the same record can be returned as JSON and CSV, compressed and uncompressed, or in different languages, those responses may need different validators. A frequent bug is generating an ETag only from the record's update timestamp while ignoring serialization, permissions, or locale. The data row has not changed, but the bytes visible to the requester have.
Test conditional requests directly. Fetch the resource once, copy its ETag, then send a second request with If-None-Match. It should return 304 only when the selected representation is unchanged. Next, change one field that affects the response and repeat the request with the old tag. Expect a normal response and a new validator. Run the same test under every header named by Vary; otherwise an intermediary cache may serve one user's representation to another request shape.
Do not mix weak and strong validation accidentally. A weak ETag can confirm semantic equivalence for cache revalidation, but it is not suitable for byte-range guarantees. Timestamp validation is also limited by clock and second-level precision. When both ETag and date validators are present, follow the HTTP precedence rules rather than inventing an application-specific order.
Finally, log the validator input in a safe diagnostic mode: resource version, representation key, and generated tag, without logging private response bodies. That turns a vague stale-cache complaint into a comparison you can reproduce with curl and keep as a regression test.