2

I'm trying to tell nginx to cache some of my assets (js, css) forever, or at least for a very long time.

The idea is that once an asset bundle is compiled and published with an /assets/ URI prefix (e.g. /assets/foo-{fingerprint}.js) it stays there and doesn't ever need to change.

The internets told me I should write the following rule:

location ~ ^/assets/.*-([^.]+)\.(js|css)$ {
  gzip_static on; # there's also a .gz of the asset
  expires max;
  add_header Cache-Control public;
  add_header Last-Modified "";
  add_header ETag "";
  break;
}

I would expect this would result in responses with HTTP code 304 "Not Modified", but what I get is a consistent HTTP 200 (OK) every time.

I have tried some other approaches, for instance:

a) explicitly setting modification time to a constant point in time in the past;

add_header Last-Modified "Thu, 01 Jan 1970 00:00:00 GMT";

b) switching to If-None-Match checks;

add_header ETag $1;
if_modified_since off;

However, the only thing that really worked as needed was this:

add_header Last-Modified "Thu, 01 Jan 2030 00:00:00 GMT";
if_modified_since before;

I'm lost. This is contrary to everything I thought was right. Please help.

krukid
  • 4,285
  • 5
  • 31
  • 30

3 Answers3

6

You should change your internets, since they give you wrong advices.

Just remove all add_header lines from your location (as well as surplus brake):

location ~ ^/assets/.*-([^.]+)\.(js|css)$ {
   gzip_static on; # there's also a .gz of the asset
   expires max;
}

and read the docs from the true Internet: http://nginx.org/r/expires and https://www.rfc-editor.org/rfc/rfc2616

Community
  • 1
  • 1
VBart
  • 14,714
  • 4
  • 45
  • 49
  • Thanks, that works with one exception: whenever an asset is `touch`ed the cache is busted. I don't want the cache to be busted ever. Is it possible to make Nginx ignore/overwrite the `mtime` of served assets? – krukid Nov 19 '13 at 11:32
  • The thing about my web setup is that I'm deploying to multiple servers and synchronize `mtime` on every deploy because my old cache-busting approach was based on `mtime`. Now I'm trying to move to fingerprint-based cache-busting, but there will be a transitional period when both approaches must work, so I have to synchronize `mtime`s _and_ serve fingerprinted assets without busting cache. – krukid Nov 19 '13 at 12:35
  • Anyway, right now I'm looking at excluding new assets from `mtime` synchronization, but I thought FS `mtime` could only bust cache via `Last-Modified` HTTP header and overwriting that with a Nginx rule should have fixed the problem. – krukid Nov 19 '13 at 12:41
  • Ah, seems like I can't get rid of `mtime` sync after all, because requests to different servers will yield different `Last-Modified` values, which will send different `If-Modified-Since` values, which will randomly bust the cache. So I'm stuck once again. – krukid Nov 19 '13 at 13:08
  • I seem to have found a solution by using the Nginx rule you suggested and `touch -camt {past_time} {fingerprinted_assets}` on deploy. – krukid Nov 19 '13 at 15:39
  • You can use `rsync` to deploy. – VBart Nov 19 '13 at 15:43
  • It doesn't quite work with my deployment scenario, but I guess you're right - `rsync -t` looks like a valid option – krukid Nov 19 '13 at 15:59
0

...can you use a cron script on each instance to get whatever desired mtime and/or fingerprint you want on your specific file(s)?

Not perfect, but it probably works for both cache strategies with potential minimal gaps in the time it might fail the criteria for being cached.

BradChesney79
  • 650
  • 7
  • 16
-1

It seems part of my configuration. During my researching I realized that browser uses heuristic analysis to validate requests with ConditionalGet headers (E-Tag, Last-Modified). It makes a lot of sense for back-end responses, so you can handle that to save server resources.

But in terms of static files (js, css, images), you can tell browser to serve them straight away without any Conditional Get validation. It is helpful if you update file name if any change takes place.

This part of configuration makes it happen:

add_header Cache-Control public;
add_header Last-Modified "";
add_header ETag "";
Anatoly
  • 15,298
  • 5
  • 53
  • 77