Rails5 Action Cable Nginx 404 and 502 errors - ruby-on-rails

Everyone :).
I know people have already faced alot of problems related to mine. I have tried all but my issue has not been resolved. I have been working from past 3 days to fix this but I am unable to do it.
I am using ActionCable for the first time and on development server it is working fine. But in production where I am using Puma and Nginx I am facing terrible issues.
Initially when I had not (location /cable) settings in nginx configuration, server gives me 404 handshake error
i.e Error during WebSocket handshake: Unexpected response code: 404
Then after I add following location /cable configuration in nginx configuration I start getting 502 bad gateway error.
Note: I have not opened any port specifically for ActionCable. I assume it is not required. only port 80 is open on my server.
I need some expert to help me with this. I need quick help to get it fixed. Thanks in advance :)
I have these two lines present in my environment/production.rb
config.action_cable.url = "ws://my_linode_domain/cable"
config.action_cable.allowed_request_origins = [/http:\/\/*/, /https:\/\/*/]
This is my nginx config file
upstream app {
# Path to Puma SOCK file, as defined previously
server unix:/home/deploy/artcrate/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
listen 80;
#server_name localhost;
server_name my_linode_domain
# prevents 502 bad gateway error
large_client_header_buffers 8 32k;
root /home/deploy/artcrate/current/public;
try_files $uri/index.html $uri #app;
location / {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_pass http://app;
}
location ~ ^/(assets|fonts|system)/|favicon.ico|robots.txt {
gzip_static on;
expires max;
add_header Cache-Control public;
}
location /cable{
proxy_pass http://app;
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;
}
I have tried various proxy_pass options in location /cable settings but none worked.

Everyone. :)
After a week of struggle, hardwork and constantly playing around with nginx and puma configuration files and keenly reading blogs again and again, I was able to figure out the issue.
My nginx configurations were correct. I had to add two more lines to puma.rb configuration which don't come with default configurations. Those two lines are:
workers 2
daemonize true
daemonize true: this tells puma to run in the background by spawning a subprocess and detaching it from the executing shell. If you don't use daemonize, you need to run the puma process via nohup and put it in the background explicitly.
I am not sure if I required workers 2 but I had added them while resolving my issue. So I let it there. But after adding above two lines my ActionCable started to work normally.

