mattiasottosson / rack-cache-heroku-cedar-example

Basic example for using HTTP-cache on Heroku with Rack:cache and Memcached
http://rack-cache-example.herokuapp.com/
5 stars 0 forks source link

can static assets have a longer max-age? #1

Open barmstrong opened 12 years ago

barmstrong commented 12 years ago

First, thanks for setting this up! It's very helpful and I've got it up and running on heroku as your described.

One question I have is about the compiled css and js files.

when I curl these I get

$ curl -I http://example.com/assets/application-cbc611cbf2c27224a19bd2f1d5923058.css
HTTP/1.1 200 OK
Server: nginx
Date: Sun, 25 Mar 2012 22:10:04 GMT
Content-Type: text/css
Connection: keep-alive
Last-Modified: Sun, 25 Mar 2012 21:52:40 GMT
Cache-Control: public, max-age=3600
X-Content-Digest: 737221efe578fe2236f2cb89d47a58f09ddbb652
X-Rack-Cache: fresh
Vary: Accept-Encoding
Content-Length: 90315
X-Varnish: 515006493
Age: 955
Via: 1.1 varnish

It seems like since Rails changes the filename (and hence url) of the static assets on each change, the max-age could be much longer than 3600. Like why not a month or year?

This suggestion comes up when I run my site through Google Page Speed: https://developers.google.com/speed/pagespeed/report

It suggests: "The following cacheable resources have a short freshness lifetime (60 minutes). Specify an expiration at least one week in the future for the following resources:..."

What do you think?

barmstrong commented 12 years ago

Btw, I tried setting static_cache_control like this but it didn't seem to have an effect:

# Use a different cache store in production
config.cache_store = :dalli_store
# https://github.com/mattiasottosson/rack-cache-heroku-cedar-example
config.middleware.use Rack::Cache,
  :verbose => true,
  :metastore => "memcached://#{ENV['MEMCACHE_SERVERS']}",
  :entitystore => "memcached://#{ENV['MEMCACHE_SERVERS']}"

config.serve_static_assets = true
config.static_cache_control = "public, max-age=2592000"
mattiasottosson commented 12 years ago

Thanks! Glad to hear that you found it useful! This is my first write-up , and I did it while I struggled to learn how http cache works. Felt that I learned it a bit deeper while writing it down, so I thought "why not make an article share it?" :bowtie:

You're correct. The asset pipeline changes the filename on the given asset and applies a hash based on the content of the file. It used to be a query string, like /stylesheets/global.css?1309495796, but that has several disadvantages: http://guides.rubyonrails.org/asset_pipeline.html#what-is-fingerprinting-and-why-should-i-care

Based on the response, it looks like you're not running your app on the cedar stack?

$ curl -I http://example.com/assets/application-cbc611cbf2c27224a19bd2f1d5923058.css HTTP/1.1 200 OK Server: nginx

It says "Server: niginx", which is not available for the cedar stack. On cedar, all the requests goes directly through the app server (WEBrick by default), so you should see a app server here.

So, if the the app runs on bamboo and the stylesheets are precompiled on deploy, they get written to disk and served directly from nginx without going through the rails stack. Therefore, the static cache control setting doesn't apply and nginx serves the file handles the cache header it self. This applies to the app servers as well, but it happens a "layer back".

The tip/trick is to disable asset precompiling, enable on the fly compiling and gzip together with a cdn, like amazon cloudfront:

curl -I http://d1hedtpacycd9y.cloudfront.net/assets/like_manifest-ad2e08adf6d67d2818455672899c74a4.css HTTP/1.0 200 OK Cache-Control: public, max-age=31536000 Content-Type: text/css Date: Fri, 16 Mar 2012 15:03:18 GMT ETag: "ad2e08adf6d67d2818455672899c74a4" Last-Modified: Fri, 16 Mar 2012 14:49:32 GMT

Here is a really good article on the subject: http://blog.arvidandersson.se/2011/10/03/how-to-do-the-asset-serving-dance-on-heroku-cedar-with-rails-3-1

mattiasottosson commented 12 years ago

Oh, two updates btw.

I found that Rack::Cache is enabled in Rails >= 3.1 by default, so you don't need to include it to your Gemfile.

Also found that Dalli also handles the memcache setup, so there is no need to include this:

config.middleware.use Rack::Cache, :verbose => true, :metastore => "memcached://#{ENV['MEMCACHE_SERVERS']}", :entitystore => "memcached://#{ENV['MEMCACHE_SERVERS']}"

It just works right out of the box! :smile: