Node-red server with nginx reverse proxy howto guide

Navigatable version on github

Ubuntu 20.04 - nigix - https - ssl - security - Fail2ban 403 bans, etc

Table of Contents


This Guide is for setting up a secure Dedicated Server.

Running node-red

This guide assumes you have a running server with Ubuntu 20.04 and that you have access to terminal on the server. You can host this yourself however this guide does not address port forwarding from routers or DMZ settings and or ipv6 settings.

You need a Domain Name and the ability to point it to your IP address. Let’s Encrypt doesn’t issue certificates for bare IP addresses, only domain names. You’ll need to register a domain name in order to get a Let’s Encrypt certificate.

What your server needs: a visable IP address to the internet to include if you wish a ipv6 address.

I used a VPS (Virtual Private Server) provider. For less than $10 a month, I have a server, and I have complete root access to and can spin up Ubuntu on. I used I was not paid or compensated to link them. Its just what I use.

I suggest you purchase a server close to your geographical location.

If all you are going to use this server for is node-red you will only need 1gb of ram and 1 cpu however I recommend 2gb of ram and 2 cpu's

MORE! ram and cpu if you plan on running Huge flows with multiple/constantly running injections

This Guide is not for individuals who fear the CLI (command line interface). If you have issues your going to need to read and search. You can always open an issue here and if i have time I can try to help.


Bring up a terminal on the server


You should never log into your server as root. example ssh root@youripaddress
If you have not or no privileged user account exists lets create one.

If you have a privileged user account you can skip this step

In terminal:

$ adduser YourChosenNameHere

Give your new user account sudo rights by appending (-a) the sudo group (-G) to the user’s group membership:

In terminal:

$ usermod -a -G sudo YourChosenNameHere

Exit root account and login with the new privileged user account you have created


Update the local repositories and upgrade the operating system and installed applications by applying the latest patches.

Keep settings or files during the upgrade if asked. Follow the suggestions presented in terminal

In terminal:

$ sudo apt update && sudo apt upgrade -y


If you have a firewall setup you can skip this step.

Install a firewall, enable it, and configure it only to allow network traffic that you designate

In terminal:

$ sudo apt install ufw

UFW (universal firewall) denies all incoming connections and allows all outgoing connections

Enable access to SSH, HTTP, and HTTPS

In terminal:

$ sudo ufw allow ssh
$ sudo ufw allow http
$ sudo ufw allow https

Now lets start UFW

In terminal:

$ sudo ufw enable

You can see what services are allowed and denied with:

In terminal:

$ sudo ufw status

If you ever want to disable UFW, you can do so by typing:

In terminal:

$ sudo ufw disable


Fail2ban is an application that examines server logs looking for repeated or automated attacks. If any are found, it will alter the firewall to block the attacker’s IP address either permanently or for a specified amount of time.

In terminal:

$ sudo apt install fail2ban -y

Copy the included configuration file

In terminal:

$ sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

And restart Fail2ban

In terminal:

$ sudo service fail2ban restart


Install follows the guide @

Using the install script method works for Ubuntu


Install possible dependency build-essential first

In terminal:

$ sudo apt install build-essential git


Install nod-red and its dependencies

In terminal:

