Websocket connection on Google Compute Engine using a custom domain name - node.js

I have a websocket server (node.js) which runs fine on localhost and on a previous heroku deployment. I am now migrating to google compute engine and running into some issues.
The websocket handshake is failing returning a 301 error. As pointed out in this answer this can be due to the request going through front end servers that do not support a websocket connection and can be worked around by targeting ws://my_external_gce_ip directly. I am wondering if there is some load balancing configuration I can update so that I can address my backend using the custom domain name.
While I understand the problem, it seems to me that the domain should be resolved to the external ip after a dns lookup so I don't understand the constraint really.
Sorry if this is very obvious. I'm new to GCE and have been googling all day trying to get this. I will paste my code below as well as the NGINX config but I don't think either are particularly helpful as all works fine addressing using the IP
index.js:
/* requirements */
var bodyParser = require("body-parser");
const WebSocket = require("ws");
const http = require("http");
const express = require("express");
const port = process.env.PORT || 3000;
/*
server definition and config
*/
const app = express();
app.use(bodyParser.json());
const server = http.createServer(app);
/*
web socket stuff
*/
const webSocketServer = new WebSocket.Server({
server,
});
webSocketServer.on("connection", (webSocket) => {
console.log("board trying to connect...");
webSocket.on("message", (data) => {
webSocketServer.clients.forEach((client) => {
if (client === webSocket && client.readyState === WebSocket.OPEN) {
client.send("[SERVER MESSAGE]: You are connected to the server :)");
}
});
});
});
/*
activate server
*/
server.listen(port, () => {
console.log(`Server is now running on port ${port}\n`);
});
nginx config
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location \ {
# we're actually going to proxy all requests to
# a Nodejs backend
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# I added this baby in
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server
{
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name my_domain; # managed by Certbot
location / {
# we're actually going to proxy all requests to
# a Nodejs backend
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/my_domain/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/my_domain/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 = my_domain) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80 ;
listen [::]:80 ;
server_name my_domain;
return 404; # managed by Certbot
}
Thanks very much in advance and sorry if this is a noob question. Total noob when it comes to load balancing

Solved. After a lot more googling I found this thread of people facing the same problem most either using apache servers or elastic beanstalk so not using nginx.
It seems that a lot of people get websockets to "work" using socket.io but they don't really have a duplex connection as it is falling back to long polling.
In my case the answer was simple, I didn't include the server name in my nginx (facepalm) and I may have forgot to include a header. The https forwarding now looks like this (and addressing using my domain works)
location / {
# we're actually going to proxy all requests to
# a Nodejs backend
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
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;
proxy_read_timeout 90;
}
Don't forget to restart your nginx after you update
sudo systemctl restart nginx

Related

nginx and node js server with ssl

const https = require(`https`);
const fs = require(`fs`);
const options = {
key: fs.readFileSync(...),
cert: fs.readFileSync(...)
};
https.createServer(options, app).listen(8000);
My node js server looks like this.
does it mean I have to configure nginx like this?
location / {
proxy_pass https://localhost:8000/;
}
Not, proxy_pass to http://localhost:8000/
what makes me confused is that I think network inside the server does'n need ssl.
https server on application server also needs ssl configuration on nginx, right?
I also tried app.listen instead of https.createServer.
My node js app and nginx configuration looks like this.
app.listen(4416);
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
root /root/app;
index template.html;
ssl_certificate ...;
ssl_certificate_key ...;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
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;
proxy_pass http://localhost:4416/;
proxy_buffering off;
proxy_read_timeout 90;
#websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
server{
listen 80;
listen [::]:80;
return 301 https://$host$request_uri;
return 404;
}
In this case, redirection keeps incurred, and I don't know the reason why. What did I miss?
If I did https.createServer(SSL_OPTION, app).listen(4416) instead of app.listen(4416), it doesn't have a connection at all. And if I fix nginx configuration proxy_pass to https://localhost:4416;, then it works well. Things are done well but, I wonder why this is happening.

Cannot connect to node js websocket server through nginx with ssl

I used the websocket protokoll for a period of time and didn't have any problem with that. But now I installed nginx and ssl on my server and cannot connect to that. My setup is quasar (VueJs), Node Js,Nginx, Ubuntu 18.4.
My nginx config:
server {
server_name mySite.mySiteCountry;
location = /ws/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_pass http://localhost:4510;
}
location / {
proxy_set_header Host $host;
proxy_pass http://localhost:81;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/...; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/...; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
My nodejs server:
const app = express();
app.use(cors(corsOptionsDelegate));
app.use(headerSetter);
const sslPort = dev ? 4041 : 4510;
let serverForWs = app.listen(sslPort);
const WebSocket = require("ws");
const wss = new WebSocket.Server({
noServer: true,
clientTracking: true,
});
wss.on("connection", function connection(ws, request) {
...
If I try to connect without ssl to the 4510 port (ws://mySite.mySiteCountry:4510) its working. If I try through the nginx proxy (wss://mySite.mySiteCountry/ws | ws://mySite.mySiteCountry/ws) its not. What am I doing wrong?
Thanks in advance!
Edit:
I get this error from client. The backend doesn't recieve anything. As I see, the request reaches the nginx, but I don't know what happens after that.

Websocket Cloudflare with Nginx 520 error

I am jumping in on a project with some socket issues over SSL and Cloudflare... I know.. I have read about 50 different stack overflow posts and 200 blog posts to try to figure this out. The project works on my local dev server/computer just fine...
I think I am on the right track - But could use some help/pointers if ya'll can.
First, I thought it was weird that the /socket-io/ proxy_pass was at port 6379, the same as redis... Maybe it should be? When this was set at 6379, the socket connection will not connect - With or Without Cloudflare enabled ( I paused cloudflare to test this out).
I read through the express server and saw that the socket server seems like it's linked to the express server at port 4000... so I changed the proxy_pass for /socket-io/ to port 4000 and it reconnects. This works with Cloudflare paused/running... so maybe it's not cloudflare after all. Still, even though it says the socket has reconnected in the browser, nothing is working.
I'll start by sharing my NGINX config - Let me know what else ya'll need to see, please. Thanks for taking your time to help me out/pointing me in the right direction! I really appreciate learning about this stuff.
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name dev-app.myapp.com;
location / {
root /var/www/myapp_frontend/build/;
try_files $uri $uri/ /index.html;
#proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
location /api/ {
proxy_pass http://localhost:4000/;
include /etc/nginx/proxy_params;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /socket.io/ {
proxy_pass http://localhost:6379;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_read_timeout 86400;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location ~ /\.ht {
deny all;
}
ssl_certificate /etc/letsencrypt/live/dev-app.myapp.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/dev-app.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 = dev-app.myapp.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name dev-app.myapp.com;
listen 80 default_server;
listen [::]:80 default_server;
return 404; # managed by Certbot
}
Edit-1
I did see that cloudflare requires certain ports... Am I wrong to think that these ports only refer to the initial listening port, for example 443 above, since the proxy_pass ports are all using localhost?

'Cannot GET' on Express when using proxy pass in Nginx

I'm running two websites on a DigitalOcean droplet, and now I've upgraded to https on both of them. On one of the sites I'm doing a request to a node server I'm running on there to send emails, but since I've enabled https it blocks this request because it isn't https (logically). So I tried to use the node mailserver as a proxy pass on one of the sites that has https enabled.
To test, my node server I want to proxy pass looks like this:
exports.start = () => {
app.use(bodyParser.json());
app.use(cors());
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(4004, () => console.log('Server listening on port 4004'));
};
this.start();
My Nginx sites-enabled/default looks like this:
server {
server_name mywebsite.nl www.website.nl;
location / {
proxy_pass http://<PIVATE IP>:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /ws {
proxy_pass http://<PIVATE IP>:4002;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
}
location /mailmeister {
proxy_pass http://<PIVATE IP>:4004;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/mywebsite.nl/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/mywebsite.nl/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
}
So the /mailmeister is where I want to redirect all requests to the node server to.
When I run curl localhost:4004 or curl PRIVATE IP:4004 I get the expected
Hello World! response. But when I run curl https://mywebsite.nl/mailmeister It returns a webpage that says
Cannot GET /mailmeister
Anyone know how to fix this?
In a nutshell, since you have:
location /mailmeister {
proxy_pass http://<PIVATE IP>:4004;
}
It should be handled like so:
app.get('/mailmeister ', (req, res) => res.send('Hallo World!'));
Not tested this particular setup. There is a related answer where this is explained further. Once you get it, the documentation makes more sense.

Socket.io disconnects on http request

I'm running nginx for my node.js application on a AWS EC2 instance. I want to use websockets (socket.io) and normal http request/response. My problem is, whenever I have an active socket connection from my mobile device to the server and try to make a normal http request, the mobile device's socket.io error function is called with the message "502 Bad Gateway".
Only socket works. Only normal http request works as well.
I figured out, that this problem occurred after I setup nginx to use https only.
Here is my nginx config in /sites-enabled /sites-available:
server {
listen 80;
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
if ($scheme != "https") {
rewrite ^ https://$host$request_uri? permanent;
}
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
Nginx error log:
[error] 14117#14117: *50 upstream prematurely closed connection while reading response header from upstream, client: 78.94.9.226, server: example.com, request: "GET /socket.io/?transport=polling&b64=1&sid=rgXMQhL6mbSET8ktAAAA HTTP/1.1", upstream: "http://127.0.0.1:3000/socket.io/?transport=polling&b64=1&sid=rgXMQhL6mbSET8ktAAAA", host: "example.com"
iOS error log:
LOG SocketIOClient: Handling event: error with data: ["Got unknown error from server <html>\r\n<head><title>502 Bad Gateway</title></head>\r\n<body bgcolor=\"white\">\r\n<center><h1>502 Bad Gateway</h1></center>\r\n<hr><center>nginx/1.10.3 (Ubuntu)</center>\r\n</body>\r\n</html>\r\n"]
If you need any more information, let me know!
I fixed the problem by myself. It was a really dumbass problem. I created a file inside my node.js server folder called access.log and told the morgan logger to write into the file. The thing I forgot was, that I'm using PM2 to restart the server whenever there is a change in code inside the server folder. So PM2 restarted the server every time I made a http request and the socket disconnected.
Change your nginx config into two blocks
server {
listen 80;
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
if ($scheme != "https") {
rewrite ^ https://$host$request_uri? permanent;
}
location /socket.io {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
You want to only upgrade connection for socket.io and not other urls

Resources