Overriding rails Cache-Control header on redirect - ruby-on-rails

Whether I do:
head 302
or
head 307
or
redirect_to
calls in the same controller action to
response.headers['Cache-Control'] = "public, max-age=86400"
have no effect. Rails sends:
Cache-Control: no-cache
no matter what. I need to send the Cache-Control header to instruct an edge cache to serve the redirect for a day. Is this possible?

You can't set Cache-Control directly into the headers (anymore?), as you need to modify the response.cache_control object (since it will be used to set the Cache-Control header later).
Luckily, the expires_in method takes care of this for you:
expires_in 1.day, :public => true
See more here:
http://apidock.com/rails/ActionController/ConditionalGet/expires_in

Try using this instead
response.headers['Cache-Control'] = 'public, max-age=300'
and make sure your in production mode. Rails wont cache in development.

With Rails 5 you can do
response.cache_control = 'public, max-age=86400'

. I need to send the Cache-Control header to instruct an edge cache to serve the redirect for a day.
How is this possible ? in case of temp redirect , browsers will always try to get original url first and on redirect they will try other url,which if cached on proxies can be served from there.
But again browser will still make first contact with your server.

Related

Exclusively server-side caching of a Rails.app with Rack::Cache

I have the following problem: I want to cache the result of an action in Redis. For this reason, I use https://github.com/jodosha/redis-rack-cache. The fact that an action should be cached by Rack::Cache is determined by setting the appropriate HTTP header information in Rails, e.g.:
response.headers['Cache-Control'] = 'max-age=3600, public, must-revalidate'
Now, Rack::Cache will correctly cache the response in Redis. However, this header does also tell the browser to cache the response, which I don't want! The request should be cached exclusively on the server-side.
As a workaround, I am replacing the header in nginx, which I use as a reverse proxy, but there must be a more elegant way. Does anybody know how to do it?
Best regards,
Martin
One option would be to write your own middleware that sits above Rack::Cache and then removes these Cache-Control headers from the response.
Something as simple as:
def call(env)
status, headers, body = #app.call(env)
headers.delete("Cache-Control")
[status, headers, body]
end
would work as a middleware.

Rails assets: How can you reference images from another app?

