I ran into a lot of WebSocket trouble when trying to deploy this app to Heroku. I found a lot of different suggestions for how to fix the issue and just about tried them all.
I do not wish that brand of despair on anyone, so I distilled all of the fixes down to what actually solved the problem for me and wrote the following step-by-step guide for deploying an app utilizing ActionCable to Heroku.
This post assumes you have already successfully implemented ActionCable in your development environment. If you have not, check out this excellent primer on how to setup ActionCable in a Rails/React app by Dakota Lillie (this was my guiding light through the ActionCable darkness).
I’m also assuming you’ve already deployed your Rails API to Heroku and came across this blog as you desperately scoured the Internet for answers to your errors. If not, follow Heroku’s documentation to initially deploy your API.
To begin, take a look at the
config/cable.yml file in your Rails directory. You’ll see that under
development the adapter is set to
async, but under
production it’s set to
redis. Also, production has two additional properties:
Unless you’re planning to have more than one app access your API, you can comment out the
channel_prefix bit. As for the
url part, we’ll get to that in a moment.
One thing, at least, is clear; in order to use ActionCable in production, we’ll need to setup Redis. Go to your Gemfile and uncomment
gem ‘redis’, ‘~> 4.0’. Then run
bundle in your terminal to install the gem.
Now we need to add the
redistogo add-on to your Heroku app. From your Rails directory, type
heroku addons:create redistogo:nano.
Heroku will prompt you to verify your billing information if you do not already have a credit card setup on your account. Don’t worry, the Nano plan designated in the above command is free. It’s fairly limited (it only allows 10 connections), but it’ll get your app up and running. Here’s the pricing information for all
redistogo plans if you anticipate higher traffic.
After you’ve added on
redistogo, you’ll receive a Redis URL. Type
heroku config — app <your heroku app name> | grep REDISTOGO_URL in your terminal to view this URL. Your Heroku app name is the pleasant phrase and string of numbers preceding
herokuapp.com in your Heroku domain (mine is
Copy the Redis URL from your terminal and go back to the
config/cable.yml file in your Rails API. Replace whatever’s after
production with your URL. Do not put quotes around your Redis URL.
Now open the
config/environments/production.rb file in your Rails API. Scroll down until you find the following:
# Mount Action Cable outside main process or domain.# config.action_cable.mount_path = ‘/cable’# config.action_cable.url = ‘example.com’# config.action_cable.allowed_request_origins = [ ‘http://example.com', /http:\/\/example.com.*/ ]
Uncomment the last two lines and fill in your Heroku URLs like this:
config.action_cable.url = 'wss://whispering-crag-66572.herokuapp.com/cable'config.action_cable.allowed_request_origins = [ 'https://fathomless-cove-56346.herokuapp.com', /http:\/\/fathomless-cove-56346.herokuapp.com.*/ ]
The first line
config.action_cable.url is where you put the ActionCable route you setup in your
config/routes.rb file. My route looks like this:
mount ActionCable.server => ‘/cable’
config.action_cable.url is the domain Heroku provided for my Rails API plus my ActionCable route.
You might have noticed that I’m using
wss:// for this instead of the
ws:// you most likely used in development to preface your WebSocket route in React. Because your Heroku domain uses the secure
https://, your Rails API will dually use the secure WebSocket protocol,
wss://. Make sure to account for this difference on your front end.
The second line,
config.action_cable.allowed_request_origins, looks a little strange, but it’s pretty straightforward. The first item in the array is the
https:// Heroku domain for my React app written as a string.
Make sure that this URL matches exactly what your front end is sending as the
HTTP_ORIGIN. I wasted a couple hours debugging only to find that I just needed to remove a slash at the end of my URL to get everything working.
If this is giving you trouble, you can find the
HTTP_ORIGIN in the Heroku logs for your API. While in your Rails directory, type
heroku logs --tail into the terminal to track your logs. If the WebSocket connection is failing, you should see blocks of logs that look like this:
I feel like a hacker in a movie when I look at these logs
On the right-hand side of the picture above, you should see a log (four lines from the top) that says
Request origin not allowed: https://fathomless-cove-56346.herokuapp.com. That URL is the
HTTP_ORIGIN that my front end sends in the header when establishing the WebSocket connection.
Ok, so that covers the first item in the
allowed_origins array. I went down a little bit of a tangent, so let’s take another look at what I’m talking about:
config.action_cable.allowed_request_origins = [ 'https://fathomless-cove-56346.herokuapp.com', /http:\/\/fathomless-cove-56346.herokuapp.com.*/ ]
The second item in the above array is the regex version of your front end’s Heroku domain using
http://. I believe this is written in regex to cover any
http requests that are not secure. For example, I discovered that this allows for requests from both
Now, push your Rails API to Heroku again, and that should do it! I hope this guide can save at least one other person from the pit of debugging despair I found myself in.