Rails, ActionCable and WebSockets

Hey there!

I’m trying to run a Rails application which aside from other functionality is also using WebSockets. Though I get an error when trying to connect to wss://my-domain.com (application hosted on render.com).
Do I have to do any specific configuration to allow this?

And by the way, there’s no Slack support anymore on Render.com?

Thank you!

Hello!

Web sockets should work without any configuration of Render’s settings. Can you share more details about the error you are seeing? Feel free to DM me if you’d prefer.

We have deprecated the Slack channel in favor of this community in order to build a more searchable knowledge base for users.

Thank you for replying Jake!

I’m trying to connect from a React app, this works locally and on my previous staging server (AWS) and it does not work on render.
The error I keep getting on the client app is this:

WebSocket connection to ‘wss://some-domain.com/cable?..’ failed: e.open @ 2.69d9521f.chunk.js:2

and the request does not hit the backend app, it’s somehow rejected before.

You probably already know that but there are some specific configs needed depending on the load balancers used or whatever is in front of our apps on render. On my AWS servers, the nginx config needs to include something like:

location /wsapp/ {
    proxy_pass http://wsbackend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
}

and on Apache, something like this:

<VirtualHost domain.of.the.rails-app:80>
    ServerName domain.of.the.rails-app
    DocumentRoot /var/www/apps/rails-app/public
    ProxyPreserveHost On
    ProxyPass /error-documents !
    ErrorDocument 503 /error-documents/503.html
    Alias /error-documents /var/www/apps/rails-app/public
    ProxyPass / http://0.0.0.0:3000/
    ProxyPassReverse / http://0.0.0.0:3000/
    RewriteEngine on
    RewriteCond %{HTTP:UPGRADE} websocket [NC]
    RewriteRule /(.*) ws://localhost:3000/$1 [P]
</VirtualHost>

so I’m not sure exactly what you guys use but there needs to be some specific configs in place to allow for web sockets.

Thanks again!

I’m running into some issues with ActionCable on Render as well. Although it could be because of my own code (it did work on my previous host however).

Would love to see a basic example in the render-examples repository of a working ActionCable project. Something I can host and compare my own implementation to.

( For reference, my project is https://expensive.chat but I’m currently working on it. So you might see some misconfiguration due to testing, etc. )

I’m getting the following error:

WebSocket connection to 'wss://expensive.chat/cable' failed: Unexpected response code: 301

Most results on Google and StackOverflow talk about nginx configuration. Which does suggest there might be something one Render’s side.

I think I’ve solved it by disabling force_ssl

I’m not sure why that’s needed. Maybe Render is doing something internally making Rails think the websocket connection is insecure so it tries to redirect.

3 Likes

That does make sense. Render forces SSL and automatically terminates it at the proxy, so your app receives an insecure request and returns a 301 when force_ssl is enabled.

1 Like

This fixed it for me also. Great find marc!

Thank you both!

1 Like

That makes sense. Thanks for the explanation.

I expect other people might run into this as well, as it’s common practice (and possibly even default behavior?) for Rails apps to have that setting enabled.

Not sure what you can do about it on Render’s site, but perhaps it’s worth adding a note in the docs.

3 Likes

I just wanted to say this worked for me too. Thanks!

For reference, in addition to config.force_ssl = false, I also have config.action_cable.url and config.action_cable.allowed_request_origins set in the production.rb file.

Hi @jake,

Setting force_ssl = false will also turn off HSTS and secure cookies I think. I don’t see an easy way to keep those enabled while just disabling the redirects. Do you have a recommendation here?

Tim

@jake Seems a better way to do this would be to add the X-Forwarded-Proto header, setting it to https when your proxy terminates the ssl.

We already set the X-Forwarded-Proto to wss for websocket upgrade requests, and to https for regular HTTPS requests.

Are you using Puma? We could dig into the app server code to figure out which headers it needs in addition to XFP.

Yes, using Puma. Personally I’m using Puma v5.4.0, and Rails 6.1.4.1 (both current).

I don’t know if you made adjustments already, but my websocket connections are succeeding now. :slight_smile:

We didn’t change anything! Did your force_ssl config change?

No. config.force_ssl = true is still there. I did deploy a few times though.

I’ll keep an eye on it.

1 Like

After spending some more time with it today, I don’t think my problem was with ssl. I’m intermittently connecting okay. Usually if I refresh the browser, it’ll fail 1 - 10 times before successfully connecting for some reason. If it succeeds sometimes, the problem is likely elsewhere.

Following up over DM.