Expires and Cache-Control are “strong caching headers”
Last-Modified and ETag are “weak caching headers”
First the browser checks Expires/Cache-Control to determine whether or not to make a request to the servers.
If it has to make a request, it will send Last-Modified/ETag in the HTTP request. If the Etag value of the document matches that, the server will send a 304 code instead of 200, and no content. The browser will load the contents from its cache.
I recommend using one of the strong caching headers, along with one of the weak caching headers.
See also:
- Google Web Fundamentals: HTTP-Caching
- MDN web docs: HTTP caching