I am running nginx, and have the following block for expiration:

expires 52w;

However when I use Google Chrome Developer Tools to observe network traffic, some of the assets are loaded from cache (200-from cache) while most of the assets are making a request to the server (304 Not Modified).

I want to load all assets from cache without communicating with the server if possible. (200-from cache)

What would be the required change in my nginx configuration?

  • 304 tells the browser to use its cached copy. – Michael Hampton Oct 18 '13 at 17:55
  • @Michael Yes, but the client (browser) still communicates with the server to get that 304 info. I'm trying to prevent that request too and just use cached copy without a round-trip. – ustun Oct 18 '13 at 17:57

What you're looking for is expires max; This sets a far-future header which most browsers will take to mean don't even bother asking if the resource was modified.

Remember, you can never control what requests any user agent will make you can only give it instructions and hope it listens. This is about the best you can do.

| improve this answer | |
  • Thanks, I gave this a try, I had already set it to 52weeks, max sets it till 2037. It still caches all, doesn't make any requests after that for some but not all of the images, but makes an if-modified-since request for all css and js. – ustun Oct 19 '13 at 14:27
  • 1
    Most likely the browser has cached the previous caching duration. But as I said you cannot force a browser, only instruct it. For me this results in full caching: i.imgur.com/aDpRlFL.png – Martin Fjordvald Oct 21 '13 at 14:12

First thing, a 304 HTTP Status does not contain a message-body, rather it should not as described in specifications, there are some specific conditions based on cache validators also, check W3C documentation for more details.

About enabling a caching setup on Nginx this is how I had setup caching

# all pages or URL patterns that are to be cached go here
location ~* \.do$ {
  expires -1;

# allow caching for static assets
location ~* \.(css|js|gif|jpe?g|png|svg)$ {
  expires 2w;
  add_header Pragma public;
  add_header Cache-Control "public, must-revalidate, proxy-revalidate";
| improve this answer | |
  • I added the Cache-Control header, but it still sends a 304 request instead of using the file in cache. – ustun Oct 18 '13 at 11:08
  • 1
    @ustun remove must-revalidate from cache control to fix that. – rahulroy9202 Nov 15 '18 at 7:19

You should refer to HTTP specifications IMHO; and fully understand the mechanism behind caching and the meaning of 304, it doesn't transfer same data like a 200 response does.

Like Martin pointed out, you give a "directive" and leave it to that, but the browser will check whether to use the cached copy or not. Additionally, you can add a age and max-age headers keeping max-age higher than age to sometimes avoid even a 304.

And lastly, if you trying to view the effect of all of this on page by a refresh, you might still get 304 responses due to requests initiated by browsers, which is something you cannot really control AFAIK.

| improve this answer | |
  • Thanks. Yes, I know 304 doesn't transfer data like 200 does, but it seems browser decides how to behave in the end as you say, so there is not much I can do. The question is, why does the browser choose to server some from the cache without making a If-Modified check, and some after making the check. – ustun Oct 21 '13 at 10:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.