ActionCable doesn't work in production. Works well in development, but not in production.
Running Nginx with Puma on Ubuntu 14.04. I have checked that 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 client:
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
Response:
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:
Don't forget to restart nginx :) That was the problem for me.
You should change the value of proxy_pass property from http://puma to http://puma/cable.
Therefore, the correct location section for the /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";
}
While other posts have correctly posted solutions I thought I'd post a bit more info on how to identify where the problem is/where to fix it for other nginx noobs.
You will know your nginx config needs the proxy_set_header Upgrade at the path you're mounting action cable if the rails error contains HTTP_UPGRADE:. (Meaning nothing is passed to HTTP_UPGRADE). After fixing the problem my logs are showing HTTP_UPGRADE: websocket
Gotchya 1: As mentioned by the op, make sure you restart nginx after making a change(I was incorrectly doing this).
Gotchya 2: Also look for include statements in the nginx config as your config could be split across multiple files. The location /cable { section should be inside of server { which in my case was missing because it was in a different config file from an includes statement which I didn't notice for a while.
Similar error but different problem: Your rails logs will contain an additional error in the logs right before the one the OP mentioned saying the origin is not allowed, that is when your rails config needs to be updated as another answer mentions updating config.action_cable.allowed_request_origins.
The logging is subject to change with rails but hopefully this helps clarify where the problem and a few gotchya's I encountered as someone who knows nothing about nginx.
Super late to this conversation, however, for anyone who is facing the same error message using Rails5, Action Cable, etc. & DEVISE you simply solve it like suggested here. It all comes down to the web socket server not having a session, hence the error message.
app/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
app/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
Solution was developed by Greg Molnar
The resolution that needs a NGINX configuration changes 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.
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' ]
You can change you nginx config about /cable
proxy_set_header X-Forwarded-Proto http;
I used you nginx config and add this change on myu server, it works fine.
Worked with:
location ^~ /cable {
...
}
Location requires ^~
Your cable.yml file should look like this:
production:
adapter: redis
url: <%=ENV['REDIS_URL']%>
Then you should have this key set up in the environment, should look something like this:
REDIS_URL: 'redis://redistogo:keyblahblahblhblah'
Also, you should have this in production.rb:
config.web_socket_server_url = "wss://YOUR_URL.com/cable"
Related
I have been struggling to get my Rails app deployed correctly for a while now, and have decided it is finally time to consult the community for some help. I have read just about every stackoverflow post on this issues, including the following, with no luck:
Rails 5 ActionCable fails to upgrade to WebSocket on Elastic Beanstalk -> From this post I ensured I was using an Application Load Balancer
ActionCable on AWS: Error during WebSocket handshake: Unexpected response code: 404 -> Configured an nginx proxy, with no change
Problem Description
I am using the following setup:
Ruby 2.7.5
Rails 6.1.0
GraphQL
React Frontend (separate repo)
Elastic Beanstalk
Ruby 2.7 running on 64bit Amazon Linux 2/3.4.1
Application Load Balancer
Postgres ActionCable adapter
My application is deployed to AWS Elasticbeanstalk and all requests to /graphql are successful. However, when attempting to connect to /cable I get this error in my browser console:
WebSocket connection to 'wss://api.redacted.io/cable' failed:
When checking the Elastic Beanstalk logs I see:
/var/app/containerfiles/logs/production.log:
I, [2022-02-20T19:35:25.849990 #32761] INFO -- : [8e1d3e86-81cc-4708-89d3-ebad56470f8f] Started GET "/cable" for <redacted IP> at 2022-02-20 19:35:25 +0000
I, [2022-02-20T19:35:25.850342 #32761] INFO -- : [8e1d3e86-81cc-4708-89d3-ebad56470f8f] Started GET "/cable/"[non-WebSocket] for <redacted IP> at 2022-02-20 19:35:25 +0000
E, [2022-02-20T19:35:25.850384 #32761] ERROR -- : [8e1d3e86-81cc-4708-89d3-ebad56470f8f] Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: close, HTTP_UPGRADE: )
I, [2022-02-20T19:35:25.850419 #32761] INFO -- : [8e1d3e86-81cc-4708-89d3-ebad56470f8f] Finished "/cable/"[non-WebSocket] for <redacted IP> at 2022-02-20 19:35:25 +0000
Potentially Relevant Files
cable.yml:
development:
adapter: postgresql
test:
adapter: test
production:
adapter: postgresql
production.rb:
...
config.action_cable.url = 'wss://api.redacted.io/cable'
config.action_cable.allowed_request_origins = ['redacted.io', 'http://redacted.io', 'https://redacted.io']
...
.ebextensions/nginx_proxy.config:
files:
"/etc/nginx/conf.d/websockets.conf" :
content: |
upstream backend {
server unix:///var/run/puma/my_app.sock;
}
server_names_hash_bucket_size 128;
server {
listen 80;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
server_name redacted.elasticbeanstalk.com;
# prevents 502 bad gateway error
large_client_header_buffers 8 32k;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
# prevents 502 bad gateway error
proxy_buffers 8 32k;
proxy_buffer_size 64k;
proxy_pass http://backend;
proxy_redirect off;
location /assets {
root /var/app/current/public;
}
# enables WS support
location /cable {
proxy_pass http://backend/cable;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
}
container_commands:
01restart_nginx:
command: "nginx -t && service nginx restart"
After posting on reddit, I was able to fix my issue by:
Removing my .ebextensions/nginx_proxy.config file.
Creating a new file, .platform/nginx/conf.d/elasticbeanstalk/websocket.conf with the contents:
location /cable {
proxy_pass http://my_app/cable;
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-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
I'm having trouble getting ActionCable hooked up in my prod environment, and related questions haven't had a working solution. I'm using an nginx+puma setup with Rails 6.1.3.2 on Ubuntu 20.04. I have confirmed that redis-server is running on port 6379, and that Rails is running as production.
Here's what I'm getting in my logs:
I, [2021-05-25T22:47:25.335711 #72559] INFO -- : [5d1a85f7-0102-4d25-bd4e-d81355b846ee] Started GET "/cable" for 74.111.15.223 at 2021-05-25 22:47:25 +0000
I, [2021-05-25T22:47:25.336283 #72559] INFO -- : [5d1a85f7-0102-4d25-bd4e-d81355b846ee] Started GET "/cable/"[non-WebSocket] for 74.111.15.223 at 2021-05-25 22:47:25 +0000
E, [2021-05-25T22:47:25.336344 #72559] ERROR -- : [5d1a85f7-0102-4d25-bd4e-d81355b846ee] Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: close, HTTP_UPGRADE: )
I, [2021-05-25T22:47:25.336377 #72559] INFO -- : [5d1a85f7-0102-4d25-bd4e-d81355b846ee] Finished "/cable/"[non-WebSocket] for 74.111.15.223 at 2021-05-25 22:47:25 +0000
This happens every few seconds. You can see matching output in the browser console:
For one, I'm pretty sure that I need to add some sections to my nginx site config, such as a /cable section, but I haven't figured out the correct settings. Here's my current config:
server {
root /home/rails/myapp/current/public;
server_name myapp.com;
index index.htm index.html;
location ~ /.well-known {
allow all;
}
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# needed to allow serving of assets and other public files
location ~ ^/(assets|packs|graphs)/ {
gzip_static on;
expires 1y;
add_header Cache-Control public;
add_header Last-Modified "";
add_header ETag "";
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/myapp.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 = myapp.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name myapp.com;
return 404; # managed by Certbot
}
Here's my config/cable.yml:
development:
adapter: async
test:
adapter: test
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: myapp_production
In config/environments/production.rb, I've left these lines commented out:
# Mount Action Cable outside main process or domain.
# config.action_cable.mount_path = nil
# config.action_cable.url = 'wss://example.com/cable'
# config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
I have not mounted ActionCable manually in my routes or anything. This is as standard of a setup as you can get. I'm thinking the answer lies centrally in a correct nginx configuration, but I don't know what it should be. Perhaps there are Rails config settings that are needed too, though. I don't remember having to change any when deploying with passenger, but maybe puma is a different story.
Update
I also noticed that a lot of the proposed solutions in other questions, like this one, seem to reference a .sock file in tmp/sockets/. My sockets/ directory is empty, though. Web server's running fine besides ActionCable though.
Update #2
I also noticed that changing the config.action_cable.url to something like ws://myapp.com instead of wss://myapp.com has no effect even after restarting Rails. The browser console errors still say its trying to connect to wss://myapp.com. Possibly due to how I'm set up to force redirect HTTP to HTTPS. I wonder if that has anything to do with it?
I got it working. Here are the settings I needed:
nginx config
The server section from the config in my question must be modified to include the following two sections for the / and /cable locations:
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /cable {
proxy_pass http://localhost:3000;
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;
}
config/environments/production.rb
# Change myapp.com to your app's location
config.action_cable.allowed_request_origins = [ 'https://myapp.com' ]
config.hosts << "myapp.com"
config.hosts << "localhost"
Huge thanks to Lam Phan in the comments for helping me out.
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
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.>
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)
Could not connect websocket using Action Cable in Rails 5.1. HTTP server is Unicorn on nginx and adapter is Redis.
Rails configuration is the following.
# config/environments/production.rb
config.action_cable.disable_request_forgery_protection = true
nginx configuration is the following.
upstream unicorn {
server unix:/rails/current/tmp/sockets/unicorn.sock;
}
server {
listen 80;
charset utf-8;
server_name sub.example.com;
root /rails/current/public;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://unicorn;
break;
}
}
location /cable {
proxy_pass http://unicorn;
proxy_http_version 1.1;
proxy_set_header Upgrade "websocket";
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
}
...
}
Errors in console of web browser are the following.
WebSocket connection to 'wss://sub.example.com/cable' failed: Error during WebSocket handshake: Unexpected response code: 404
Errors in Rails are the following.
[ERROR] Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: close, HTTP_UPGRADE: )
[INFO] Finished "/cable/"[non-WebSocket] for xxx.xxx.xxx.xxx at 2017-xx-xx
A strangest thing is a error in Rails does not have HTTP_UPGRADE value. But HTTP request headers of web browser include Upgrade key and websocket value. Also setting "websocket" for proxy header in nginx configurations.
What should I do?
On the back of that writeup, I just realized the universal solution is
a simple change to the config/secrets.yml file to reference the
ENV["PORT"] setting
...
# Be sure to restart your server when you modify this file.
development:
secret_key_base: 231bf79489c63f8c8facd7...
action_cable_url : http://localhost:<%= ENV["PORT"] %>
test:
secret_key_base: 1ab8adbcf8410aebb...
action_cable_url : http://localhost:<%= ENV["PORT"] %>
# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
action_cable_url : <%= ENV["SERVER_PORT"] %>
.
You can also have this in your dot file:
export PORT=3000
source
Good day!
I want to run websocket app with Faye gem, but the following problem occurs: when I deploy my app on production server, Ngix can't receive faye.js and can't connect to faye server. In Nginx error.log I found next errors:
2014/12/24 15:44:39 [error] upstream prematurely closed connection while reading response header from upstream, client: 46.0.121.23, server: example.com, request: "GET /faye HTTP/1.1", upstream: "http://127.0.0.1:9292/faye", host: "example.com"
2014/12/24 15:44:39 [error] *1 connect() failed (111: Connection refused) while connecting to upstream, client: 46.0.121.23, server: example.com, request: "GET /faye HTTP/1.1", upstream: "http://127.0.0.1:9292/faye", host: "example.com"
I try the How to start faye server on a rails app deployed using dokku? and Error 502 Bad Gateway on NGINX + rails + dokku answers, but it's not help for me.
My Procfile is
web: bundle exec rails s Puma -p 5000
faye: bundle exec rackup s Puma faye.ru
My faye.ru is
require 'faye'
require File.expand_path('../config/initializers/faye_token.rb', __FILE__)
class ServerAuth
def incoming(message, callback)
if message['channel'] !~ %r{^/meta/}
if message['ext']['auth_token'] != FAYE_TOKEN
message['error'] = 'Invalid authentication token.'
end
end
callback.call(message)
end
end
faye_server = Faye::RackAdapter.new(:mount => '/faye', :timeout => 0)
faye_server.add_extension(ServerAuth.new)
run faye_server
My nginx.conf is:
upstream example.com { server 127.0.0.1:49169; }
server {
listen [::]:80;
listen 80;
server_name example.com;
location / {
proxy_pass http://example.com;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Request-Start $msec;
}
location /faye {
proxy_redirect off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache_bypass $http_pragma $http_authorization;
proxy_no_cache $http_pragma $http_authorization;
proxy_pass http://localhost:9292;
}
}
My app get faye.js next:
<%= javascript_include_tag "http://example.com/faye.js" %>
And connect to faye server
$(function() {
var faye = new Faye.Client('http://example.com/faye');
faye.subscribe('/comments/new', function (data) {
eval(data);
});
});
What can I do? In development enviroment all works fine, but in production only errors.
Try to config from this gist
https://gist.github.com/Bubelbub/0a942a0d51a3d329897d
In server section:
large_client_header_buffers 8 32k;
In faye location:
proxy_buffers 8 32k;
proxy_buffer_size 64k;