can Nginx randomly stop working by certain requests? - linux

I'm currently having issues with my website. Sometimes, after a fresh restart of nginx service the url of my website works just fine in the browser, It redirects successfully to the .NET Core webapp running on Kestrel.If I type the IP of my vps it also works just fine. But suddenly and randomly nginx stops serving the website and the browser just shows err_connection_closed.
Some technical information:
Kestrel is running on localhost:5000, Nginx TCP ports are managed by ufw and opened for: 80 and 443.
I'm using: Ubuntu 16.04, nginx and a .NET Core 3.1 web app. Steps were followed as next url Host and Deploy using Linux and Kestrel
Something that I have noticed in syslog file is that some IPs are blocked by ufw, but I'm not sure why they are coming from China, Mongolia or even Poland, as the initial marketing campaign is currently located for Mexico.
Other log file that I searched in was /var/log/nginx/access.log Here, some IPs try to request random urls in my website like GET /Telerik.Web.UI.WebResource.axd?type=rau HTTP/1.1" 404 0 "-" or even like "GET /phpmyadmin/ HTTP/1.1" 301 178 "-" which is absolutely not me because I'm using PostgreSQL. Although, I have to say that I've seen that after this requests are randomly made, the nginx stops working but I'm not 100% sure if this is accurate, as seen in the title, this is very random.
Some config files for nginx:
/etc/nginx/sites-available/default
# Default server configuration
#
server {
listen 80;
server_name keecheeapp.com *.keecheeapp.com;
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
/etc/nginx/proxy_conf
proxy_redirect off;
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;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffers 32 4k;
/etc/nginx/nginx.conf
#other directives
events {
worker_connections 768;
# multi_accept on;
}
http {
include /etc/nginx/proxy.conf;
limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
server_tokens off;
sendfile on;
keepalive_timeout 29; # Adjust to the lowest possible value that makes sense for your use case.
client_body_timeout 10; client_header_timeout 10; send_timeout 10;
upstream keecheeapp{
server localhost:5000;
}
server {
listen *:80;
add_header Strict-Transport-Security max-age=15768000;
return 301 https://$host$request_uri;
}
server {
listen *:443 ssl;
server_name keecheeapp.com;
ssl_certificate /etc/ssl/certs/keecheeapp.com-concat-certs.crt;
ssl_certificate_key /etc/ssl/certs/private_new.key;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
#Redirects all traffic
location / {
proxy_pass http://www.keecheeapp.com;
limit_req zone=one burst=10 nodelay;
}
}
}

There are several issues with your Nginx configuration:
In the file /etc/nginx/nginx.conf
The combination of limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s; and limit_req zone=one burst=10 nodelay; will limit the request processing rate per client to 5 requests/second. If you send too many requests per second then you will get error messages from Nginx. So if you want to keep the limit feature, try to increase the existing value to, for example, rate=50r/s and burst=100. If you want to disable this feature, delete or comment out those lines. You can learn more about this feature here.
The value http://www.keecheeapp.com for the proxy_pass directive is wrong . The correct value is keecheeapp as defined by the upstream keecheeapp {...} block. So change proxy_pass http://www.keecheeapp.com; to proxy_pass http://keecheeapp;
The server block in the file /etc/nginx/sites-available/default instructs Nginx to serve your website using HTTP.
The following server block in the file /etc/nginx/nginx.conf instructs Nginx to serve your website using HTTPS.
server {
listen *:443 ssl;
server_name keecheeapp.com;
...
}
So your website is accessible over both HTTP and HTTPS. It's not a good idea. You should redirect all HTTP requests to HTTPS as follows:
Delete or comment out the server block in in the file /etc/nginx/sites-available/default
Modify the following server block in the file /etc/nginx/nginx.conf
server {
listen *:80;
add_header Strict-Transport-Security max-age=15768000;
return 301 https://$host$request_uri;
}
To:
server {
listen *:80;
server_name keecheeapp.com *.keecheeapp.com;
add_header Strict-Transport-Security max-age=15768000;
return 301 https://$host$request_uri;
}
With your given configuration, Nginx is passing all requests to Kestrel, including static file requests (image, JS, CSS, etc.). This is unrealistic. Let Nginx handle static files, and Kestrel handles dynamic requests. Please change the following configuration block:
#Redirects all traffic
location / {
proxy_pass http://www.keecheeapp.com;
limit_req zone=one burst=10 nodelay;
}
To:
root /path/to/your/static/folder;
# Serve static file requests
location / {
try_files $uri $uri/ #kestrel;
}
# Pass dynamic requests to Kestrel
location #kestrel {
proxy_pass http://keecheeapp;
limit_req zone=one burst=10 nodelay;
}
Change /path/to/your/static/folder to the actual folder on your server.
After editing, don't forget to test Nginx configuration with sudo nginx -t, then reload it with sudo systemctl reload nginx.service.

Related

TLS connection refused with nothing on logs

I've been attempting to install an SSL certificate for the past several hours to no success. There are no errors on the nginx log, but everytime I try to access my website through https I just get a connection refused.
I've tried everything I can find on Google and SO results:
I've disabled the Ubuntu firewall
I've run netstat and there's an active listen on port 443
I've verified the sha256 checksum of the certificate, private key and certificate requests
service nginx restart + service nginx status shows everything ok
I feel like I'm arriving at the limit of my experience with nginx (which isn't much) and Linux, so I turn to SO for help.
Here's a redacted copy of my nginx.conf, which proxies the requests to a Node.JS server:
server {
listen my.ip.address:80;
listen my.ip.address:443 ssl;
ssl_certificate /etc/ssl/my-server.com.crt;
ssl_certificate_key /etc/ssl/my-server.com.key;
server_name my-server.com;
client_max_body_size 700m;
root "/var/www/my-website/";
access_log "/var/www/my-website/logs/access_log";
error_log "/var/www/my-website/logs/error_log";
location / {
proxy_connect_timeout 300s;
proxy_send_timeout 600;
proxy_read_timeout 600;
proxy_buffer_size 64k;
proxy_buffers 16 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
proxy_pass_header Set-Cookie;
proxy_redirect off;
proxy_hide_header Vary;
proxy_set_header Accept-Encoding '';
proxy_pass http://my.ip.address:8080;
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-Accel-Internal /internal-nginx-static-location;
proxy_no_cache $arg_aid;
access_log off;
}
location /internal-nginx-static-location/ {
alias /var/www/my-website/;
add_header Cache-Control no-cache;
add_header X-Powered-By nginx;
internal;
}
}
I'm running nginx 1.10.3 on Ubuntu 16.04.3 LTS
Add below line before "ssl_certificate xxx" line and try
ssl on;
Updated:
Can you try with this simple config.
upstream myapp {
server http://my.ip.address:8080;
}
server {
listen 443;
server_name mywebsite;
access_log /var/log/nginx/app_access.log main;
error_log /var/log/nginx/app_error.log;
ssl on;
ssl_certificate /etc/ssl/certs/nginx.crt;
ssl_certificate_key /etc/ssl/certs/nginx.key;
location / {
proxy_pass http://myapp;
}
}
The problem was caused by a nano .conf.save backup on the nginx /sites-enabled folder which had a misconfigured https server declaration - I was not aware that nginx loaded files other than .conf.
'Connection refused' means that nothing was listening at the target IP and port you tried to connect to.
It has nothing to do with HTTPS, TLS, SSL, or certificates whatsoever.

