NGINX - Host 2 HTTPS Node apps - node.js

I'm new to Nginx, and I have some trouble with hosting 2 websites on my RaspberryPi (Raspbian).
I have 2 domains, site1.com (:8080) and site2.com(:8000), both are Node.JS apps (with Express). I have working SSL certifications with Let's Encrypt for both.
This is my site1 nginx config (/etc/nginx/site-available/site1):
server {
server_name site2.com;
listen 80;
listen [::]:80;
location / {
include /etc/nginx/proxy_params;
proxy_pass http://192.168.1.11:8080;
}
}
This is my site2 nginx config (/etc/nginx/site-available/site2):
server {
server_name site1.com;
listen 80;
listen [::]:80;
location / {
include /etc/nginx/proxy_params;
proxy_pass http://192.168.1.11:8000;
}
}
So indeed there is no part of 443 in these conf files but https://site2.com is working well but https://site1.com redirect me to the webpage of https://site2.com (keeping the site1 URL). I guess it's because my_server_ip:443 is already taken by site2 (no ??).
And the http://site2.com give me a 502 Bad Gateway and is not redirected to https (site1 is well redirected to his https).
This is the server part of my Node apps, they are the same for the 2 apps (except a port and SSL URI)
const express = require('express')
const app = express()
var https = require('https');
var http = require('http');
var fs = require('fs');
const privateKey = fs.readFileSync('/etc/letsencrypt/live/site1or2.com/privkey.pem', 'utf8');
const certificate = fs.readFileSync('/etc/letsencrypt/live/site1or2.com/cert.pem', 'utf8');
const ca = fs.readFileSync('/etc/letsencrypt/live/site1or2.com/chain.pem', 'utf8');
const credentials = {
key: privateKey,
cert: certificate,
ca: ca
};
const httpsServer = https.createServer(credentials, app);
const bodyParser = require("body-parser");
const urlencodedParser = app.use(bodyParser.urlencoded({
extended: true
}));
http.createServer(function (req, res) {
//redirect to https
res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url });
res.end();
}).listen(8080); //8000 for my site2.com
httpsServer.listen('443', () => {
console.log('Server https listening on Port 443');
})
I tried to change the Nginx confs to add 'listen 443; SSL on;...' but I have always errors like 'Failed to start A high-performance web server and a reverse proxy server' and I don't understand how to fix it.
So is the problem from my JS code or my Nginx confs? (or both maybe..?)
Thank for reading, it's my first StackOverflow post, I hope I didn't forget information and sorry if there is an English mistake.
Have a good evening (or day)!

I finaly understood how does multiples node websites hosting with SSL works, in the node.JS configuration (app.js) the app has to only listen to 1 port (8080 and 8000 for me) and must not refer to SSL and port 443 at all.
All the ssl configurations and https redirections have to be in the Nginx conf, for exemple, my file /etc/nginx/sites-available/site1:
server {
server_name site1.com;
listen 80;
listen [::]:80;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/site1.com/cert.pem;
ssl_certificate_key /etc/letsencrypt/live/site1.com/privkey.pem;
server_name site1.com;
location / {
proxy_pass http://192.168.1.11: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;
}
}
And the same for site2 with port 8000.

Related

Error when refreshing the same page twice

