Set up nginx and node apps on ubuntu - node.js

Here's the situation -
node express app1 (backend app - REST API server) - running on port 8888 -
bunch of POST routes (auth, api, download, upload..) - communicating with mysql server - maintained by developer #1
node (webpack, react) app2 (front end app) running on port 80 - maintained by developer #2
This week we set up nginx and HTTPS (via Let's encrypt), which means that when a user comes to the www.oursite.com - it is redirected to the https site with nice green lock and secure site. So far so good. However, when the user enter her email, the app2 is supposed to call app1 and email with registration token should be sent. This worked well before we try to set up nginx - and use http on both apps. Now this call is not happening. This is our nginx setting:
server {
listen 80;
listen [::]:80;
root /var/www/oursite.com/html;
index index.html index.htm index.nginx-debian.html;
server_name oursite.com www.oursite.com;
location / {
try_files $uri $uri/ =404;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/oursite.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/oursite.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
if ($scheme != "https") {
return 301 https://$host$request_uri;
} # managed by Certbot
# Redirect non-https traffic to https
# if ($scheme != "https") {
# return 301 https://$host$request_uri;
# } # managed by Certbot
}
My question (developer #1) - should I replace all routes in my app (backend app - app1 - backend) to be https instead of http? Apparently Chrome is not calling http request from https site (as is our case)
and how should I setup the nginx then - I belive I should add another location part in nginx setting.
If there are other suggestions please let us know.

Each port must get its own server{ } block, so port 80 gets one and port 443 get another each with a listen directive to the respective port ... in above you have both ports in the same server{ } block
If the app is exposed to the internet they should be using https and if not their incoming traffic using http which is port 80 block must get redirected to send traffic to 443 ... so your nginx config file will look similar to
server {
listen 80 ;
listen [::]:80 ;
server_name example.com, www.example.com;
# Redirect all HTTP requests to HTTPS
rewrite ^/(.*) https://example.com/$1 permanent;
}
server {
listen 443 ssl ;
listen [::]:443 ssl ;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/oursite.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/oursite.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
location / {
# direct traffic to my server on its host IP and port
proxy_pass http://172.19.0.5:3000/; # notice http not https
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
In your case looks like app1 is not exposed to the internet and so does not appear in your nginx config ... it only receives http traffic downstream from app2 ... all of above config is only for your app2 ... incoming internet traffic using secure https port 443 (or port 80 which gets redirected to 443) receives what is called TLS termination ... apps shielded from the internet by the nginx TLS termination receive http traffic not https which is fine since the app ports are not visable from the internet ... and so your app2 gets http traffic not https

We got this to work by setting the app1 (backend up) as https as well. However, this works in all major browser, except for firefox. SO I guess I shiuld post a new question now. The site is not letting through the form on firefox - but only if the address does not have www. That means https://testsite.com -> not working, https://www.testsite.com working ok. But is working ok on all other browsers.

Related

ReactJS with Nginx 404 Not Found when entering a specific url path

What I am trying to do is to prepare this machine to serve my web application. It works without any problems when I serve it via npm start.
I have a domain, let's say, example.com on which I installed SSL with Certbot and have successfully rerouted http to https with www.example.com and example.com.
Now what happens is when I go to www.example.com or example.com it works fine, and I can move to whatever url path I want, as long as it is directly accessed from any button on the homepage, but whenever I try to access an individual url path, let's say myexample.com/admin/login (it is hidden from the homepage) or any manually entered url path, the webserver returns error 404. This does not happen when I do the same with npm start.
I am running an EC2 machine with the following components installed:
Operating System: Ubuntu 20.04 LTS
Webserver: Nginx 1.18
Node: 10.24.1
NPM: 6.14.12
SSL Provider: Let'sEncrypt (Certbot 0.40.0)
I have the following ports open from the EC2 Management Console:
80/custom anywhere (http)
443/custom anywhere (https)
22/custom my IP (ssh)
3000/Custom my IP (npm start default port)
I have also allocated an Elastic IP to my machine, let's say it's 1.1.1.1 .
My Nginx config file is located at:
/etc/nginx/sites-enabled
and is called: client-config
I have set up my Nginx config to look like this:
server {
server_name 1.1.1.1 example.com www.example.com; # 1.1.1.1 this is an example ip
root /home/ubuntu/app-deploy/build;
index index.html; # react by default generate the file in the build directory
location / {
try_files $uri $uri/ =404;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.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.example.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = example.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name 1.1.1.1 example.com www.example.com; # 1.1.1.1 this is an example ip
return 404; # managed by Certbot
}
Running nginx -t returns:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

NGINX: Call non SSL API from SSL based Web Call (https to http call)

I have NGINX configured with SSL and angular website is hosted in it, whereas the Login API is running without SSL.
When I try logging on UI, I get below error,
Mixed Content: The page at 'https://example.com/login' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://example.com:3333/auth/login'. This request has been blocked; the content must be served over HTTPS.
Note : UI calls internally 'http://example.com:3333/auth/login'.
Obvously, UI in SSL and node API without SSL.
But in nginx.conf I used "proxy_pass to route traffic through port 80 (to call http://example.com:3333/auth/login the way it was called without SSL certificate) to redirect to 80 port as below,
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location / {
}
server {
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
server_name example.com;
location / {
proxy_pass http://example.com:80;
}
But no luck, If I remove SSL from NGINX, all works fine. Any suggestion on how to call non-SSL api from SSL based web call?

Nginx With SSL and Frontend + Backend same server

I have a server (VPS Amazon) with Ubuntu. In this server is running my backend node and my frontend React.
My ReactApp is running over nginx and my backend over pm2.
In my react app I defined REACT_APP_BASE_URL: http://[my_ip_server]:4000.
So everything was working OK but after I configured SSL in nginx, I can access my frontend login page but when I send the request, I catch the following errors:
a) If I set https in my REACT_APP_BASE_URL (https://[my_ip_server]:4000), I get this error: ERR_SSL_PROTOCOL_ERROR.
b) If I let with http, I get Mixed Content error
Someone know How I do this work?
Thanks a lot!
My nginx.conf. At moment I'm using just port 80 until I solve my problem.
server {
#listen [::]:443 ssl ipv6only=on; # managed by Certbot
#listen 443 ssl; # managed by Certbot
#ssl_certificate /etc/letsencrypt/live/mysite.com.br/fullchain.pem; # managed by Certbot
#ssl_certificate_key /etc/letsencrypt/live/mysite.com.br/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
#if ($host = surveys.alcancenet.com.br) {
# return 301 https://$host$request_uri;
#} # managed by Certbot
listen 80 default_server;
listen [::]:80 default_server;
server_name mysite.com.br;
#return 404; # managed by Certbot
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri /index.html;
}
}
With help from #Markido. I managed to solve that.
I added in my backend the default route "/api" and after that I put in my nginx config the following
location /api {
proxy_pass http://localhost:4000;
}
Tks!!!
First off, there is a difference between running the applications (which is what i assume you are using PS2 for), and exposing them through an nginx proxy.
It would be most helpful to show us your nginx config file, and also tell us which port your backend runs on (assuming the frontend runs on port 4000).
Edit;
Thanks for the config and backend port.
I don't think you need to set the create react app base url to https, just set the port and run it on the VPS using PS2.
I can't see how you have any proxy at all pointing to 4000 in your config - do you not expose the backend?
The only exposed part is static html files. The relevant code is;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri /index.html;
}
If you want to call the backend using https, or generate your site using some tool with a process which entails HTTPS calls, you need to do so correctly in the frontend. IE something doesn't add up here.
The usual approach is;
Expose the backend and the frontend on port 443 SSL only, using different sub-domains (eg. api.mydomain.com), and then use the proxy in nginx to redirect 443 traffic for each domain to the corresponding local ports (4000, and the frontend port or static files directory more likely).
Instead of:
location / {
try_files $uri $uri /index.html;
}
Use something like:
location / {
proxy_pass http://localhost:4000;
}

Cannot listen to https on port 5050 in NGINX

I have a nodejs app that functions as a webserver listening to port 5050
I've created certificates and configured NGINX which works for normal https calls to the standard port (https://x.x/)
If I make a call to port 5050 with a normal http://x.x:5050 call it also works, but with an https://x.x:5050/conf call I get: This site can’t provide a secure connection
Below the NGINX config file:
(The names of the website are changed)
server {
root /var/www/x.x/html;
index index.html index.htm index.nginx-debian.html;
server_name x.x www.x.x;
location / {
try_files $uri $uri/ =404;
}
location /conf {
proxy_pass http://localhost:5050;
try_files $uri $uri/ =404;
}
location /wh {
proxy_pass http://localhost:5050;
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/x.x/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/x.x/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
}
What am I doing wrong here?
You configured nginx to serve as a reverse proxy, forwarding incoming requests from https://example.com/whatever to http://localhost:5050/whatever. You said you did that correctly and it works. Good. (Getting that working is a notorious pain in the xxx neck.)
You did not configure nginx to listen on port 5050. Nor should you; that's the port it uses to pass along requests to your nodejs program. You cannot forward requests from port 5050 to port 5050. If you try to have nodejs and nginx both listen to port 5050, one of them will get an EADRINUSE error when you start your servers.
Your nodejs program listens for http requests, not https requests, on port 5050. You can't easily make it listen for both http and https on the same port. Your nodejs program, when behind nginx, should not contain any https server, only http. (You're making nginx do the hard crypto work to handle https, and letting nodejs handle your requests.)
Nor do you want your nodejs program to listen directly for http-only requests from outside your server. Because cybercreeps.
If you can block access to port 5050 from anywhere except localhost, you can declare victory on your task of configuring your server. You can do this by using
server.listen({
host: 'localhost',
port: 5050, ...
});```
in your nodejs program. Or you can configure your server's firewall to block incoming requests on any ports except https (and ssh, so you can manage it). Digital Ocean has a useful tutorial on this point.

Redirection issue with two websites running Node.js on Nginx and CloudFlare

I have two files in sites-available, one for each website running on the machine. Both have identical code with only the domain name and port for the website replaced. Both sites are also symlinked to sites-enabled.
reesmorris.co.uk (which works fine):
# Remove WWW from HTTP
server {
listen 80;
server_name www.reesmorris.co.uk reesmorris.co.uk;
return 301 https://reesmorris.co.uk$request_uri;
}
# Remove WWW from HTTPS
server {
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/reesmorris.co.uk/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/reesmorris.co.uk/privkey.pem;
server_name www.reesmorris.co.uk;
return 301 https://reesmorris.co.uk$request_uri;
}
# HTTPS request
server {
# Enable HTTP/2
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name reesmorris.co.uk;
ssl_certificate /etc/letsencrypt/live/reesmorris.co.uk/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/reesmorris.co.uk/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
include /etc/nginx/proxy_params;
}
}
francescoiacono.co.uk (has a redirection loop):
# Remove WWW from HTTP
server {
listen 80;
server_name www.francescoiacono.co.uk francescoiacono.co.uk;
return 301 https://francescoiacono.co.uk$request_uri;
}
# Remove WWW from HTTPS
server {
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/francescoiacono.co.uk/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/francescoiacono.co.uk/privkey.pem;
server_name www.francescoiacono.co.uk;
return 301 https://francescoiacono.co.uk$request_uri;
}
# HTTPS request
server {
# Enable HTTP/2
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name francescoiacono.co.uk;
ssl_certificate /etc/letsencrypt/live/francescoiacono.co.uk/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/francescoiacono.co.uk/privkey.pem;
location / {
proxy_pass http://127.0.0.1:4000;
include /etc/nginx/proxy_params;
}
}
To experiment, I replaced the return value in the first server block of the broken website to be a 403, which seems to be shown even if the site is using HTTPS. Additionally, removing the first server block on the broken website altogether will cause the website to completely redirect to the already-working website.
Both websites use CloudFlare for the DNS routing. CloudFlare is 'paused' on both websites which means that it only handles the routing, in which both websites have identical routing to the server with AAAA and A records being the same.
I'm not too familiar with server blocks, so if anybody has any ideas as to what is happening then it would be greatly appreciated.
It appears as though this issue was resolved by making both websites 'paused' in the CloudFlare DNS. This was done originally, though may have not had enough time to propagate.
I had not modified the code since this post was created, though I had ensured that both sites were 'paused' on CloudFlare (reesmorris.co.uk was, however francescoiacono.co.uk was not) - and so it seems to have been a misconfiguration issue with CloudFlare.

Resources