nodejs nginx 502 gateway error

I am trying to use a nodejs app behind an nginx reverse proxy to handle the ssl
I have my app running on localhost:2000. I can confirm this as working with a curl command.
This is my nginx setup:
# the IP(s) on which your node server is running. I chose port 3000.
upstream dreamingoftech.uk {
server 127.0.0.1:2000;
keepalive 16;
}
# the nginx server instance
server {
listen 0.0.0.0:80;
server_name dreamingoftech.uk;
return 301 https://$host$request_uri;
}
#HTTPS
server {
listen 443 ssl http2;
server_name dreamingoftech.uk;
access_log /var/log/nginx/dreamingoftech.log;
error_log /var/log/nginx/dreamingoftech.error.log debug;
ssl_certificate /etc/letsencrypt/live/dreamingoftech.uk/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dreamingoftech.uk/privkey.pem;
include snippets/ssl-params.conf;
# pass the request to the node.js server with the correct headers and much more can be added, see nginx config options
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-Forwarded-Proto https;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://dreamingoftech.uk/;
proxy_redirect off;
#proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "";
proxy_ssl_session_reuse off;
proxy_cache_bypass $http_upgrade;
}
}
if I now curl https://dreamingoftech.uk, it takes a while but I do get the webpage delivered. albeit with the message:
curl: (18) transfer closed with 1 bytes remaining to read
However when viewed from a browser I get a 502 gateway error.
I have checked the error log and this is the result: ERROR LOG
I can't understand why the reverse proxy is adding such a time delay into the process. Any ideas would be greatly appreciated.
PS: in the upstream config I have tried localhost instead of 127.0.0.1 to no avail
I have almost the same configuration. Can you try the following
You can redirect all http to https
server {
listen 80;
return 301 https://$host$request_uri;
}
or for a specific site like this
server {
server_name dreamingoftech.uk;
return 301 https://dreamingoftech.uk$request_uri;
}
but choose only one for your case
and then you make sure you node server is running on http mode and not https.
Also you mentioned that you run node on port 3000, then use port 3000 and not 2000 as I can see in your config.
After you confirm the above redirect all packets into localhost like this
server {
listen 443;
server_name dreamingoftech.uk;
ssl_certificate /etc/letsencrypt/live/dreamingoftech.uk/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dreamingoftech.uk/privkey.pem;
ssl on;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
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;
# Fix the “It appears that your reverse proxy set up is broken" error.
proxy_pass http://localhost:3000;
proxy_read_timeout 90s;
proxy_redirect http://localhost:3000 https://dreamingoftech.uk;
}
}
Create a file and sum the above code put it in sites-available with a name like dreamingoftech.uk and the use ln -s to create a softlink into sites-enabled. go to your nginx.conf and make sure you include folder sites-enabled
Then must restart nginx to check if it works
#Stamos Thanks for your reply. I tried that but unfortunately it didn't work. I decided to try the most basic node app I could still using the basic modules I am using.
I tried this and it worked straight away.
The problem is with my app therefore. I will spend time rebuilding and testing step by step until I find the issue,
Thanks for your time!

Logs not coming through Nginx Reverse Proxy (Nginx config issue?)

We have Node.js applications sending logs to a URL which points to my Nginx Reverse Proxy server.
I have the nginx reverse proxy server setup in a docker container and then have a set of containers for Fluentd, ElasticSearch and Kibana which are meant to receive, collect and display these logs.
The only ports kept open on the server running these containers including nginx reverse proxy are 8080(http) and 443(https).
The logs get generated properly from the application as I have tested and confirmed that. Also, if I do the entire setup without the nginx reverse proxy in the docker container, then it all runs fine.
The same nginx reverse proxy is also being used to proxy other servers and they all are functioning fine.
The only problem seems to be the nginx reverse proxy setting which isn't able to receive the Node.js application logs which are in JSON format.
However Http and https request are going through.
I am using LetsEncrypt to generate SSL certificates automatically and auto generating this nginx config accordingly.
I have attached my nginx config file here:
# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
# scheme used to connect to this server
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
default $http_x_forwarded_proto;
'' $scheme;
}
# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
# server port the client connected to
map $http_x_forwarded_port $proxy_x_forwarded_port {
default $http_x_forwarded_port;
'' $server_port;
}
# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
# Connection header that may have been passed to this server
map $http_upgrade $proxy_connection {
default upgrade;
'' close;
}
# Set appropriate X-Forwarded-Ssl header
map $scheme $proxy_x_forwarded_ssl {
default off;
https on;
}
gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml applic
ation/xml+rss text/javascript;
log_format vhost '$host $remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log off;
# HTTP 1.1 support
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
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 $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_set_header X-Forwarded-Host $host;
# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
listen 80;
access_log /var/log/nginx/access.log vhost;
return 503;
}
upstream <hid_the_name> {
## Can be connect with "reverse-proxy" network
# fluentd
server 172.21.0.9:24224;
}
server {
server_name <hid_the_name>;
listen 80 so_keepalive=1m::10;
access_log /var/log/nginx/access.log vhost;
return 301 https://$host$request_uri;
}
server {
server_name <hid_the_name>;
listen 443 ssl so_keepalive=1m::10 http2 ;
access_log /var/log/nginx/access.log vhost;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-G
CM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-E
CDSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES2
56-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AE
S256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256
:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_certificate /etc/nginx/certs/<hid_the_name>.crt;
ssl_certificate_key /etc/nginx/certs/<hid_the_name>.key;
ssl_dhparam /etc/nginx/certs/<hid_the_name>.dhparam.pem;
add_header Strict-Transport-Security "max-age=31536000";
include /etc/nginx/vhost.d/default;
location / {
proxy_pass http://<hid_the_name>;
}
}
So this Config file was then being called in another nginx config file inside the http block. We can't accept TCP input in that block. So I just had to create another block for stream and then put in the necessary details inside that for the tcp connection and it is all good now.

How to install nginx with node js and how it works? [duplicate]

I've set up Node.js and Nginx on my server. Now I want to use it, but, before I start there are 2 questions:
How should they work together? How should I handle the requests?
There are 2 concepts for a Node.js server, which one is better:
a. Create a separate HTTP server for each website that needs it. Then load all JavaScript code at the start of the program, so the code is interpreted once.
b. Create one single Node.js server which handles all Node.js requests. This reads the requested files and evals their contents. So the files are interpreted on each request, but the server logic is much simpler.
It's not clear for me how to use Node.js correctly.
Nginx works as a front end server, which in this case proxies the requests to a node.js server. Therefore you need to set up an Nginx config file for node.
This is what I have done in my Ubuntu box:
Create the file yourdomain.example at /etc/nginx/sites-available/:
vim /etc/nginx/sites-available/yourdomain.example
In it you should have something like:
# the IP(s) on which your node server is running. I chose port 3000.
upstream app_yourdomain {
server 127.0.0.1:3000;
keepalive 8;
}
# the nginx server instance
server {
listen 80;
listen [::]:80;
server_name yourdomain.example www.yourdomain.example;
access_log /var/log/nginx/yourdomain.example.log;
# pass the request to the node.js server with the correct headers
# and much more can be added, see nginx config options
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;
proxy_pass http://app_yourdomain/;
proxy_redirect off;
}
}
If you want Nginx (>= 1.3.13) to handle websocket requests as well, add the following lines in the location / section:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Once you have this setup you must enable the site defined in the config file above:
cd /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/yourdomain.example yourdomain.example
Create your node server app at /var/www/yourdomain/app.js and run it at localhost:3000
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(3000, "127.0.0.1");
console.log('Server running at http://127.0.0.1:3000/');
Test for syntax mistakes:
nginx -t
Restart Nginx:
sudo /etc/init.d/nginx restart
Lastly start the node server:
cd /var/www/yourdomain/ && node app.js
Now you should see "Hello World" at yourdomain.example
One last note with to starting the node server: you should use some kind of monitoring system for the node daemon. There is an awesome tutorial on node with upstart and monit.
You can also setup multiple domain with Nginx, forwarding to multiple node.js processes.
For example to achieve these:
domain1.example -> to Node.js process running locally http://127.0.0.1:4000
domain2.example -> to Node.js process running locally http://127.0.0.1:5000
These ports (4000 and 5000) should be used to listen the app requests in your app code.
/etc/nginx/sites-enabled/domain1
server {
listen 80;
listen [::]:80;
server_name domain1.example;
access_log /var/log/nginx/domain1.access.log;
location / {
proxy_pass http://127.0.0.1:4000/;
}
}
In /etc/nginx/sites-enabled/domain2
server {
listen 80;
listen [::]:80;
server_name domain2.example;
access_log /var/log/nginx/domain2.access.log;
location / {
proxy_pass http://127.0.0.1:5000/;
}
}
You can also have different URLs for apps in one server configuration:
yourdomain.example/app1/* -> to Node.js process running locally
http://127.0.0.1:3000
yourdomain.example/app2/* -> to Node.js process
running locally http://127.0.0.1:4000
In /etc/nginx/sites-enabled/yourdomain:
server {
listen 80;
listen [::]:80;
server_name yourdomain.example;
location ^~ /app1/{
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;
proxy_pass http://127.0.0.1:3000/;
}
location ^~ /app2/{
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;
proxy_pass http://127.0.0.1:4000/;
}
}
Restart Nginx:
sudo service nginx restart
Starting applications.
node app1.js
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello from app1!\n');
}).listen(3000, "127.0.0.1");
console.log('Server running at http://127.0.0.1:3000/');
node app2.js
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello from app2!\n');
}).listen(4000, "127.0.0.1");
console.log('Server running at http://127.0.0.1:4000/');
I proxy independent Node Express applications through Nginx.
Thus new applications can be easily mounted and I can also run other stuff on the same server at different locations.
Here are more details on my setup with Nginx configuration example:
Deploy multiple Node applications on one web server in subfolders with Nginx
Things get tricky with Node when you need to move your application from from localhost to the internet.
There is no common approach for Node deployment.
Google can find tons of articles on this topic, but I was struggling to find the proper solution for the setup I need.
Basically, I have a web server and I want Node applications to be mounted to subfolders (i.e. http://myhost/demo/pet-project/) without introducing any configuration dependency to the application code.
At the same time I want other stuff like blog to run on the same web server.
Sounds simple huh? Apparently not.
In many examples on the web Node applications either run on port 80 or proxied by Nginx to the root.
Even though both approaches are valid for certain use cases, they do not meet my simple yet a little bit exotic criteria.
That is why I created my own Nginx configuration and here is an extract:
upstream pet_project {
server localhost:3000;
}
server {
listen 80;
listen [::]:80;
server_name frontend;
location /demo/pet-project {
alias /opt/demo/pet-project/public/;
try_files $uri $uri/ #pet-project;
}
location #pet-project {
rewrite /demo/pet-project(.*) $1 break;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $proxy_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://pet_project;
proxy_redirect http://pet_project/ /demo/pet-project/;
}
}
From this example you can notice that I mount my Pet Project Node application running on port 3000 to http://myhost/demo/pet-project.
First Nginx checks if whether the requested resource is a static file available at /opt/demo/pet-project/public/ and if so it serves it as is that is highly efficient, so we do not need to have a redundant layer like Connect static middleware.
Then all other requests are overwritten and proxied to Pet Project Node application, so the Node application does not need to know where it is actually mounted and thus can be moved anywhere purely by configuration.
proxy_redirect is a must to handle Location header properly. This is extremely important if you use res.redirect() in your Node application.
You can easily replicate this setup for multiple Node applications running on different ports and add more location handlers for other purposes.
From: http://skovalyov.blogspot.dk/2012/07/deploy-multiple-node-applications-on.html
Node.js with Nginx configuration.
$ sudo nano /etc/nginx/sites-available/subdomain.your-domain.example
add the following configuration so that Nginx acting as a proxy redirect to port 3000 traffic from the server when we come from subdomain.your_domain.example
upstream subdomain.your-domain.example {
server 127.0.0.1:3000;
}
server {
listen 80;
listen [::]:80;
server_name subdomain.your-domain.example;
access_log /var/log/nginx/subdomain.your-domain.access.log;
error_log /var/log/nginx/subdomain.your-domain.error.log debug;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarder-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://subdomain.your-domain.example;
proxy_redirect off;
}
}
I made a repository in Github which you can clone, vagrant-node-nginx-boilerplate
basically the node.js app at /var/www/nodeapp is
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(4570, '127.0.0.1');
console.log('Node Server running at 127.0.0.1:4570/');
and the nginx config at /etc/nginx/sites-available/ is
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/nodeapp;
index index.html index.htm;
server_name localhost;
location / {
proxy_pass http://127.0.0.1:4570;
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;
}
}
answering your question 2:
I would use option b simply because it consumes much less resources. with option 'a', every client will cause the server to consume a lot of memory, loading all the files you need (even though i like php, this is one of the problems with it). With option 'b' you can load your libraries (reusable code) and share them among all client requests.
But be ware that if you have multiple cores you should tweak node.js to use all of them.
Nginx can act as a reverse proxy server which works just like a project manager. When it gets a request it analyses it and forwards the request to upstream(project members) or handles itself. Nginx has two ways of handling a request based on how its configured.
serve the request
forward the request to another server
server{
server_name mydomain.example sub.mydomain.example;
location /{
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_pass_request_headers on;
}
location /static/{
alias /my/static/files/path;
}
}
Server the request
With this configuration, when the request URL is
mydomain.example/static/myjs.js it returns the myjs.js file in
/my/static/files/path folder. When you configure Nginx to serve
static files, it handles the request itself.
forward the request to another server
When the request URL is mydomain.example/dothis Nginx will forwards the
request to http://127.0.0.1:8000. The service which is running on the
localhost 8000 port will receive the request and returns the response
to Nginx and Nginx returns the response to the client.
When you run node.js server on the port 8000 Nginx will forward the request to node.js. Write node.js logic and handle the request. That's it you have your nodejs server running behind the Nginx server.
If you wish to run any other services other than nodejs just run another service like Django, flask, PHP on different ports and config it in Nginx.
You could also use node.js to generate static files into a directory served by nginx. Of course, some dynamic parts of your site could be served by node, and some by nginx (static).
Having some of them served by nginx increases your performance..
We can easily setup a Nodejs app by Nginx acting as a reverse proxy.
The following configuration assumes the NodeJS application is running on 127.0.0.1:8080,
server{
server_name domain.example sub.domain.example; # multiple domains
location /{
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_pass_request_headers on;
}
location /static/{
alias /absolute/path/to/static/files; # nginx will handle js/css
}
}
in above setup your Nodejs app will,
get HTTP_HOST header where you can apply domain specific logic to serve the response. '
Your Application must be managed by a process manager like pm2 or supervisor for handling situations/reusing sockets or resources etc.
Setup an error reporting service for getting production errors like sentry or rollbar
NOTE: you can setup logic for handing domain specific request routes, create a middleware for expressjs application
The best and simpler setup with Nginx and Nodejs is to use Nginx as an HTTP and TCP load balancer with proxy_protocol enabled. In this context, Nginx will be able to proxy incoming requests to nodejs, and also terminate SSL connections to the backend Nginx server(s), and not to the proxy server itself. (SSL-PassThrough)
In my opinion, there is no point in giving non-SSL examples, since all web apps are (or should be) using secure environments.
Example config for the proxy server, in /etc/nginx/nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
upstream webserver-http {
server 192.168.1.4; #use a host port instead if using docker
server 192.168.1.5; #use a host port instead if using docker
}
upstream nodejs-http {
server 192.168.1.4:8080; #nodejs listening port
server 192.168.1.5:8080; #nodejs listening port
}
server {
server_name example.com;
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 $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header Connection "";
add_header X-Upstream $upstream_addr;
proxy_redirect off;
proxy_connect_timeout 300;
proxy_http_version 1.1;
proxy_buffers 16 16k;
proxy_buffer_size 16k;
proxy_cache_background_update on;
proxy_pass http://webserver-http$request_uri;
}
}
server {
server_name node.example.com;
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 $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
add_header X-Upstream $upstream_addr;
proxy_redirect off;
proxy_connect_timeout 300;
proxy_http_version 1.1;
proxy_buffers 16 16k;
proxy_buffer_size 16k;
proxy_cache_background_update on;
proxy_pass http://nodejs-http$request_uri;
}
}
}
stream {
upstream webserver-https {
server 192.168.1.4:443; #use a host port instead if using docker
server 192.168.1.5:443; #use a host port instead if using docker
}
server {
proxy_protocol on;
tcp_nodelay on;
listen 443;
proxy_pass webserver-https;
}
log_format proxy 'Protocol: $protocol - $status $bytes_sent $bytes_received $session_time';
access_log /var/log/nginx/access.log proxy;
error_log /var/log/nginx/error.log debug;
}
Now, let's handle the backend webserver.
/etc/nginx/nginx.conf:
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
load_module /etc/nginx/modules/ngx_http_geoip2_module.so; # GeoIP2
events {
worker_connections 1024;
}
http {
variables_hash_bucket_size 64;
variables_hash_max_size 2048;
server_tokens off;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
autoindex off;
keepalive_timeout 30;
types_hash_bucket_size 256;
client_max_body_size 100m;
server_names_hash_bucket_size 256;
include mime.types;
default_type application/octet-stream;
index index.php index.html index.htm;
# GeoIP2
log_format main 'Proxy Protocol Address: [$proxy_protocol_addr] '
'"$request" $remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# GeoIP2
log_format main_geo 'Original Client Address: [$realip_remote_addr]- Proxy Protocol Address: [$proxy_protocol_addr] '
'Proxy Protocol Server Address:$proxy_protocol_server_addr - '
'"$request" $remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'$geoip2_data_country_iso $geoip2_data_country_name';
access_log /var/log/nginx/access.log main_geo; # GeoIP2
#===================== GEOIP2 =====================#
geoip2 /usr/share/geoip/GeoLite2-Country.mmdb {
$geoip2_metadata_country_build metadata build_epoch;
$geoip2_data_country_geonameid country geoname_id;
$geoip2_data_country_iso country iso_code;
$geoip2_data_country_name country names en;
$geoip2_data_country_is_eu country is_in_european_union;
}
#geoip2 /usr/share/geoip/GeoLite2-City.mmdb {
# $geoip2_data_city_name city names en;
# $geoip2_data_city_geonameid city geoname_id;
# $geoip2_data_continent_code continent code;
# $geoip2_data_continent_geonameid continent geoname_id;
# $geoip2_data_continent_name continent names en;
# $geoip2_data_location_accuracyradius location accuracy_radius;
# $geoip2_data_location_latitude location latitude;
# $geoip2_data_location_longitude location longitude;
# $geoip2_data_location_metrocode location metro_code;
# $geoip2_data_location_timezone location time_zone;
# $geoip2_data_postal_code postal code;
# $geoip2_data_rcountry_geonameid registered_country geoname_id;
# $geoip2_data_rcountry_iso registered_country iso_code;
# $geoip2_data_rcountry_name registered_country names en;
# $geoip2_data_rcountry_is_eu registered_country is_in_european_union;
# $geoip2_data_region_geonameid subdivisions 0 geoname_id;
# $geoip2_data_region_iso subdivisions 0 iso_code;
# $geoip2_data_region_name subdivisions 0 names en;
#}
#=================Basic Compression=================#
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/css text/xml text/plain application/javascript image/jpeg image/png image/gif image/x-icon image/svg+xml image/webp application/font-woff application/json application/vnd.ms-fontobject application/vnd.ms-powerpoint;
gzip_static on;
include /etc/nginx/sites-enabled/example.com-https.conf;
}
Now, let's configure the virtual host with this SSL and proxy_protocol enabled config at /etc/nginx/sites-available/example.com-https.conf:
server {
real_ip_header proxy_protocol;
set_real_ip_from 192.168.1.1; #proxy server ip address
#set_real_ip_from proxy; #proxy container hostname if you are using docker
server_name 192.168.1.4; #Your current server ip address. It will redirect to the domain name.
listen 80;
listen 443 ssl http2;
listen [::]:80;
listen [::]:443 ssl http2;
ssl_certificate /etc/nginx/certs/example.com.crt;
ssl_certificate_key /etc/nginx/certs/example.com.key;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
return 301 https://example.com$request_uri;
}
server {
real_ip_header proxy_protocol;
set_real_ip_from 192.168.1.1; #proxy server ip address
#set_real_ip_from proxy; #proxy container hostname if you are using docker
server_name example.com;
listen *:80;
return 301 https://example.com$request_uri;
}
server {
real_ip_header proxy_protocol;
set_real_ip_from 192.168.1.1; #proxy server ip address
#set_real_ip_from proxy; #proxy container hostname if you are using docker
server_name www.example.com;
listen 80;
listen 443 http2;
listen [::]:80;
listen [::]:443 ssl http2 ;
ssl_certificate /etc/nginx/certs/example.com.crt;
ssl_certificate_key /etc/nginx/certs/example.com.key;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
return 301 https://example.com$request_uri;
}
server {
real_ip_header proxy_protocol;
set_real_ip_from 192.168.1.1; #proxy server ip address
#set_real_ip_from proxy; #proxy container hostname if you are using docker
server_name example.com;
listen 443 proxy_protocol ssl http2;
listen [::]:443 proxy_protocol ssl http2;
root /var/www/html;
charset UTF-8;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy no-referrer;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
keepalive_timeout 70;
ssl_buffer_size 1400;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=86400;
resolver_timeout 10;
ssl_certificate /etc/nginx/certs/example.com.crt;
ssl_certificate_key /etc/nginx/certs/example.com.key;
ssl_trusted_certificate /etc/nginx/certs/example.com.crt;
location ~* \.(jpg|jpe?g|gif|png|ico|cur|gz|svgz|mp4|ogg|ogv|webm|htc|css|js|otf|eot|svg|ttf|woff|woff2)(\?ver=[0-9.]+)?$ {
expires modified 1M;
add_header Access-Control-Allow-Origin '*';
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
access_log off;
}
location ~ /.well-known { #For issuing LetsEncrypt Certificates
allow all;
}
location / {
index index.php;
try_files $uri $uri/ /index.php?$args;
}
error_page 404 /404.php;
location ~ \.php$ {
try_files $uri =404;
fastcgi_index index.php;
fastcgi_pass unix:/tmp/php7-fpm.sock;
#fastcgi_pass php-container-hostname:9000; (if using docker)
fastcgi_pass_request_headers on;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
fastcgi_ignore_client_abort off;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_request_buffering on;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
include fastcgi_params;
}
location = /robots.txt {
access_log off;
log_not_found off;
}
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
And lastly, a sample of 2 nodejs webservers:
First server:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello From Nodejs\n');
}).listen(8080, "192.168.1.4");
console.log('Server running at http://192.168.1.4:8080/');
Second server:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello From Nodejs\n');
}).listen(8080, "192.168.1.5");
console.log('Server running at http://192.168.1.5:8080/');
Now everything should be perfectly working and load-balanced.
A while back I wrote about How to set up Nginx as a TCP load balancer in Docker. Check it out if you are using Docker.
You can run nodejs using pm2 if you want to manage each microservice means and run it. Node will be running in a port right just configure that port in Nginx (/etc/nginx/sites-enabled/domain.example)
server{
listen 80;
server_name domain.example www.domain.example;
location / {
return 403;
}
location /url {
proxy_pass http://localhost:51967/info;
}
}
Check weather localhost is running or not by using ping.
And
Create one single Node.js server which handles all Node.js requests. This reads the requested files and evals their contents. So the files are interpreted on each request, but the server logic is much simpler.
This is best and as you said easier too

Node.js + Nginx - What now?

I've set up Node.js and Nginx on my server. Now I want to use it, but, before I start there are 2 questions:
How should they work together? How should I handle the requests?
There are 2 concepts for a Node.js server, which one is better:
a. Create a separate HTTP server for each website that needs it. Then load all JavaScript code at the start of the program, so the code is interpreted once.
b. Create one single Node.js server which handles all Node.js requests. This reads the requested files and evals their contents. So the files are interpreted on each request, but the server logic is much simpler.
It's not clear for me how to use Node.js correctly.
Nginx works as a front end server, which in this case proxies the requests to a node.js server. Therefore you need to set up an Nginx config file for node.
This is what I have done in my Ubuntu box:
Create the file yourdomain.example at /etc/nginx/sites-available/:
vim /etc/nginx/sites-available/yourdomain.example
In it you should have something like:
# the IP(s) on which your node server is running. I chose port 3000.
upstream app_yourdomain {
server 127.0.0.1:3000;
keepalive 8;
}
# the nginx server instance
server {
listen 80;
listen [::]:80;
server_name yourdomain.example www.yourdomain.example;
access_log /var/log/nginx/yourdomain.example.log;
# pass the request to the node.js server with the correct headers
# and much more can be added, see nginx config options
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;
proxy_pass http://app_yourdomain/;
proxy_redirect off;
}
}
If you want Nginx (>= 1.3.13) to handle websocket requests as well, add the following lines in the location / section:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Once you have this setup you must enable the site defined in the config file above:
cd /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/yourdomain.example yourdomain.example
Create your node server app at /var/www/yourdomain/app.js and run it at localhost:3000
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(3000, "127.0.0.1");
console.log('Server running at http://127.0.0.1:3000/');
Test for syntax mistakes:
nginx -t
Restart Nginx:
sudo /etc/init.d/nginx restart
Lastly start the node server:
cd /var/www/yourdomain/ && node app.js
Now you should see "Hello World" at yourdomain.example
One last note with to starting the node server: you should use some kind of monitoring system for the node daemon. There is an awesome tutorial on node with upstart and monit.
You can also setup multiple domain with Nginx, forwarding to multiple node.js processes.
For example to achieve these:
domain1.example -> to Node.js process running locally http://127.0.0.1:4000
domain2.example -> to Node.js process running locally http://127.0.0.1:5000
These ports (4000 and 5000) should be used to listen the app requests in your app code.
/etc/nginx/sites-enabled/domain1
server {
listen 80;
listen [::]:80;
server_name domain1.example;
access_log /var/log/nginx/domain1.access.log;
location / {
proxy_pass http://127.0.0.1:4000/;
}
}
In /etc/nginx/sites-enabled/domain2
server {
listen 80;
listen [::]:80;
server_name domain2.example;
access_log /var/log/nginx/domain2.access.log;
location / {
proxy_pass http://127.0.0.1:5000/;
}
}
You can also have different URLs for apps in one server configuration:
yourdomain.example/app1/* -> to Node.js process running locally
http://127.0.0.1:3000
yourdomain.example/app2/* -> to Node.js process
running locally http://127.0.0.1:4000
In /etc/nginx/sites-enabled/yourdomain:
server {
listen 80;
listen [::]:80;
server_name yourdomain.example;
location ^~ /app1/{
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;
proxy_pass http://127.0.0.1:3000/;
}
location ^~ /app2/{
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;
proxy_pass http://127.0.0.1:4000/;
}
}
Restart Nginx:
sudo service nginx restart
Starting applications.
node app1.js
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello from app1!\n');
}).listen(3000, "127.0.0.1");
console.log('Server running at http://127.0.0.1:3000/');
node app2.js
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello from app2!\n');
}).listen(4000, "127.0.0.1");
console.log('Server running at http://127.0.0.1:4000/');
I proxy independent Node Express applications through Nginx.
Thus new applications can be easily mounted and I can also run other stuff on the same server at different locations.
Here are more details on my setup with Nginx configuration example:
Deploy multiple Node applications on one web server in subfolders with Nginx
Things get tricky with Node when you need to move your application from from localhost to the internet.
There is no common approach for Node deployment.
Google can find tons of articles on this topic, but I was struggling to find the proper solution for the setup I need.
Basically, I have a web server and I want Node applications to be mounted to subfolders (i.e. http://myhost/demo/pet-project/) without introducing any configuration dependency to the application code.
At the same time I want other stuff like blog to run on the same web server.
Sounds simple huh? Apparently not.
In many examples on the web Node applications either run on port 80 or proxied by Nginx to the root.
Even though both approaches are valid for certain use cases, they do not meet my simple yet a little bit exotic criteria.
That is why I created my own Nginx configuration and here is an extract:
upstream pet_project {
server localhost:3000;
}
server {
listen 80;
listen [::]:80;
server_name frontend;
location /demo/pet-project {
alias /opt/demo/pet-project/public/;
try_files $uri $uri/ #pet-project;
}
location #pet-project {
rewrite /demo/pet-project(.*) $1 break;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $proxy_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://pet_project;
proxy_redirect http://pet_project/ /demo/pet-project/;
}
}
From this example you can notice that I mount my Pet Project Node application running on port 3000 to http://myhost/demo/pet-project.
First Nginx checks if whether the requested resource is a static file available at /opt/demo/pet-project/public/ and if so it serves it as is that is highly efficient, so we do not need to have a redundant layer like Connect static middleware.
Then all other requests are overwritten and proxied to Pet Project Node application, so the Node application does not need to know where it is actually mounted and thus can be moved anywhere purely by configuration.
proxy_redirect is a must to handle Location header properly. This is extremely important if you use res.redirect() in your Node application.
You can easily replicate this setup for multiple Node applications running on different ports and add more location handlers for other purposes.
From: http://skovalyov.blogspot.dk/2012/07/deploy-multiple-node-applications-on.html
Node.js with Nginx configuration.
$ sudo nano /etc/nginx/sites-available/subdomain.your-domain.example
add the following configuration so that Nginx acting as a proxy redirect to port 3000 traffic from the server when we come from subdomain.your_domain.example
upstream subdomain.your-domain.example {
server 127.0.0.1:3000;
}
server {
listen 80;
listen [::]:80;
server_name subdomain.your-domain.example;
access_log /var/log/nginx/subdomain.your-domain.access.log;
error_log /var/log/nginx/subdomain.your-domain.error.log debug;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarder-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://subdomain.your-domain.example;
proxy_redirect off;
}
}
I made a repository in Github which you can clone, vagrant-node-nginx-boilerplate
basically the node.js app at /var/www/nodeapp is
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(4570, '127.0.0.1');
console.log('Node Server running at 127.0.0.1:4570/');
and the nginx config at /etc/nginx/sites-available/ is
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/nodeapp;
index index.html index.htm;
server_name localhost;
location / {
proxy_pass http://127.0.0.1:4570;
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;
}
}
answering your question 2:
I would use option b simply because it consumes much less resources. with option 'a', every client will cause the server to consume a lot of memory, loading all the files you need (even though i like php, this is one of the problems with it). With option 'b' you can load your libraries (reusable code) and share them among all client requests.
But be ware that if you have multiple cores you should tweak node.js to use all of them.
Nginx can act as a reverse proxy server which works just like a project manager. When it gets a request it analyses it and forwards the request to upstream(project members) or handles itself. Nginx has two ways of handling a request based on how its configured.
serve the request
forward the request to another server
server{
server_name mydomain.example sub.mydomain.example;
location /{
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_pass_request_headers on;
}
location /static/{
alias /my/static/files/path;
}
}
Server the request
With this configuration, when the request URL is
mydomain.example/static/myjs.js it returns the myjs.js file in
/my/static/files/path folder. When you configure Nginx to serve
static files, it handles the request itself.
forward the request to another server
When the request URL is mydomain.example/dothis Nginx will forwards the
request to http://127.0.0.1:8000. The service which is running on the
localhost 8000 port will receive the request and returns the response
to Nginx and Nginx returns the response to the client.
When you run node.js server on the port 8000 Nginx will forward the request to node.js. Write node.js logic and handle the request. That's it you have your nodejs server running behind the Nginx server.
If you wish to run any other services other than nodejs just run another service like Django, flask, PHP on different ports and config it in Nginx.
You could also use node.js to generate static files into a directory served by nginx. Of course, some dynamic parts of your site could be served by node, and some by nginx (static).
Having some of them served by nginx increases your performance..
We can easily setup a Nodejs app by Nginx acting as a reverse proxy.
The following configuration assumes the NodeJS application is running on 127.0.0.1:8080,
server{
server_name domain.example sub.domain.example; # multiple domains
location /{
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_pass_request_headers on;
}
location /static/{
alias /absolute/path/to/static/files; # nginx will handle js/css
}
}
in above setup your Nodejs app will,
get HTTP_HOST header where you can apply domain specific logic to serve the response. '
Your Application must be managed by a process manager like pm2 or supervisor for handling situations/reusing sockets or resources etc.
Setup an error reporting service for getting production errors like sentry or rollbar
NOTE: you can setup logic for handing domain specific request routes, create a middleware for expressjs application
The best and simpler setup with Nginx and Nodejs is to use Nginx as an HTTP and TCP load balancer with proxy_protocol enabled. In this context, Nginx will be able to proxy incoming requests to nodejs, and also terminate SSL connections to the backend Nginx server(s), and not to the proxy server itself. (SSL-PassThrough)
In my opinion, there is no point in giving non-SSL examples, since all web apps are (or should be) using secure environments.
Example config for the proxy server, in /etc/nginx/nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
upstream webserver-http {
server 192.168.1.4; #use a host port instead if using docker
server 192.168.1.5; #use a host port instead if using docker
}
upstream nodejs-http {
server 192.168.1.4:8080; #nodejs listening port
server 192.168.1.5:8080; #nodejs listening port
}
server {
server_name example.com;
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 $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header Connection "";
add_header X-Upstream $upstream_addr;
proxy_redirect off;
proxy_connect_timeout 300;
proxy_http_version 1.1;
proxy_buffers 16 16k;
proxy_buffer_size 16k;
proxy_cache_background_update on;
proxy_pass http://webserver-http$request_uri;
}
}
server {
server_name node.example.com;
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 $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
add_header X-Upstream $upstream_addr;
proxy_redirect off;
proxy_connect_timeout 300;
proxy_http_version 1.1;
proxy_buffers 16 16k;
proxy_buffer_size 16k;
proxy_cache_background_update on;
proxy_pass http://nodejs-http$request_uri;
}
}
}
stream {
upstream webserver-https {
server 192.168.1.4:443; #use a host port instead if using docker
server 192.168.1.5:443; #use a host port instead if using docker
}
server {
proxy_protocol on;
tcp_nodelay on;
listen 443;
proxy_pass webserver-https;
}
log_format proxy 'Protocol: $protocol - $status $bytes_sent $bytes_received $session_time';
access_log /var/log/nginx/access.log proxy;
error_log /var/log/nginx/error.log debug;
}
Now, let's handle the backend webserver.
/etc/nginx/nginx.conf:
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
load_module /etc/nginx/modules/ngx_http_geoip2_module.so; # GeoIP2
events {
worker_connections 1024;
}
http {
variables_hash_bucket_size 64;
variables_hash_max_size 2048;
server_tokens off;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
autoindex off;
keepalive_timeout 30;
types_hash_bucket_size 256;
client_max_body_size 100m;
server_names_hash_bucket_size 256;
include mime.types;
default_type application/octet-stream;
index index.php index.html index.htm;
# GeoIP2
log_format main 'Proxy Protocol Address: [$proxy_protocol_addr] '
'"$request" $remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# GeoIP2
log_format main_geo 'Original Client Address: [$realip_remote_addr]- Proxy Protocol Address: [$proxy_protocol_addr] '
'Proxy Protocol Server Address:$proxy_protocol_server_addr - '
'"$request" $remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'$geoip2_data_country_iso $geoip2_data_country_name';
access_log /var/log/nginx/access.log main_geo; # GeoIP2
#===================== GEOIP2 =====================#
geoip2 /usr/share/geoip/GeoLite2-Country.mmdb {
$geoip2_metadata_country_build metadata build_epoch;
$geoip2_data_country_geonameid country geoname_id;
$geoip2_data_country_iso country iso_code;
$geoip2_data_country_name country names en;
$geoip2_data_country_is_eu country is_in_european_union;
}
#geoip2 /usr/share/geoip/GeoLite2-City.mmdb {
# $geoip2_data_city_name city names en;
# $geoip2_data_city_geonameid city geoname_id;
# $geoip2_data_continent_code continent code;
# $geoip2_data_continent_geonameid continent geoname_id;
# $geoip2_data_continent_name continent names en;
# $geoip2_data_location_accuracyradius location accuracy_radius;
# $geoip2_data_location_latitude location latitude;
# $geoip2_data_location_longitude location longitude;
# $geoip2_data_location_metrocode location metro_code;
# $geoip2_data_location_timezone location time_zone;
# $geoip2_data_postal_code postal code;
# $geoip2_data_rcountry_geonameid registered_country geoname_id;
# $geoip2_data_rcountry_iso registered_country iso_code;
# $geoip2_data_rcountry_name registered_country names en;
# $geoip2_data_rcountry_is_eu registered_country is_in_european_union;
# $geoip2_data_region_geonameid subdivisions 0 geoname_id;
# $geoip2_data_region_iso subdivisions 0 iso_code;
# $geoip2_data_region_name subdivisions 0 names en;
#}
#=================Basic Compression=================#
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/css text/xml text/plain application/javascript image/jpeg image/png image/gif image/x-icon image/svg+xml image/webp application/font-woff application/json application/vnd.ms-fontobject application/vnd.ms-powerpoint;
gzip_static on;
include /etc/nginx/sites-enabled/example.com-https.conf;
}
Now, let's configure the virtual host with this SSL and proxy_protocol enabled config at /etc/nginx/sites-available/example.com-https.conf:
server {
real_ip_header proxy_protocol;
set_real_ip_from 192.168.1.1; #proxy server ip address
#set_real_ip_from proxy; #proxy container hostname if you are using docker
server_name 192.168.1.4; #Your current server ip address. It will redirect to the domain name.
listen 80;
listen 443 ssl http2;
listen [::]:80;
listen [::]:443 ssl http2;
ssl_certificate /etc/nginx/certs/example.com.crt;
ssl_certificate_key /etc/nginx/certs/example.com.key;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
return 301 https://example.com$request_uri;
}
server {
real_ip_header proxy_protocol;
set_real_ip_from 192.168.1.1; #proxy server ip address
#set_real_ip_from proxy; #proxy container hostname if you are using docker
server_name example.com;
listen *:80;
return 301 https://example.com$request_uri;
}
server {
real_ip_header proxy_protocol;
set_real_ip_from 192.168.1.1; #proxy server ip address
#set_real_ip_from proxy; #proxy container hostname if you are using docker
server_name www.example.com;
listen 80;
listen 443 http2;
listen [::]:80;
listen [::]:443 ssl http2 ;
ssl_certificate /etc/nginx/certs/example.com.crt;
ssl_certificate_key /etc/nginx/certs/example.com.key;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
return 301 https://example.com$request_uri;
}
server {
real_ip_header proxy_protocol;
set_real_ip_from 192.168.1.1; #proxy server ip address
#set_real_ip_from proxy; #proxy container hostname if you are using docker
server_name example.com;
listen 443 proxy_protocol ssl http2;
listen [::]:443 proxy_protocol ssl http2;
root /var/www/html;
charset UTF-8;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy no-referrer;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
keepalive_timeout 70;
ssl_buffer_size 1400;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=86400;
resolver_timeout 10;
ssl_certificate /etc/nginx/certs/example.com.crt;
ssl_certificate_key /etc/nginx/certs/example.com.key;
ssl_trusted_certificate /etc/nginx/certs/example.com.crt;
location ~* \.(jpg|jpe?g|gif|png|ico|cur|gz|svgz|mp4|ogg|ogv|webm|htc|css|js|otf|eot|svg|ttf|woff|woff2)(\?ver=[0-9.]+)?$ {
expires modified 1M;
add_header Access-Control-Allow-Origin '*';
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
access_log off;
}
location ~ /.well-known { #For issuing LetsEncrypt Certificates
allow all;
}
location / {
index index.php;
try_files $uri $uri/ /index.php?$args;
}
error_page 404 /404.php;
location ~ \.php$ {
try_files $uri =404;
fastcgi_index index.php;
fastcgi_pass unix:/tmp/php7-fpm.sock;
#fastcgi_pass php-container-hostname:9000; (if using docker)
fastcgi_pass_request_headers on;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
fastcgi_ignore_client_abort off;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_request_buffering on;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
include fastcgi_params;
}
location = /robots.txt {
access_log off;
log_not_found off;
}
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
And lastly, a sample of 2 nodejs webservers:
First server:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello From Nodejs\n');
}).listen(8080, "192.168.1.4");
console.log('Server running at http://192.168.1.4:8080/');
Second server:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello From Nodejs\n');
}).listen(8080, "192.168.1.5");
console.log('Server running at http://192.168.1.5:8080/');
Now everything should be perfectly working and load-balanced.
A while back I wrote about How to set up Nginx as a TCP load balancer in Docker. Check it out if you are using Docker.
You can run nodejs using pm2 if you want to manage each microservice means and run it. Node will be running in a port right just configure that port in Nginx (/etc/nginx/sites-enabled/domain.example)
server{
listen 80;
server_name domain.example www.domain.example;
location / {
return 403;
}
location /url {
proxy_pass http://localhost:51967/info;
}
}
Check weather localhost is running or not by using ping.
And
Create one single Node.js server which handles all Node.js requests. This reads the requested files and evals their contents. So the files are interpreted on each request, but the server logic is much simpler.
This is best and as you said easier too

Resources