I'm trying to deploy an angular app on a remote server using nodejs and nginx.
I built the application and I serve it with a node app. Nginx acts here as a reversed proxy.
I can acess the website and navigate without an issue. However, when I try to refresh the current page, my browser canno't find it anymore : response get a 404.
But if I enter the domain in the url bar, I can access again to the website.
Does anyone know where I made a mistake ?
Here's the code and the config for the node app and for nginx :
var express = require('express');
var app = express(); // create our app w/ express
var morgan = require('morgan'); // log requests to the console (express4)
var bodyParser = require('body-parser'); // pull information from HTML POST (express4)
var methodOverride = require('method-override'); // simulate DELETE and PUT (express4)
const mongoose = require("mongoose");
const dotenv = require("dotenv");
const mustacheExpress = require("mustache-express");
dotenv.config();
// configuration =================
mongoose.connect(process.env.DB_CONNECT, { useNewUrlParser: true }, () => {
console.log("connected to DB !");
});
app.use(express.static("../front-end/dist/website"));
app.use(morgan('dev')); // log every request to the console
app.use(bodyParser.urlencoded({'extended':'true'})); // parse application/x-www-form-urlencoded
app.use(bodyParser.json()); // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json
app.use(methodOverride());
// Middleware
app.use(express.json());
// Templating
app.engine("html", mustacheExpress());
//import routes
const authRoute = require("./routes/auth");
const devisRoute = require("./routes/devis");
const messageRoute = require("./routes/message");
const actuRoute = require("./routes/actu");
//Routes middlewares
app.use("/api/user", authRoute);
app.use("/api/sendDevis", devisRoute);
app.use("/api/message", messageRoute);
app.use("/api/actu", actuRoute);
// listen (start app with node server.js) ======================================
app.listen(3000);
server {
root /var/www/domain.com/html;
index index.html index.htm index.nginx-debian.html;
server_name domain.com www.domain.com;
location / {
proxy_pass http://localhost:3000;
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/domain.com/fullchain.pem; # m anaged by Certbot
ssl_certificate_key /etc/letsencrypt/live/domain.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.domain.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = domain.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name domain.com www.domain.com;
return 404; # managed by Certbot
}
This is an easy trap to fall into when doing client-side routing with a Single Page App.
The trick is this: when you do you navigating in the browser, the browser is not making requests to your server. When you refresh the page, the browser does send the request to the server. Since you're making an SPA, all of the information about what's on the page is in your Javascript, and that Javascript is loaded in your index.html file. The only file that exists on your server is your root file, and the express.static middleware maps URLs onto your files, so there is no file for it to find (hence the 404).
What you need server-side is to always serve up your index.html file no matter what html file is requested.
The easiest solution is add a fallback handler last in your routing using Express's build-in .sendFile() function. The simple, nuance-free way would be something like this:
// ... ^^ all your other routes
router.get("*",
// The '*' means match *everything*
(req,res)=>{
res.sendFile('index.html'); // Probaby need to set some options here to start in the right directory...
}
);
You may want to use different middleware or add your own bells and whistles for including compression, etc, but the idea would be the same.

Nginx proxy_pass with Node and SSL

I have managed to setup HTTPS using Nginx on a Node server at localhost:4000 by placing the cert and key paths in the Nginx conf file, however, I would also like to understand how to embed the ssl certificate inside the node index.js file and just pass the proxy'd address to the localhost:4000 with the certs inside the application rather than the nginx.conf file.
This nginx.conf file will work correctly with https/ssl once node isn't being served as ssl.
server {
listen 4500 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
access_log /var/log/nginx/example.com.access.log;
location / {
proxy_pass http://localhost: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;
}
}
However, if I comment out the following nginx.conf
# ssl_certificate /etc/letsencrypt/live/api.ballers.ie/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/api.ballers.ie/privkey.pem;
And then add the certs to my node server as follows, it tells me that the site can't be reached.
// ...
// dependencies & config
// ...
const privateKey = fs.readFileSync('/etc/letsencrypt/live/example.com.privkey.pem', 'utf8');
const certificate = fs.readFileSync('/etc/letsencrypt/live/example.com.cert.pem', 'utf8');
const ca = fs.readFileSync('/etc/letsencrypt/live/example.com.chain.pem', 'utf8');
const credentials = {
key: privateKey,
cert: certificate,
ca,
};
const httpsServer = https.createServer(credentials, app);
app.get('*', (req, res) => {
res.send('I am a HTTPS endpoint !');
});
httpsServer.listen(4000);
I have also tried changing proxy_pass to proxy_pass https://localhost:4000;
I'd be very interested to understand how to make this work, thanks for your assistance.

Nginx proxy pass subdomain, node vhost

I have some trouble with nginx proxy_pass redirection on localhost subdomain. I have a domain "domain.com", i want to redirect all request on *.domain.com on *.localhost:9000. Then node handle all request on *.localhost:9000 to the good express app.
On nginx conf when i try the following :
server {
server_name extranet.domain.com;
listen 80;
location / {
proxy_pass http://extranet.localhost:9000;
}
}
Request on extranet.domain.com are well redirected to the good express webapp.
With this :
server {
server_name ~^(.*?)\.domain\.com$;
listen 80;
location / {
proxy_pass http://localhost:9000/$1;
}
}
Express app running on localhost:9000 handle request /mysubdomainname, which implie that regex is good.
But when i try :
server {
server_name ~^(.*?)\.domain\.com$;
listen 80;
location / {
proxy_pass http://$1.localhost:9000;
}
}
All request on *.domain.com return http code 502.
Why http://localhost:9000/$1; works and not http://$1.localhost:9000; ?
(all subdomain are set in /etc/hosts).
Thanks in advance. I'm totally lost !
When a host name isn't known at run-time, nginx has to use its own resolver. Unlike the resolver provided by OS, it doesn't use your /etc/hosts file.
Maybe this will give you a hint, I wanted to pass the subdomain from Nginx to an Express app. Here is my code:
nginx.conf
http {
upstream express {
server localhost:3000;
}
domain.com inside nginx/sites-available
server {
listen 80;
server_name ~^(?<subdomain>.+)\.domain\.com$;
location / {
proxy_set_header Subdomain $subdomain;
proxy_set_header Host $host;
proxy_pass http://express;
}
}
Express app index.js
var express = require('express');
var app = express();
app.get('/', function (req, res) {
const subdomain = req.headers.subdomain;
});
app.listen(3000, function () {
console.log('Example app listening on port 4000!');
});

How to change parse server mount url to main root url "/"

Here is my index.js file, i have changed below line so i can mount the parse server in root url for example parse.example.com:1337 instead of parse.example.com:1337/parse but Im not sure if it is correct way and i have very little experience with nodejs and javascript
"var mountPath = process.env.PARSE_MOUNT || '/parse';" to " var mountPath = process.env.PARSE_MOUNT || '/';"
index.js
var express = require('express');
var ParseServer = require('parse-server').ParseServer;
var path = require('path');
var databaseUri = process.env.DATABASE_URI || process.env.MONGODB_URI;
if (!databaseUri) {
console.log('DATABASE_URI not specified, falling back to localhost.');
}
var api = new ParseServer({
databaseURI: databaseUri || 'mongodb://parse:secretpass#127.0.0.1:27017/parsedb',
cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
appId: process.env.APP_ID || 'xxxxxxxxxxxxxxxxxx',
masterKey: process.env.MASTER_KEY || 'xxxxxxxxxxxxxxxx', //Add your master key here. Keep it secret!
serverURL: process.env.SERVER_URL || 'http://localhost:1337/', // Don't forget to change to https if needed
liveQuery: {
classNames: ["Posts", "Comments"] // List of classes to support for query subscriptions
}
});
var app = express();
// Serve static assets from the /public folder
app.use('/public', express.static(path.join(__dirname, '/public')));
// Serve the Parse API on the /parse URL prefix
var mountPath = process.env.PARSE_MOUNT || '/';
app.use(mountPath, api);
// Parse Server plays nicely with the rest of your web routes
app.get('/', function(req, res) {
res.status(200).send('I dream of being a website. Please star the parse-server repo on GitHub!');
});
// There will be a test page available on the /test path of your server url
// Remove this before launching your app
app.get('/test', function(req, res) {
res.sendFile(path.join(__dirname, '/public/test.html'));
});
var port = process.env.PORT || 1337;
var httpServer = require('http').createServer(app);
httpServer.listen(port, function() {
console.log('parse-server-example running on port ' + port + '.');
});
// This will enable the Live Query real-time server
ParseServer.createLiveQueryServer(httpServer);
You could use NGINX as a reverse proxy.
Install the nginx package:
sudo apt-get install -y nginx
Open /etc/nginx/sites-enabled/default
sudo nano /etc/nginx/sites-enabled/default
Replace it with the following:
# HTTP - redirect all requests to HTTPS
server {
listen 80;
listen [::]:80 default_server ipv6only=on;
return 301 https://$host$request_uri;
}
# HTTPS - serve HTML from /usr/share/nginx/html, proxy requests to /parse/
# through to Parse Server
server {
listen 443;
server_name your_domain_name;
root /usr/share/nginx/html;
index index.html index.htm;
ssl on;
# Use certificate and key
ssl_certificate /etc/letsencrypt/live/your_domain_name/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain_name/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
# Pass requests for /parse/ to Parse Server instance at localhost:1337
location /parse/ {
rewrite ^/parse(/.*)$ $1 break;# this is the line that will redirect to root url
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://localhost:1337/;
proxy_ssl_session_reuse off;
proxy_set_header Host $http_host;
proxy_redirect off;
}
location / {
try_files $uri $uri/ =404;
}
}
Save, exit and restart
sudo service nginx restart
Update:
Parse Server needs an ssl certificate to work by default, you can disable it but it is very strongly recommended to only use it via HTTPS.
I am using Lets Encrypt certificates, if you need help creating them I can show you a tutorial I wrote, or you can use your own certificates.

Socket.io + node.js + express + ssl + Nginx - client will not connect - 400 (Bad Request)

I have an express app on a single server running behind nginx
I've tested socket.io communication and it runs fine on our dev server.
Adding ssl to the mix yielded the following console log on the client:
As you can notice it produces a 400 (Bad Request) - and then loads the same request successfully, but my console logs are not being run since the connection is not successful...
Would appreciate any feedback. Thanks.
This is the code:
This is my nginx config:
upstream example.com {
server 127.0.0.1:3000;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/nginx/ssl/example.crt;
ssl_certificate_key /path/to/nginx/ssl/example.key;
location / {
proxy_pass http://example.com;
proxy_redirect off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
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 https;
}
location /public {
root /var/www/example/;
}
location /favicon.ico {
root /var/www/example/public/favicon.ico;
}
}
server {
listen 443 ssl;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
this is my socket.io related code on the express server
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
http.listen(port, function(){
console.log('MyDomain.com app listening at ', 'http://localhost:'+port);
io.on('connection', function(socket){
console.log('socket.io connected','socket.id: '+socket.id);
socket.on('new-user', function(data) {
var user_id = data.user_id;
console.log('new-user, user_id: ', user_id);
});
});
});
this is my client side that tries to connect:
var socket = io.connect(null, { secure: true, port: 443, rememberTransport: false, 'reopen delay': 1000 });
//I have also tried these variations...
//-----------------------------------------
//var socket = io();
//var socket = io.connect('https://localhost', {secure: true});
//var socket = io.connect('https://example.com',{secure: true});
//var socket = io.connect('https://example.com');
//-----------------------------------------
socket.on('connect', function () {
console.log('on "connect"');
socket.emit('new-user', {user_id:some_user_id});
});
on my html template I'm using this to load socket.io
<script src="/socket.io/socket.io.js"></script>
UPDATE 01:
I've noticed that when I go to a different URL in the browser, then come back - the sockets are loading correctly...
Does this indicate a delay which is needed...?

Resources