Home Rails 4 - response.cache_control vs. response.headers['Cache-Control']
Reply: 0

Rails 4 - response.cache_control vs. response.headers['Cache-Control']

reid
1#
reid Published in 2017-11-29 22:31:41Z

Rails 4 Caching Bug/Issue

Running: Heroku (although I experience issue locally) Rails 4.1.14.2

The other day I noticed that on my production rails application, this JSON file that I expected to be cached and stored on my Amazon Cloudfront CDN was not being stored. I was receiving a 'cache missed' header (stating to me that it was asking my rails application for the file).

When reviewing that endpoint, I did notice that I am telling the response to expire in 3 minutes - which I expect the response to set the cache control headers on the response. Most of the results I have found either on SO or on Google have given me the same advice, calling expires_in. That makes sense to me, however I have found that it is not working for me.

Below is my controller with some notations:

  class Api::V1::CatalogController < Spree::BaseController

    respond_to :json
    protect_from_forgery except: :index
    skip_before_filter :set_utm_fields

    def index
      headers['Access-Control-Allow-Origin'] = '*'
      if Rails.env.test?
        json = Catalog::InventoryBuilder.build.as_json
      else
        # response.headers['Cache-Control'] = "public, max-age=#{3.minutes}"
        # response.headers['Expires'] = "#{3.minutes.from_now.to_formatted_s (:rfc822)}"

        # (Rails 4 Bug?)
        # Rails sets the response.cache_control properly when below is called
        # however, the 'Cache-Control' header is not set to expected values
        expires_in 3.minutes, :public => true

        puts response.cache_control
        puts response.headers

        json = Rails.cache.fetch('all_available_products') { Catalog::InventoryBuilder.build.as_json }
      end
      render json: json, callback: params[:callback]
    end
  end

When curling a request, using expires_in 3.minutes, :public => true in development (as well in production), I am returned these headers:

  HTTP/1.1 200 OK
  X-Frame-Options: SAMEORIGIN
  X-Xss-Protection: 1; mode=block
  X-Content-Type-Options: nosniff
  Cache-Control: no-cache, no-store
  Pragma: no-cache
  Expires: Mon, 01 Jan 1990 00:00:00 GMT
  Access-Control-Allow-Origin: *
  Date: Wed, 29 Nov 2017 21:33:02 GMT
  Content-Type: application/json; charset=utf-8
  X-Meta-Request-Version: 0.3.4
  X-Request-Id: 4bfa1309-8bc8-479b-8ad3-882002094baa
  X-Runtime: 0.578353
  Vary: Accept-Encoding
  Server: WEBrick/1.3.1 (Ruby/2.1.5/2014-11-13)
  Content-Length: 0
  Connection: Keep-Alive
  Set-Cookie: guest_token=BAhJIhtLY2xpeHVBOTNBUFBDYXBEM3BFRnNBBjoGRUY%3D--3967e721b77676d7f936e0adf4ae4313bb62631b; path=/; expires=Sun, 29 Nov 2037 21:33:02 -0000
  Set-Cookie: _session_id=4d4b751a08fc9059058f3aa957730f06; path=/; expires=Wed, 28 Feb 2018 21:33:02 -0000; HttpOnly

Even by explicitly calling expires_in 3.minutes, :public => true before returning the response, the response headers still state that the Cache-Control header is no-cache. But when I call response.cache_control, I see {:max_age=>180 seconds, :public=>true, :must_revalidate=>nil, :extras=>[]} which is expected. However, it seems like they are not applied.

I am unsure where in Rails/Rack, that response.cache_control is applied to response['Cache-Control']. Is that even the intended architecture?

Next up, when I modify the end point to explicitly set response.headers['Cache-Control'] = "public, max-age=#{3.minutes}" and response.headers['Expires'] = "#{3.minutes.from_now.to_formatted_s (:rfc822)}", the response returns the expected headers:

  HTTP/1.1 200 OK
  X-Frame-Options: SAMEORIGIN
  X-Xss-Protection: 1; mode=block
  X-Content-Type-Options: nosniff
  Cache-Control: public, max-age=180
  Pragma: no-cache
  Expires: Wed, 29 Nov 2017 16:52:30 -0500
  Access-Control-Allow-Origin: *
  Content-Type: application/json; charset=utf-8
  Etag: "17e6e6b937e574ad76d1b88b89e8e861"
  X-Meta-Request-Version: 0.3.4
  X-Request-Id: 48594617-d6ea-495d-95b7-3972adb1a163
  X-Runtime: 0.616826
  Vary: Accept-Encoding
  Server: WEBrick/1.3.1 (Ruby/2.1.5/2014-11-13)
  Date: Wed, 29 Nov 2017 21:49:30 GMT
  Content-Length: 0
  Connection: Keep-Alive
  Set-Cookie: guest_token=BAhJIhtpTjdsUmRSY2lONmZteE1DMXdjTjlnBjoGRUY%3D--b13baa5e41d06f680c4db318ec7684923c78f31c; path=/; expires=Sun, 29 Nov 2037 21:49:30 -0000
  Set-Cookie: _session_id=1cb56c54691c910ee41db46357a87173; path=/; expires=Wed, 28 Feb 2018 21:49:30 -0000; HttpOnly

In production (not development), I do have set:

  config.serve_static_assets = false
  config.static_cache_control = "public, max-age=31536000"

However it is my understanding that this is set for any static asset therefore it does not affect this network response(?).

My questions are:

  1. Why is it when I call expires_in 3.minutes, :public => true, the response headers still set to no-cache?
  2. When I explicitly set the Cache-Header for the response within my controller, why does it fulfill my demand?
  3. If using expires_in is the intended practice, when does the instance variable attribute response.cache_control get set to the response's Cache-Header? Within ActiveDispatch or Rack or something else?
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.319595 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO