Cache-Control response header is the standard caching mechanism for modern HTTP. It can have multiple values, which are comma-separated.
no-store: the browser cannot cache the response, ever
no-cache: the browser must check with the server before assuming that the resource is still valid.
On successful ETag validation, it can skip downloading the resource again, but still requires a request to the server.
private: the resource can be cached by the browser, but not at any higher levels, e.g., a CDN.
public: the resource can be cached. Redundant if given other arguments, like
max-age: specifies the number of seconds that the resource can be cached.
ETag response header is used to uniquely identify a potentially cached resource. As far as I can tell, this is only used when the
max-age time has been exceeded. I.e., the browser doesn’t even have to check with the server, hoping for an HTTP 304 “Not Modified”, until after
max-age seconds have elapsed since it originally received the resource. After that time period elapses, though, it will send the same HTTP GET, but with an
If-None-Match request header, setting the header’s value to the ETag it originally received.
If the server is smart, it will check the received ETag against some hash or modification timestamp of the requested resource, and return an HTTP 304 with no body. But that seems a lot more complicated than a simple
max-age value. And it makes more since when serving static files rather than records from a database.