RAILS and RUBY version I am using for this project is
Rails 5.0.7
ruby 2.3.1p112
ok. So today I wanted to integrate Action Cable again in my project but faced the same issue again. I applied my above solution but it didn't work. Last time although it worked but I wasn't satisfied with the solution thinking that why would ActionCable work in single thread/worker on local machine.
But this time I focussed and figured out the culprit.
Culprit is NGINX configuration
Configuration when I was facing 404 handshake errors
location /cable {
proxy_pass http://example.com;
proxy_http_version 1.1;
proxy_set_header Upgrade websocket;
proxy_set_header Connection Upgrade;
}
Configuration when all started working fine.
location /cable {
proxy_pass http://puma;
proxy_http_version 1.1;
proxy_set_header Upgrade websocket;
proxy_set_header Connection Upgrade;
}
so the culprit line was:
proxy_pass http://example.com;
Here we are pointing it to NGINX which is wrong, it should point to our puma server path which in my configuration is represented by 'puma'.
Here is the Summary of my implementation of ActionCable and its working copy on Production Server
So to integrate Action Cable with Rails 5 you need to follow following steps:
Setup Redis on default port.
Add these lines in environments/staging.rb or environments/production.rb, depending on your application environment.
config.action_cable.url = [/ws://*/, /wss://*/]
config.action_cable.allowed_request_origins = [/http://*/, /https://*/]
Finally setup you NGINX file as explained above. Here is my complete NGINX configuration in gist nginx.conf. I had replaced my site name with 'example.com' and project name with 'example'. So if you are copying anything, just make sure you replace those with yours otherwise nothing will work as paths will be broken.
I hope this will really release the pain while pushing ActionCable to live application and resolve this handshake error for anyone as this is very very tricky and technical thing and a lot of docs just mention to point action cable to your main site url and not puma server running behind your nginx.
Thanks.

Related

.Net core POST API in NGINX reverse proxy throws error 404

This is my first time posting a question. Kindly let me know if I am missing something that needs to be shared.
I am trying to POST some data into database through my C# APP and API (separately build), but it throws error 404 only for the POST API. All other pages work fine and so does the GET request. The app and API have been deployed on a LINUX machine through NGINX reverse proxy server. Both of them work on HTTP protocol. The feature works for localhost, but not for IP dependent URL.
Here is the content of service file for the app, I do not know what is missing in it. Please take care of the "/" as well where ever it is needed. While performing RnD, I found that the POST request in NGINX gets redirected to GET, I don't know if this will be helpful or not, but felt like sharing.
server {
listen myIP:6002;
server_name attendancepp;
root /home/user/net-core/Publish/AttendanceModule/AttendanceApp;
location /AttendanceApp/{
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://myIP:6002/;
proxy_set_header Accept-Encoding "";
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_redirect off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
The app works on the URL http://myIP:6002/attendance/allPages . All the pages are accessible without any issue. Just the POST part is not working.
Thank you, in advance.
working fine after commenting
try_files $uri $uri/ =404
cd /etc/nginx/sites-available/
sudo nano default << nginx
edit file then CTR+X and 'y' for yes
sudo systemctl restart nginx

NGINX: invalid number of arguments in "proxy_set_header"

Setting up a reverse proxy for a couple sites, one of which requires the users IP for login purposes. The reverse proxy is configured with NGINX and hosted in a docker container, docker is running Linux 10 (Buster) and is on a windows machine.
This code works, but doesn't pass the users IP:
server {
listen 80;listen [::]:80;
server_name your.domain.com;
location / {
proxy_pass http://Host IP:Port/;
proxy_buffering off;
}
}
When i add the headers it suddenly stops working, which doesn't really make much sense to me as i got the code snippets straight from NGINX's docs, this is how the code looks with the headers:
server {
listen 80;listen [::]:80;
server_name your.domain.com;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://Host IP:Port/;
proxy_buffering off;
}
}
When i restart the container, it instantly shuts down and i get this error message:
2020/11/13 20:17:58 [emerg] 1#1: invalid number of arguments in "proxy_set_header" directive in /etc/nginx/conf.d/admin.conf:6
nginx: [emerg] invalid number of arguments in "proxy_set_header" directive in /etc/nginx/conf.d/admin.conf:6
I've tried removing one of the headers but the result is still the same, when i remove both everything works as intended, been trying to find a fix for a couple hours to no avail so i hope that someone here can help me out.

Faye requesting an insecure script <URL>

I'm was exploring faye to implement websocket feature but got stuck due to following issue.
The page at was loaded over HTTPS, but requested an insecure script http://xyz/faye?message=[{"channel":"/meta/handshake","version":"1.0","supportedConnectionTypes":["websocket","eventsource","long-polling","cross-origin-long-polling","callback-polling"],"id":"1"}]&jsonp=__jsonp1. This request has been blocked; the content must be served over HTTPS
Config details
rails( 4.2.11)
faye (1.3.0)
puma (4.3.1)
Everything fine locally since everything running over HTTP.
On staging, we are getting the above issue. Not sure what are we doing wrong. Points to note are:
Puma is running using --ssl.
https://xyz/faye.js renders the client JS (Note the https)
Ran faye server using rackup faye.ru -s thin -E production
nginx config below
server {
listen 80;
root /var/app/current;
location / {
proxy_pass http://mlp;
proxy_set_header Host $host;
rewrite /favicon.ico /public/favicon.ico;
}
location /faye {
proxy_set_header Host $host;
proxy_pass http://localhost:9292;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
}
}

nginx does not forward to my rails app

I am rather new to using nginx. I want to use it to serve static content in order to reduce the load on the rails server. It seems to be a rather simple task but I just can't find a solution that works for me.
I want nginx to serve static files which exist in the public directory inside my rails application directory. To be more precise: I got an index.html inside the directory I want to get served when entering http:/[domainname]. Instead I just get the default nginx index.html. I already checked that thin is running and when I query 127.0.0.1:3000 I get the page I want.
So here's the file called [domainname] in the sites-available directory.
upstream rails {
server 127.0.0.1:3000; #This is where thin is waiting for connections
}
# HTTP server
server {
listen 80;
server_name [domainname];
set $app /home/projektor/website/app.[domainname];
root $app/public;
# Set a limit to POST data
client_max_body_size 8M;
# Errors generated by Rails
error_page 400 /400.html;
error_page 422 /422.html;
error_page 500 504 /500.html;
# Errors generated *outside* Rails
error_page 502 #502;
error_page 503 #503;
# If the public/system/maintenance.html file exists,
# return a 503 error, that ...
if (-f $document_root/system/maintenance.html) {
return 503;
}
# ... will serve the very same file. This construct
# is needed in order to stop the request before
# handing it to Rails.
location #503 {
rewrite ^ /system/maintenance.html break;
}
# When a 502 error occurs - that is, Rails is not
# running, serve the 502.html file
location #502 {
rewrite ^ /502.html break;
}
# Add client-side caching headers to static files
#
location ~ ^/(stylesheets|javascripts|images|system/avatars) {
expires 720h;
}
# Hand over the request to Rails, setting headers
# that will be interpreted by request.remote_ip,
# request.ssl? and request.host
#
location / {
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 http;
proxy_set_header Host $http_host;
proxy_redirect off;
# If the file exists as a static file serve it directly
if (-f $request_filename) {
break;
}
# Oh yeah, hand over the request to Rails! Yay! :-D
proxy_pass http://rails;
}
}
The file is based on this one.
Thanks for your help.
Edit:
I already exchanged 127.0.0.1:3000 for 0.0.0.0:3000 in the upstream part. I also checked the the ownership of the files in sites-available and sites-enabled and they should both be ok.
I hardcoded return 503; into the location instruction and it seems that it never matches. It seems that it always matches the precreated default configuration.
Take a look at the Mongrel example from the try_files documentation. Something like this should work:
location / {
try_files /system/maintenance.html $uri $uri/index.html $uri.html #thin;
}
location #thin {
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 http;
proxy_set_header Host $http_host;
proxy_redirect off;
# Oh yeah, hand over the request to Rails! Yay! :-D
proxy_pass http://rails;
}
When I set up a recent version of NGINX, the solution to getting it to point properly to my rails app had everything to do with fully, and properly configuring it. There were a few extra steps I didn't find in the documentation. It was acting just as yours is. I don't have a clean install to go off of, but bear with me here.
I set a directory called sites-enabled in the default install location. This is a debian install from apt repository, so the install loccations are /etc/nginx and /var/nginx.
mkdir /etc/nginx/sites-enabled /etc/nginx/sites-available
Place conf file for site in sites-available/
Add this line to the bottom of /etc/nginx/nginx.conf
include /etc/nginx/sites-enabled/*;
Look for and remove any reference that may include the DEFAULT configuration, which is telling nginx to actually load this file. Which is what gives you the nginx default index.html (/etc/nginx/conf.d/default.conf)
grep -r "default.conf" /etc/nginx
Symlink (man ln) your file in sites-available to sites-enabled.
ln -s /etc/nginx/sites-available/myfilename /etc/nginx/sites-enabled/myfilename
Test your configuration.
/etc/init.d/nginx configtest
Once your configuration is set up properly, restart nginx
/etc/init.d/nginx restart
I can't remember if I removed this reference or including the line in step 2 was enough. If you update or comment on this answer and give me the results, I can try to dig up the other steps I took.
I don't think the problem here is your actual configuration.

Websocket-rails doesn't work on production evironment with Nginx and Unicorn

I have Rails 3.2 application with gem websocket-rails 0.7.
On development machine, all work fine
On production enviroment, I use Nginx/1.6 as proxy server and Unicorn as http server. Thin is used on standalone mode (following https://github.com/websocket-rails/websocket-rails/wiki/Standalone-Server-Mode).
nginx config:
location /websocket {
proxy_pass http://localhost:3001/websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
On backend side, I have the following code for send notification to clients
WebsocketRails[:callback_requests].trigger 'new', call_request
On client side, I got a connection using:
dispatcher = new WebSocketRails window.location.host + ':3001/websocket'
channel = dispatcher.subscribe 'callback_requests'
But notification doesn't come to the client.
Related issue on github - github.com/websocket-rails/websocket-rails/issues/211
Your nginx config is matching requests below /websocket/ with the trailing /. That is the directory component of /websocket/blah.
If you look in your nginx access log file you'll find your requests to /websocket are being 301 redirected to /websocket/.
Remove the trailing /
location /websocket {
proxy_pass http://localhost:3001/websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

Resources