Rails adds an md5 hash to images names when moving them to the /public/assets directory during precompilation. The problem is that these hashes are unpredictable, so how can I know what they're going to be called when trying to link to them?
For example, if I'm hosting an image named flowers.jpg, and then try to access it at www.mysite.com/flowers.jpg, it fails, because the file has been renamed flowers-4182172ae014ec23dc02739229c08dcc.
I know Rails has helpers that will automatically find these images. But what if you're trying to link to these images from a completely different website or application? Is there a way to get Rails to say, "Well I can't find a precompiled version of flowers.jpg, so instead of serving from /public/assets I'll serve from /app/assets."?
EDIT: According to this post (http://stackoverflow.com/questions/10045892/rails-compiles-assets-both-with-and-without-md5-hash-why), Rails should be compiling a version of my assets both with and WITHOUT an md5 hash? Any idea why my copy of Rails isn't generating a version without a fingerprint?
Rails handle that for you using image_tag:
image_tag "myimage.jpg"
and this will get you the right url for it. You might be able to write a small service that generates the image url for your as (untested):
Class AssetsService < ApplicationController
def index
end
end
index.js.haml
= image_tag "myimage.jpg"
The answer wasn't with Rails. I don't think Rails is supposed to compile images without the fingerprint. It's supposed to still be able to serve them, however, and I had added some code to my nginx config file that prevented this from happening. This was the offending code:
location ~* ^/assets/ {
# Per RFC2616 - 1 year maximum expiry
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
expires 1y;
add_header Cache-Control public;
# Some browsers still send conditional-GET requests if there's a
# Last-Modified header or an ETag header even if they haven't
# reached the expiry date sent in the Expires header.
add_header Last-Modified "";
add_header ETag "";
break;
}

Rails 3.1 and Http Page Caching

Given that Heroku Cedar doesn't have http caching provided by Varnish I would like to use Rack::Cache.
I have been told that rails 3.1.1 have Rack::Cache active by default, I just need to make sure to have in the configuration:
config.action_controller.perform_caching = true
and I need to pick a cache store, for this experiment I'm using:
config.cache_store = :memory_store
In the action of the page I want to cache I've added the following lines:
response.header['Cache-Control'] = 'public, max-age=300'
response.header['Expires'] = CGI.rfc1123_date(Time.now + 300)
This code used to work fine with Varnish, the first request would return a 200 and the subsequent (for 5 mins) would return a 304.
This doesn't happen with Rails 3.1 and Heroku Cedar Stack.
I do get those headers in the response but subsequent requests returns 200 instead of 304.
What am I doing wrong? Thank you.
As you noted, the Cedar stack doesn't use Varnish. That means a web request will always hit the ruby server.
With that in mind, Rack::Cache will respect your headers and serve the cached content.
However, since the request is actually going past the http layer into the rails app, the response will always be 200 since the cache doesn't happen at the http layer anymore.
To confirm this is true, insert this in one of your cached actions:
<%= Time.now.to_i %>
Then, reload the page several times and you'll notice the timestamp won't change.

Cache-Control Headers and Heroku Dynamic Images

I'm trying to understand HTTP Caching on Heroku. After reading their article, I'm curious about how the Cache-Control HTTP header is working.
In the sample application mentioned in the article the header is set in a controller action:
def image
#qrimage = QRImage.find_by_md5(params[:md5])
if #qrimage
headers['Cache-Control'] = 'public; max-age=2592000' # cache image for a month
send_data #qrimage.data, :filename => #qrimage.filename, :disposition => 'inline', :type => "image/png"
else
render :nothing => true, :status => 404
end
end
The code for #qrimage.data is like:
def data
qrcode = RQRCode::QRCode.new(self.message, :size => self.version, :level => self.ecc.to_sym)
qrcode.to_s
end
So to me it looks like the image is being generated on the server every time. And then cached by the browser for a month. So the only savings here is when the same visitor tries to view the same image.
If different visitors try to view the same image, it will still be generated and sent. Not really all that helpful if you ask me.
Is my understanding correct, or will the same image not be regenerate for each site visitor?
Heroku apps on the Aspen and Bamboo stacks are fronted by Varnish, an
HTTP accelerator. Varnish will cache output from your application
according to cues provided by standard HTTP headers to describe a
page’s cacheability. These headers are the same ones used by browsers,
so setting these headers correctly gives your app a double boost of
speed when on Heroku: at the Varnish layer, and again at the user’s
browser.
If you don't know, Varnish is a really fast cache that sits between your application and the internet, essentially. When headers say it's safe to cache, Varnish does so and responds to additional requests with the cached object without ever hitting your application.

Remove charset from Rails content type

I have a old-stupid service making request to my app that fails when the Content-Type include the charset line
Content-Type text/html; charset=utf-8
and I don't know how to remove it from my rails response. Every time that I override the headers forcing just the first part (Content-Type text/html) Rails adds the charset to the header...
For Rails 3/4, the code that handles this is in ActionDispatch::Response.assign_default_content_type_and_charset! in actionpack/lib/action_dispatch/http/response.rb.
Setting response.headers['Content-Type'] instead of response.content_type should eliminate the charset. Chubas' solution does this for all responses.
For Rails 2, the code that handles this is in content_type= and charset= in actionpack/lib/action_controller/response.rb.
As Carson's solution describes, setting ActionController::Base.default_charset = nil should eliminate the charset.
This worked for me:
class MyController
after_filter :remove_charset
def remove_charset
headers['Content-type'] = "text/html"
end
end
If you're working on development, make sure you clear your browser's cache.
There is this method, but didn't work for me. I don't know why, it may even be a bug.
The only way I was able to get it to work is by setting the default charset
ActionController::Base.default_charset = nil
Also, setting the Content-Transfer-Encoding header to binary will turn off the charset.
Putting this in the controller did it for me:
ActionDispatch::Response::default_charset = nil
I put in in my base controller to remove it from all responses.

Resources