Last Updated: February 25, 2016
· widescape

Redirecting old ID routes to new slugs

I just updated my models to use slugs by installing the Stringex gem. Now I wanted to redirect those old URLs that my users know and use to the new slug URLs.

/answers/3 => /answers/everything

But since someone could come up with some full digit answer title (whatever the question) we should still allow full digit URLs like:


Sure, in the unlikely case that someone clicked on an outdated link like /answers/42 with 42 being not a slug, but an ID from before the route changed - while in the meantime someone else created a new answer with that digit title, he will not get the answer he was looking for. But hey, it'll be still an answer!


Disable the default #show route and create a #show route that accepts an ID or slug.

# config/routes.rb
resources :answers, :except => :show
get "answers/:id_or_slug" => 'answers#show', :as => :answer

Then enhance your controller's #show action to redirect with a 301 (Moved Permanently) if no instance was found using the slug, but using the ID. And raise a RoutingError if no instance was found at all.

# app/controllers/answers_controller.rb
def show
  @answer = Answer.find_by_url(params[:id_or_slug])
  if @answer.nil?
    @answer = Answer.find_by_id(params[:id_or_slug])
    if @answer.nil?
      raise'Not Found')
      redirect_to @answer, :status => 301

Note: If you're using resource loaders like CanCan, you'll need to disable resource loading for the action #show:

# app/controllers/answers_controller.rb
load_and_authorize_resource :except => :show, :find_by => :url
authorize_resource :only => :show

2 Responses
Add your response

hey great tip! though if you go to a url with an id the url in the browser is still the id route. how would you go about changing that? been working on a fix for my site, because i dont want people to copy the old links, and share them.

over 1 year ago ·

@koryteg The above solution teaches users the new slug URLs by always redirecting them to the slug URLs (like /answers/everything) when they used the id (such as /answers/42). So the old id URLs will never be made public by your own website. There's always that redirect.

over 1 year ago ·