Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActionCable - Error during WebSocket handshake: Unexpected response code: 404

Have deployed my Rails 5.2 app via Capistrano, and am having problems with ActionCable. I am using Nginx, Puma and Lets Encrypt.

I have tried many combinations of configuration, but each time am receiving the same error. I am not sure how to debug this issue, and suggestions as well and any tips on re-arranging my ngnx.conf would be well-appreciated.

Have changed the real website to website.com

nginx.conf

upstream puma {
  server unix:///home/deploy/apps/website/shared/tmp/sockets/website-puma.sock;
}

server {
  server_name website.com www.website.com;
  root /home/deploy/apps/website/current/public;
  index index.html;

  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 Upgrade websocket;
    proxy_set_header Connection Upgrade;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 10M;
  keepalive_timeout 10;

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/website.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/website.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = www.website.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = website.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


  listen 80 default_server;
  listen [::]:80 default_server ipv6only=on;

  server_name website.com www.website.com;
    return 404; # managed by Certbot
}

config/production.rb

  config.action_cable.mount_path = '/cable'
  config.action_cable.url = 'wss://website.com/cable'
  config.action_cable.allowed_request_origins = [ 'https://website.com', 'http://website.com' ]

Error Message

enter image description here

Reviewed posts

  • Rails5 Action Cable Nginx 404 and 502 errors
  • ActionCable on AWS: Error during WebSocket handshake: Unexpected response code: 404
  • Actioncable Nginx and Puma WebSocket handshake: Unexpected response

UPDATE

Updated nginx.conf

upstream puma {
  server unix:///home/deploy/apps/immersive/shared/tmp/sockets/immersive-puma.sock;
}


  server {
    if ($host = www.immersive.ch) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = immersive.ch) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    server_name immersive.ch www.immersive.ch;
    return 404; # managed by Certbot
  }

  server {
    server_name immersive.ch www.immersive.ch;
    root /home/deploy/apps/immersive/current/public;
    index index.html;

    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 Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_pass_request_headers on;

      proxy_buffering off;
      proxy_redirect off;
      break;
    }

    error_page 500 502 503 504 /500.html;
    client_max_body_size 10M;
    keepalive_timeout 10;

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/immersive.ch/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/immersive.ch/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
  }

production.rb

config.action_cable.mount_path = '/cable'
config.action_cable.url = 'wss://immersive.ch/cable'
config.action_cable.allow_same_origin_as_host = true
config.action_cable.allowed_request_origins = [ '*' ]
#config.action_cable.allowed_request_origins = [ 'https://immersive.ch', 'http://immersive.ch' ]

Partial output of curl

> GET /cable HTTP/1.1
> Host: immersive.ch
> User-Agent: curl/7.54.0
> Accept: */*
> Origin: https://immersive.ch
> Sec-WebSocket-Key: MIN4DsiwEAutsE11kgG5rg==
> Upgrade: websocket
> Connection: Upgrade
> Sec-WebSocket-Version: 13
>
< HTTP/1.1 404 Not Found
< Server: nginx/1.15.5 (Ubuntu)
< Date: Tue, 16 Apr 2019 20:10:43 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Connection: keep-alive
< Cache-Control: no-cache
< X-Request-Id: 7a9aa8f1-676d-419b-9e4f-0c1bb38bcaa2
< X-Runtime: 0.004730
<
* Connection #0 to host immersive.ch left intact
Page not found%

puma.rb

threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
port        ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
workers 2
daemonize true
plugin :tmp_restart

EDIT 2

/var/logs/nginx/access.log

HTTP/1.1" 200 4447 "https://immersive.ch/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"
xxx.xxx.xxx.xxx - - [16/Apr/2019:22:33:58 +0200] "GET /cable HTTP/1.1" 404 24 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"
xxx.xxx.xxx.xxx - - [16/Apr/2019:22:33:59 +0200] "GET /cable HTTP/1.1" 404 24 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"

puma.error.log

I, [2019-04-16T22:39:06.100106 #21136]  INFO -- : [80dc2a43-13e1-499e-a8f6-9fd54d48270b] Started GET "/cable" for xxx.xxx.xxx.xxx at 2019-04-16 22:39:06 +0200
I, [2019-04-16T22:39:06.103811 #21136]  INFO -- : [80dc2a43-13e1-499e-a8f6-9fd54d48270b] Started GET "/cable/"[non-WebSocket] for xxx.xxx.xxx.xxx at 2019-04-16 22:39
E, [2019-04-16T22:39:06.103943 #21136] ERROR -- : [80dc2a43-13e1-499e-a8f6-9fd54d48270b] Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: close, HTTP_UPGRADE: )
I, [2019-04-16T22:39:06.104062 #21136]  INFO -- : [80dc2a43-13e1-499e-a8f6-9fd54d48270b] Finished "/cable/"[non-WebSocket] for xxx.xxx.xxx.xxx at 2019-04-16 22:39

puma.access.log

2019-04-16 20:58: HTTP parse error, malformed request (127.0.0.1): #<Puma::HttpParserError: Invalid HTTP format, parsing fails.>
like image 443
port5432 Avatar asked Dec 22 '25 21:12

port5432


1 Answers

ActionCable answers 404 when Origin header is missing or does not match/invalid.

Check that it's not you regular 404:

curl -v 'https://your_site.com/cable' -H 'Origin: https://your_site.com' -H 'Sec-WebSocket-Key: MIN4DsiwEAutsE11kgG5rg=='  -H 'Upgrade: websocket' -H 'Connection: Upgrade' -H 'Sec-WebSocket-Version: 13'

When everything is fine there'll be HTTP/1.1 101 Switching Protocols, on origin mismatch - just Page not found body, or your regular 404 page if there's some other routing trouble.

Make sure allowed_request_origins in settings is correct. Note that it includes port, if it's non-standard. Check which Origin browser sends in devtools

Also there's config.action_cable.allow_same_origin_as_host = true (the default, needs correct Host and X-Forwarded-Proto headers)

Then we need nginx to pass all the headers that are being used to reconstruct origin:

  location /cable {
    proxy_pass http://puma;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme; # <- most probably this one is missing
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass_request_headers on; # this is default, but just to be sure

    proxy_buffering off;
    proxy_redirect off;
    break;
  }

Update:

Remaining two cases when activecable responds with this is failed connection authentication and missing websocket driver(should not be the case for puma)

like image 151
Vasfed Avatar answered Dec 24 '25 10:12

Vasfed