Rails 5 SSL action cable during production - Failed to connect to WebSocket: WebSocket handshake error: unexpected response 301

I am trying to run an application with rails 5.0.0beta3 and websockets. I have everything that works locally on development, but in production I get this answer in my browser console:

"Failed to connect to WebSocket: WebSocket handshake error: 301 unexpected response"

Here is my nginx conf.

upstream app { server unix:/home/dev/workspace/my_app/tmp/sockets/thin.0.sock max_fails=1 fail_timeout=15s; server unix:/home/dev/workspace/my_app/tmp/sockets/thin.1.sock max_fails=1 fail_timeout=15s; } server { listen 443 ssl; server_name www.my_app.co; root /home/dev/workspace/my_app/public; try_files $uri/index.html $uri @app; location @app { proxy_next_upstream error timeout http_502 http_503; proxy_read_timeout 60s; proxy_pass http://app; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-FORWARDED-PROTO $scheme; proxy_redirect off; } location /websocket/ { proxy_pass http://localhost:28080/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; } error_page 500 502 503 504 /500.html; client_max_body_size 4G; keepalive_timeout 10; ssl on; ssl_certificate /ssl/www.my_app.co.crt; ssl_certificate_key /ssl/www.my_app.co.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES"; ssl_prefer_server_ciphers on; } 

I work thinly for my application server and run puma for the websockets server along with redis locally. I am trying to run an example application with an example action: https://github.com/rails/actioncable-examples .

I start my puma server as follows: bundle exec puma -p 28080 cable/config.ru

Using this puma.rb in config :

 workers 1 threads 1, 10 app_dir = File.expand_path("../..", __FILE__) shared_dir = "#{app_dir}/shared" rails_env = ENV['RAILS_ENV'] || "production" environment rails_env stdout_redirect "#{shared_dir}/log/puma.stdout.log", "#{shared_dir}/log/puma.stderr.log", true pidfile "#{shared_dir}/pids/puma.pid" state_path "#{shared_dir}/pids/puma.state" 

Here are the relevant parts of my production.rb configuration:

 config.action_cable.allowed_request_origins = ['https://www.chatben.co', 'https://45.55.192.195'] config.action_cable.url = "wss://www.chatben.co/websocket" config.force_ssl = false 

Here is my development.rb config:

 config.action_cable.url = "ws://localhost:28080" config.action_cable.allowed_request_origins = ['http://localhost:3000'] config.action_cable.disable_request_forgery_protection = true 

In my application, I run my cable as follows:

 @App = {} App.cable = ActionCable.createConsumer() 

Any suggestions would be great. I noticed someone here: RoR 5.0.0 ActionCable wss Handshake WebSocket: Unexpected response code: 301 was able to solve this using a separate domain. This is what I will probably try to do next, but I was hoping it would not come.

Thanks in advance for your help! I really appreciate that.

+6
source share
4 answers

Probably the problem is that Rails is forcibly connecting to ssl. Since nginx terminates the ssl connection, you need to set the X-Forwarded-Proto header so that Rails knows that everything is fine. Here is the complete configuration that works for me:

  location /cable { proxy_pass http://app; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto https; proxy_redirect off; } 
+21
source

I could not understand my previous problem, so instead I ended up with a passenger instead. I'm still wondering why every request has 301, but hey, at least I have web clips now!

EDIT: After reading the troelskn answer above, I was able to deploy the Puma server without having to use Passenger.

+1
source

for websocket-rails, I have to do this in NGINX:

 location /websocket{ proxy_pass http://localhost:3001/websocket; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto https; proxy_redirect off; } 

and pass false to javascript to use http: // instead of ws: //

 var websocket = new WebSocketRails(window.location.hostname + '/websocket', false); 
0
source

I updated my answer, basically it was pretty easy for me that I could not understand before. I just did this in the production.rb file after setting up SSL, as usual, in the nginx conf file.

 config.action_cable.allowed_request_origins = [#domains] 

He just did everything and connected correctly.

0
source

All Articles