$ bash <(curl -sL


We want node-red to run automatically every time the server restarts

In terminal:
$ sudo systemctl enable nodered.service


We need to setup our DNS for our domain. Install Nginx to reverse proxy so users don't have to enter a port to get to the server. Install Lets encrypt certbot to issue ssl certificates and automate the process so you never have to manually renew ssl certificates; also so users can access the site using https for security and so browsers will not complain and block access. Configure fail2ban so random people on the internet can't mess with your server.


Nginx is a web server which can also be used as a reverse proxy.

By default, Nginx is configured to start automatically when the server boots/reboots

In terminal:

$ sudo apt install nginx

Check to see if Nginx is running

In terminal:

$ systemctl status nginx

Check to see if Nginx is hosting properly.

In a web browser type your servers ip address with no port. IE http://server_ip

You should see a welcome to nginx page


You need to tie your server ipv4 and if you have one ipv6 addresses to you DNS records.
I like to use subdomains for my reverse proxy needs in Nginx.

Log into your domain name hosting provider of choice and add some records.

DNS advice is outside the scope of this tutorial. You will need to learn and or ask your provider for help if you do not understand the steps below.

Create an A record for your ipv4 server address.

Create an AAAA record for your ipv6 server address.

Now Create a CNAME record pointing a subdomain to your domain name. IE

It should look somting like this:

Hostname Type Address / Value
A server_ipv4_address
AAAA server_ipv6_address
node-red CNAME

The goal of all this is to create the address for users to access

You can pick a subdomain name of your choice.

Testing that your domain works with Nginx. Enter your domain name.

You should see a welcome to nginx page

If not DO NOT PASS GO. Fix your domain issues.


This is used so when users goto they are directed to the node-red server running on port :1880

First create a server block file for the url , making sure to change:


To the that you own

In terminal:

$ sudo nano /etc/nginx/sites-available/

This will open a text editor

Paste the following into the editor, making sure to change one line:


To the that you own

#proxy for node-red @ port :1880
server {
    	listen 80;
	listen [::]:80;


    	location = /robots.txt {
    		add_header  Content-Type  text/plain;
    		return 200 "User-agent: *\nDisallow: /\n";
    	location / {

		#Defines the HTTP protocol version for proxying
		#by default it it set to 1.0.
		#For Websockets and keepalive connections you need to use the version 1.1    
		proxy_http_version  1.1;

		#Sets conditions under which the response will not be taken from a cache.
		proxy_cache_bypass  $http_upgrade;

		#These header fields are required if your application is using Websockets
		proxy_set_header Upgrade $http_upgrade;

		#These header fields are required if your application is using Websockets    
		proxy_set_header Connection "upgrade";

		#The $host variable in the following order of precedence contains:
		#hostname from the request line, or hostname from the Host request header field
		#or the server name matching a request.    
		proxy_set_header Host $host;

		#Forwards the real visitor remote IP address to the proxied server
		proxy_set_header X-Real-IP $remote_addr;

		#A list containing the IP addresses of every server the client has been proxied through    
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

		#When used inside an HTTPS server block, each HTTP response from the proxied server is rewritten to HTTPS.    
		proxy_set_header X-Forwarded-Proto $scheme;

		#Defines the original host requested by the client.    
		proxy_set_header X-Forwarded-Host $host;

		#Defines the original port requested by the client.    
		proxy_set_header X-Forwarded-Port $server_port;


Now enable the file by creating a link from it to the sites-enabled directory, which Nginx reads from during startup:

making sure to change:


To the that you own

In terminal:

$ sudo ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/

To avoid a possible hash bucket memory problem that can arise from adding additional server names, it is necessary to adjust a single value in the /etc/nginx/nginx.conf file. Open the file:

In terminal:

$ sudo nano /etc/nginx/nginx.conf

Now uncomment (remove the # symbol) from the http section line server_names_hash_bucket_size 64; so it looks like this:

http {
    ...ignore stuff here...
    server_names_hash_bucket_size 64;
    ...ignore stuff here...

Next, test to make sure that there are no syntax errors in any of your Nginx files:

In terminal:

$ sudo nginx -t

Restart Nginx to enable your changes:

In terminal:

$ sudo systemctl restart nginx

Later if we ever need to disable a server block file simply remove the symbolic link.

In terminal:

$ sudo rm /etc/nginx/sites-enabled/

And don't forget to restart to effect changes

In terminal:

$ sudo systemctl restart nginx

Test if the reverse proxy is working

Goto your website, non https, and make sure it works

The node-red page should load. I know its tempting but do not configure it now. exit the page / close the browser.


Install certbot to get ssl certificates for domain

at this time cerbot not relesed for ppa package use snap

In terminal:

$ sudo snap install --beta certbot --classic

Generate the certificate for domain
making sure to change:
To the that you own

In terminal:

$ sudo certbot --nginx -d

If asked to redirect HTTP traffic to HTTPS, removing HTTP access:

Select 2: Redirect - Make all requests redirect to secure HTTPS access.

Test if https is working

Goto your website, https, and make sure it works

The node-red page should load. I know its tempting but do not configure it now. exit the page / close the browser.

Test that lets encrypt cerbot is working and will automatically update certificates before they expire

In terminal:

$ sudo certbot renew --dry-run


Setup node-reds built in login a password for the admin area, also create a fail2ban filter for 4xx failed requests.


First lets hash a password so we can add it to the settings.js file.

My method of choice for this is to use bcryptjs

Add to your node-red manage pallet node-red-contrib-bcrypt

Now import this flow into node-red

[{"id":"6fb8f86c.3dd218","type":"comment","z":"93ccda7b.0f6118","name":"create a key for a password","info":"","x":220,"y":120,"wires":[]},{"id":"2c990cc6.7e76e4","type":"bcrypt","z":"93ccda7b.0f6118","name":"","action":"encrypt","field":"payload","hash":"payload","rounds":"10","x":570,"y":160,"wires":[["480852c7.37515c"]]},{"id":"e203ec3d.eb8938","type":"inject","z":"93ccda7b.0f6118","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":160,"wires":[["15919cce.3e10a3"]]},{"id":"15919cce.3e10a3","type":"change","z":"93ccda7b.0f6118","name":"Enter Password Here","rules":[{"t":"set","p":"payload","pt":"msg","to":"MyPassword","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":160,"wires":[["2c990cc6.7e76e4"]]},{"id":"480852c7.37515c","type":"debug","z":"93ccda7b.0f6118","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":730,"y":160,"wires":[]}]

Replace MyPassword with your password!!!

Trigger the flow and check the msg.payload

copy the payload as it is your password hash you will need next

We need to setup a user name and password by editing node-red's settings.js file.

Edit the settings.js file in our current privileged_user_account /home/YourChosenNameHere/.node-red/settings.js

We can use $HOME variable to accomplish this

In terminal:

$ nano $HOME/.node-red/settings.js

This will open a text editor

Edit the adminAuth section removing forward slashes // so it looks like this:

adminAuth: {
    type: "credentials",
    users: [
            username: "admin",
            password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",
            permissions: "*"

Replace $2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN. within QUOTES with your password hash you created.

More information on this can be found at:


Every time a user....hacker...etc tries to login to node-red with a bad user name or password it sends a 403 Forbidden error

This response code indicates that the server understood the request but refuses to authorize it

What we would like todo is ban users who have multiple failed attempts to login

That way we can keep the bad people from password spamming the server trying to gain access.

Lets block 404, 444, 403, 400 responses from the server. Feel free to read up on these codes but the gist of it is we will be blocking people trying to gain access to areas they should not be trying to get into.

Create a new fail2ban filter for 404, 444, 403, 400 responses from the server.

In terminal:

sudo nano /etc/fail2ban/filter.d/nginx-4xx.conf

This will open a text editor

Add the following to the file:

failregex = ^<HOST>.*"(GET|POST).*" (404|444|403|400) .*$
ignoreregex =

Now, edit your /etc/fail2ban/jail.conf, to use the new filter we created and ban after 10 failed attempts.

In terminal:

$ sudo nano /etc/fail2ban/jail.conf

This will open a text editor

Add the following to the bottom of the file:

enabled = true
port = http,https
logpath = %(nginx_access_log)s
maxretry = 10

Restart fail2ban

In terminal:

$ sudo service fail2ban restart

Check that the new rule is working

In terminal:

sudo fail2ban-client status nginx-4xx

Now the user will be banned for the amount of time you have set in fail2ban after 10 failed attempts

you can check this is working with the logs.

In terminal:

$ sudo cat /var/log/fail2ban.log | grep Ban

and to check in on what the nginx log is producing

In terminal:

$ sudo cat /var/log/nginx/access.log



Many malicious actors will just take note of the fixed ban time and just keep at it every 10min. Instead of increasing the fixed time we want to increase the time for specific bad actors and not every one.

In terminal:

$ sudo nano /etc/fail2ban/jail.conf

This will open a text editor

search for and remove the # sign in front of the following lines so they look like this:

bantime.increment = true

bantime.factor = 1

bantime.formula = ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFact>

Restart fail2ban

In terminal:

$ sudo service fail2ban restart



PLACE HOLDER for docker version of the guide next week

1 Like

Excellent write up. I'll be bookmarking this for later.
Thank you.

Note: I haven't tested it yet but if/when I do I'll feed back on success or any issues I encounter.

1 Like

Shouldn't this be in the Projects category?

I have moved it to the FAQs category.

1 Like

In the context of using node-red, a 404 error is occasionally seen on actual usage too. Especially when editing flows and having a typo in a url somewhere it’s what will make you notice you’ve made a mistake. Is this handled somewhere, or will those connections be dropped too? Note that I’m on mobile and have only quickly scanned the fail2ban configuration. I certainly agree with blocking it, seeing a lot of interesting attempts on my CentOS cloud server with a similar fail2ban setup, but I wonder if it’s best in a node-red context that wouldn’t purely run flows in production.

You would have to get ten 404's in ten minutes to trigger a 10min ban. I've not banned myself yet. But have made the list a few times with 3 triggers now and again.

Its a per IP address. So if you have a bunch of users all behind a NAT triggering 404's then yes you might want to increase the limit or remove 404 from the list.

Also until you get a ban it will still post the 404 for the user to see they made a mistake with a malformed url or a admin noticing that its not being served.

Remove 404 banning Example:

failregex = ^<HOST>.*"(GET|POST).*" (404|444|403|400) .*$
ignoreregex =


failregex = ^<HOST>.*"(GET|POST).*" (444|403|400) .*$
ignoreregex =

I use 404 ban because people are always fishing the server URL for attack vectors and I'm tired of seeing all their failed attempts to access or
Rather they get a ban than clog up the server with requests.

Good Question @afelix

All questions/solutions welcome. I make mistakes all the time. Lets make this a great Howto TEAM

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.