How to hide IDs from page URLs in Ruby on Rails
Ruby on Rails uses transparent URLs by default:
x.com/users/4 signals that this is the 4th user on the site. But sometimes, you don't want to let someone know that they're your 4th user! This post will teach you how to hide these ids; in other words, to turn
To do this, we'll implement a gem called hashid-rails by Justin Cypret. It's an 80-line wrapper for the Hashids project, and it can do all the work from the model. This allows your database to use integers as IDs, but the ID will appear as a hash like x4g59d in the URL. Add it to your Gemfile:
gem "hashid-rails", "~> 1.0"
And then run
Create a file at config/initializers/hashid.rb, and add the following. Customize
config.salt to a word or phrase that you like - it'll scramble the ID to your project.
Hashid::Rails.configure do |config| # The salt to use for generating hashid. Prepended with table name. config.salt = "Mark is the best" # The minimum length of generated hashids config.min_hash_length = 6 # The alphabet to use for generating hashids config.alphabet = "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "1234567890" # Whether to override the `find` method config.override_find = true # Whether to sign hashids to prevent conflicts with regular IDs (see https://github.com/jcypret/hashid-rails/issues/30) config.sign_hashids = true end
Then, in whatever model you'd like a hashed URL, include
Hashid::Rails. This is what it would look like inside models/user.rb:
class User < ApplicationRecord include Hashid::Rails end
Now, start up your server, take a look at your user (or whatever type of record you're implementing this on), and it'll have a hashed URL!
The only problem is that it can still be accessed via its numerical URL. For instance, although you can access it at x.com/users/g7j3dV, you can still access it at x.com/users/2, too. Let's fix that so people can't look through your records sequentially.
I recommend turning off
config.override_find inside the Hashid initializer that we created earlier. This isn't necessary, but it helps us avoid shooting ourselves in the foot as we continue developing the application:
# Whether to override the `find` method config.override_find = false
And inside your controller, use
find_by_hashid instead of
# users_controller.rb # ... def set_user @user = User.find_by_hashid(params[:id]) end
Now, people can't look at users via their ID – they need to access them via hash.
If you enjoyed this post, please make a t-shirt with my face on it.