Customized URLs (in Rails) and how to remove IDs from paths

Alberto Carreras
5 min readJun 11, 2018

If we look at this same post’s URL, we observe that the path is composed of a long string containing a slugified* version of this post title followed by a random sequence of numbers and letters.

For Ruby on Rails beginners, most of our Rails apps will work with a pretty basic database. Also, the apps routes will use the index to find for entries in our database. If Medium used that system, this post URL could look something like this: ; 12 being the id of this post in the database. In that scenario, it will be pretty likely to have a posts between 1 and 12 , and we could access them just changing the path (for instance

Customizing our URLs will make them not only prettier but also more human-readable - slugified URLs are more sensical, we can guess the page content based on the URL- and more search-engine-indexing-algorithm readable. The latter is also very important because search engine spider crawls will traverse a website through its links storing each page’s data in a database. Part of the stored data is the webpage’s URL, information that will be used for identifying attributes of a webpage and indexing it.

At the same time, using numeric sequential URLs can facilitate scrapping websites. Let’s take a look at the following website:

As we observe, BoardGameGeek uses its board game database index in each game URL. We could easily use Nokigiri to scrap all BoardGameGeek’s games from 1 to … a few hundred thousands.

We can optimize our URLs with two simple steps: installing FriendlyId in your app and slugifying the URL and changing the paths on our Rails app routes.

These following steps are based on a basic blog post model example. You can find the Rails app repository here.

Installing FriendlyId in your app

These are the basic steps to install FriendlyId and use its basic functionality. All of these steps can be found on the FriendlyId’s GitHub page.

  • Gemfile. Add gem ‘friendly_id’, ‘~> 5.1.0’ in your Gemfile and run bundle install to install it.
  • Configuration. Run the following code on your terminal rails generate friendly_id so that Rails can generate all the FriendlyId migration files. In your db/migrate folder, there will be a new migration CreateFriendlyIdSlugs. Run rake db:migrate to create the new table and add it to your database schema.
  • IMPORTANT: The models which will be using FriendlyId need to have a slug column in their database.
    If you are creating a new model, add slug:string:uniq to it. For example:
    rails g model Post title:string description:text slug:string:uniq
    If you are building up on a previous model, run the following code on the terminal to add a column to your existing table posts.
    rails g migration add_slug_to_posts slug:string:uniq
    Runrake db:migrate to execute the migration. In your db/migrate folder, there will be a new migration AddSlugToPosts.
  • Edit the model. Go to app/models/post.rb and add the following code.
class User < ApplicationRecord
extend FriendlyId
friendly_id :title, use: :slugged

  • Edit the controller. Go to app/controllers/posts_controller.rb and replace Post.find by Post.friendly.find . For example:
class UserController < ApplicationController
def show
@post = Post.friendly.find(params[:id])
endTo scrap

Our app shows the slugged title in the URL. However, if we enter http://localhost:3000/posts/1, the app will still display the post view. Both id and slug work.

Slugified URL

In order to prevent that, and only be able to access the post using the slug , we have to update the app routes to use the slugs instead of the ids.

Removing IDs from paths

  • Update your routes to use slugs. Run rake routes in your terminal. You will see something like this:
Run Rake routes on the terminal

Rails default for routes will use the :id and redirect to /posts/:id when using post_path or @post.

Go to config/routes.rb and add param: :slug to your resources. Also, change :id by :slug in any other manual routes. It should look something like this:

Rails.application.routes.draw do
resources :posts, param: :slug

Now, run rake routes in your terminal again. Your routes are redirecting to slug now.

Run Rake routes on the terminal
  • Edit the controller. Go to app/controllers/posts_controller.rb and replace Post.find(params[:id]) by Post.friendly.find_by_slug(params[:slug]) . For example:
class PostsController < ApplicationController
def show
@user = User.friendly.find_by_slug(params[:slug])

Updating Slugs with Title Changes

Our app works as we expected. However, our app has a fully CRUD interface implemented. If we update one of our posts, we want to see that change on our customized URL as well.

FriendlyId can do that for us, but we have to empty the attribute slug before it can overwrite it. One option to achieve that, go to app/controllers/posts_controller.rb and add the following code to the #updatemethod after a truthy validation:
@post.slug = nil if @post.title != params[:title]
@post.update(posts_params(:title, :description))

Check the following video to see the results of our post app with customized URLs.

*What’s a Slug? What does to Slugify mean?

Slug Source: