ActionCable - Failed to switch to WebSocket during production

ActionCable does not work in production. Works well in development, but not in production.

Running Nginx from Puma on Ubuntu 14.04. I checked that the redis server is up and running.

Rails -v 5.0.0.1

production.log :

 INFO -- : Started GET "/cable/"[non-WebSocket] for 178.213.184.193 at 2016-11-25 14:55:39 +0100 ERROR -- : Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: close, HTTP_UPGRADE: ) INFO -- : Finished "/cable/"[non-WebSocket] for 178.213.184.193 at 2016-11-25 14:55:39 +0100 

Request from customer:

 GET ws://mityakoval.com/cable HTTP/1.1 Host: mityakoval.com Connection: Upgrade Pragma: no-cache Cache-Control: no-cache Upgrade: websocket Origin: http://mityakoval.com Sec-WebSocket-Version: 13 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36 Accept-Encoding: gzip, deflate, sdch Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4,uk;q=0.2,nb;q=0.2 Cookie: _vaktdagboka_session=****** Sec-WebSocket-Key: ******* Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits Sec-WebSocket-Protocol: actioncable-v1-json, actioncable-unsupported 

Answer:

 HTTP/1.1 404 Not Found Server: nginx/1.4.6 (Ubuntu) Date: Fri, 25 Nov 2016 13:52:21 GMT Content-Type: text/plain Transfer-Encoding: chunked Connection: keep-alive Cache-Control: no-cache X-Request-Id: d6374238-69ef-476e-8fc5-e2f8bbb663de X-Runtime: 0.002500 

nginx.conf :

 upstream puma { server unix:///home/mityakoval/apps/vaktdagboka/shared/tmp/sockets/vaktdagboka-puma.sock; } server { listen 80 default_server deferred; # server_name example.com; root /home/mityakoval/apps/vaktdagboka/current/public; access_log /home/mityakoval/apps/vaktdagboka/current/log/nginx.access.log; error_log /home/mityakoval/apps/vaktdagboka/current/log/nginx.error.log info; location ^~ /assets/ { gzip_static on; expires max; add_header Cache-Control public; } try_files $uri/index.html $uri @puma; location @puma { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://puma; } location /cable { proxy_pass http://puma; proxy_http_version 1.1; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; } error_page 500 502 503 504 /500.html; client_max_body_size 10M; keepalive_timeout 10; } 

cable.yml :

 redis: &redis adapter: redis url: redis://127.0.0.1:6379 production: *redis development: adapter: async test: adapter: async 

in production.rb :

 config.action_cable.allowed_request_origins = ["http://mityakoval.com"] 

in routes.rb :

 mount ActionCable.server, at: '/cable' 

UPDATE:

Remember to restart nginx :) It was a problem for me.

+8
ruby ruby-on-rails ruby-on-rails-5 nginx actioncable
source share
7 answers

At the end of this conversation, it’s late, for those who encounter the same error message using Rails5, Action Cable, etc. and DEVISE , you just solve it as suggested here . It all comes down to the fact that the web socket server does not have a session, so an error message.

Application / channels / application_cable / connection.rb

 module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user logger.add_tags 'ActionCable', current_user.name end protected def find_verified_user verified_user = User.find_by(id: cookies.signed['user.id']) if verified_user && cookies.signed['user.expires_at'] > Time.now verified_user else reject_unauthorized_connection end end end end 

application / Config / Initializers / warden_hooks.rb

 Warden::Manager.after_set_user do |user,auth,opts| scope = opts[:scope] auth.cookies.signed["#{scope}.id"] = user.id auth.cookies.signed["#{scope}.expires_at"] = 30.minutes.from_now end Warden::Manager.before_logout do |user, auth, opts| scope = opts[:scope] auth.cookies.signed["#{scope}.id"] = nil auth.cookies.signed["#{scope}.expires_at"] = nil end 

The solution was developed by Greg Molnar.

+1
source share

You must change the value of the proxy_pass property from http://puma to http://puma/cable .

Therefore, the correct location section for /cable will be:

location /cable { proxy_pass http://puma/cable; proxy_http_version 1.1; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; }

+1
source share

Work with:

 location ^~ /cable { ... } 

Location requires ^~

+1
source share

Your cable.yml file should look like this:

 production: adapter: redis url: <%=ENV['REDIS_URL']%> 

Then you should install this key in the environment, it should look something like this:

 REDIS_URL: 'redis://redistogo:keyblahblahblhblah' 

In addition, you should have this in the production.rb file:

 config.web_socket_server_url = "wss://YOUR_URL.com/cable" 
0
source share

You can change the nginx o /cable configuration

proxy_set_header X-Forwarded-Proto http;

I used nginx configuration and add this change on myu server, it works fine.

0
source share

My solution was to add these lines to my production.rb file:

  config.action_cable.url = 'ws://your_site.com/your_action_cable' config.action_cable.allowed_request_origins = [ 'http://your_site.com' ] 
0
source share

The permission that needs NGINX configuration is changed to accept this action cable request .

 location / { proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } 

Add the above lines to your location block in the nginx site configuration, then restart nginx.

0
source share

All Articles