Setting Akamai Edge-Control headers with Ruby on Rails
Just a short and sweet little tip.
Several months ago we moved one of our clients over to Akamai’s Content Delivery Network (CDN). Ww were previously using a combination of Amazon S3 and CloudFront with some benefits, but we were finding several key areas of the world were not s covered by Amazon (yet) for asset delivery. Along with that, we really wanted to take advantage of the CDN for more of our HTML content with a lot of complex rules that related to geo-targeting and regionalization of content.
I’ll try to cover those topics in another post, but wanted to share a few tidbits of code that we are using to manage Akamai’s Edge-control caches from within our Rails application.
With Akamai, we’re able to tell their Edge servers whether it should hold on to the response so it can try to avoid an extra request to the origin (aka our Rails application). From Rails, we just added a few helper methods to our controllers so that we can litter our application with various expiration times.
# Sets the headers for Akamai
# acceptable formats include:
# 1m, 10m, 90m, 2h, 5d
def set_cache_control_for(maxage="20m")
headers['Edge-control'] = "!no-store, max-age=#{maxage}"
end
This allows us to do things like:
class ProductsController < ApplicationController
def show
set_cache_control_for('4h')
@product = Product.find(params[:id])
end
end
Then when Akamai gets a request for http://domain.com/products/20-foo-bar
, it’ll try to keep a cached copy around for four hours before it hits our server again.
Managing SEO-friendly HTML Titles with Rails
I’ve seen this come up a few times in the #rubyonrails IRC channel and figured that I’d post a quick entry for future reference.
Problem: HTML titles
You want to have a clean way to manage the titles on your HTML pages.
<html>
<head>
<title>Robby on Rails — Article Title Goes Here</title>
</head>
<body>
...
Possible Solution(s):
Since the <title>
tag is usually declared in your layout, you need to be able to dynamically update this information from almost every action in your application.
Here are a few ways that I’ve seen this handled.
- Use a instance variable, which would have a default value and you could override it in any controller action
- Use the
content_for
method to manage it.
Let’s take a few minutes to look at these two approaches.
Instance Variable
With the instance variable, you might end up with something like:
# app/views/layouts/application.html.erb
<title>Robby on Rails — <%= @html_title || 'Default text here...' -%></title>
Then in a controller action…
# app/controllers/articles_controller.rb
def show
# ...
@html_title = @article.title
end
So, that’s one way to handle it and is probably a more common way.
The content_for
helper method approach
This solution is very similar (and underneath uses an instance variable).
We’ll use the content_for and a little yield
action.
# app/views/layouts/application.html.erb
<title>Robby on Rails <%= (html_title = yield :html_title) ? html_title : '— Default text here...' %></title>
Then we’ll create a helper method.
# app/helpers/application_helper.rb
def set_html_title(str="")
unless str.blank?
content_for :html_title do
"— #{str} "
end
end
end
Now, instead of defining the HTML <title>
value in the controllers, we’ll just toss this into our html.erb files as necessary.
<% set_html_title(@article.name) -%>
... rest of view
..and that’s pretty much it.
Which is the better solution?
This is where we’ll not find a lot of consensus amongst people. I’m a fan of the content_for
-based approach and defining the title in views rather than in controller actions. I’m an advocate of skinny controllers and while I’m not a big fan of messy views, I believe that there is less overhead in managing this within the View-world.
I’d love to hear your thoughts on this. Perhaps you have a more eloquent for managing things like this? Do share. :-)