Obfuscate Numerical IDs in Rails
By default, Rails displays the record’s ID in the URL (e.g. http://localhost:3000/articles/1). Although there is nothing inherently wrong with this approach, sometimes it’s helpful to obfuscate the record’s ID (e.g. http://localhost:3000/articles/xb3mm6k). In this tutorial I will show you how to obfuscate numerical IDs in Rails.
Step 1. Add a Hashid Column to Your Model
In order to obfuscate our record’s IDs we’ll first need to add a column to our model to store a random value. We can call this column anything, but let’s call it hashid.
rails g migration add_hashid_to_articles hashid:stringrails db:migrate
Step 2. Set the Value of the Hashid in a Callback
Next we need to programmatically set the value of the hashid column. There are many ways to achieve this, but I like using SecureRandom.urlsafe_base64 in combination with a before_validation callback.
SecureRandom.urlsafe_base64 generates a random URL-safe base64 string.
1.
- First we create a private
set_hashidmethod that will set thehashidto the return value ofSecureRandom.urlsafe_base64(5). - Then we call this method with a
before_validationcallback.- We add
prepend: trueto ensure this callback is called beforefriendly_idset’s theslug(note that we have not yet installedfriendly_id). - We use a :if with a Proc to ensure that the
set_hashidmethod is only called if the record does not yet have ahashid. This ensures that thehashiddoes not change each time a record is updated.
- We add
Step 3. Install and Configure friendly_id
Now that we are programmatically assigning a hashid to our model, we need to use that value in the URL. Luckily the friendly_id makes this easy.
- Add
gem 'friendly_id', '~> 5.3'to yourGemfile. -
Run the following commands.
-
Next, update your model so it can use
friendly_idto set aslug -
Then update your controller to use
friendlyby replacingModel.findwithModel.friendly.find - Finally, update any existing records by opening the rails console and running
Article.find_each(&:save)
Conclusion and Next Steps
One important thing to note is that SecureRandom.urlsafe_base64 does not guarantee a unique value. This means that there’s a chance multiple records could have the same value for the hashid. Fortunately fiendly_id accounts for any conflicting slugs by appending a UUID to the slug. If you want more control over the what is appended to the url, you can use candidates.