Permanent 301 redirections using a Rack middleware in Rails

It’s a quarter to midnight and you are just about to deploy a new version of a web site that has a totally new hierarchy.  This happens a lot when migrating a PHP site to Rails.  And then you realize that the structure or the URLs is totally different than the one on the old site.

Maybe the old site has a nice ranking in Google and a good reputation or lots of people have bookmarked pages of the site.  You don’t want to make Google and those users unhappy.  So you want to have the new web site send a 301 reponse code (permanent redirect) instead of the famous 404 (page not found).

One way to do this in Apache is to enable the rewrite engine and add some rewrite rules in the web site configuration.  But these rules are not easy to build and it involves changing the Apache config, which is not trivial for some people.

I found a Rack middleware called Rack::Rewrite that can take care of the redirections.  I find this solution interesting because it is web server agnostic (will work with Apache, Nginx, …).  Although the performance is not as good as having rewrite rules at the web server level.

rails 2.x

First, you need install the gem:

gem install rack-rewrite

Then, you want to include a reference to the gem in config/environment.rb:

config.gem 'rack-rewrite', '~> 0.2.0'

Rails 3.x

First, you need to include the gem in your Gemfile:

gem 'rack-rewrite'

Then, we need to load the middleware in the rails stack of our application.  Instead of putting this in the environment.rb file, I prefer to put it in an initializer (config/initializers/rack_rewrite.rb) since we will also include the redirection rules in this file:

Rails 2.x

ActionController::Dispatcher.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
  r301 '/fr/rates_and_packages', '/fr/services-et-tarifs'
  r301 '/en/rates_and_packages', '/en/services-and-rates'
  r301 '/fr/information', '/fr/renseignements/horaire-et-coordonnees'
  r301 '/en/information', '/en/informations/business-hours-and-location'
  r301 %r{\A/index.php}, '/fr'
  r301 %r{/en/index.php}, '/en'
end

Rails 3.x

YourApplicationName::Application.config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
  r301 '/fr/rates_and_packages', '/fr/services-et-tarifs'
  r301 '/en/rates_and_packages', '/en/services-and-rates'
  r301 '/fr/information', '/fr/renseignements/horaire-et-coordonnees'
  r301 '/en/information', '/en/informations/business-hours-and-location'
  r301 %r{\A/index.php}, '/fr'
  r301 %r{/en/index.php}, '/en'
end

Remember to include your application namespace in the previous code snippet.

As you can see, the first rules look for a perfect match. The last two rules apply a regular expression to the request URI. This is really cool when a bunch of old URLs have a similar structure and share the same redirection URL.  There is a rewrite command that allows to transform the URL and pass it down the Rails stack.  See the documentation for more options.

Now, how do you find out what are the old URLs you need to redirect?  Well, you can go on the old site and manually crawl the site.  But I prefer to use a simple ruby crawler called Anemone.

gem install anemone

Then write some ruby code and run it.

require 'rubygems'
require 'anemone'

Anemone.crawl('http://www.awebsitetocrawl.ca') do |anemone|
  anemone.on_every_page do |page|
    puts page.url
  end
end

It will give you a list of all the URLs found on the given site. Though it does not work if the site is using Flash menus (normally, such sites should have regular navigation menus somewhere on the page for SEO purposes, but I’ve seen some sites that don’t).

That’s it. A simple way to generate redirection codes. There is a more complete solution named Refraction that you can use as well, but I am satisfied with the straight forward Rack::Rewrite solution.

Here you go, hoping it helps you in your Rails project.

Advertisements

Comment retracer ses contacts LinkedIn dans Twitter

J’ai un bon réseau de contacts dans LinkedIn et je désirais savoir ceux qui étaient dans Twitter pour possiblement les suivre sur Twitter.  A ma grande surprise, je n’ai rien trouvé pour “facilement” faire ça.

Voici donc une méthode manuelle pour y arriver.

  1. Dans votre profile LinkedIn, cliquez sur “Contacts” dans le menu à gauche pour voir la liste des tous vos contacts.
  2. Au bas de la page, cliquez sur “Exporter la liste de mes relations”.  Choisissez le format “Microsoft Outlook”.
  3. Ensuite, accédez à votre compte Gmail (ou Yahoo).  Si vous n’en n’avez pas un, ca ne coûte rien de vous en créer un.
  4. Cliquez sur “Contacts” à gauche dans Gmail.
  5. En haut à droite, vous allez voir un lien “Import”.  Cliquez ce lien et choisissez le fichier qui a été généré à l’étape 2.
  6. Sur Twitter.com, assurez-vous d’avoir ouvert une session et cliquer sur “Find people” au haut de l’écran.
  7. Cliquez sur “Find on other networks” et sélectionnez Gmail.
  8. Entrez vos informations Gmail et cliquez sur “Continue”.

Twitter va ensuite accéder à vos contacts Gmail et regarder si ils ont un compte sur Twitter.  Il vous affichera ensuite la liste et vous demandera de choisir ceux que vous désirez suivre sur Twitter.

Voilà, en espérant que ca vous aide!

Hugo