Protect Node Red from anywhere

Node-RED Security Variants Discussed and Tested

Context

The goal is to protect a Node-RED installation exposed through a router, while keeping the setup simple and understandable.

The following four variants were discussed and tested in stages.

Common assumptions:

  • Node-RED runs on port 1880.

  • Node-RED admin authentication is enabled.

  • Nginx is used as a reverse proxy.

  • Nginx access logs are enabled.

  • Failed Node-RED editor logins appear as:

    POST /auth/token ... 403

  • Fail2ban monitors the Nginx access log.

  • The Fail2ban filter used is:

[Definition]
failregex = ^ .* "POST /auth/token HTTP/." 403 .
ignoreregex =

================================================
VARIANT 1
HTTP + Nginx + Fail2ban Full IP Ban

Architecture:

Internet / LAN
|
v
Nginx :80
|
v
Node-RED :1880

Fail2ban watches:

POST /auth/token ... 403

Jail configuration:

[nodered]
enabled = true
filter = nodered
backend = polling

action = route

logpath = /var/log/nginx/access.log

maxretry = 5
findtime = 600
bantime = 300

Meaning:

  • 5 failed login attempts
  • within 10 minutes
  • full source IP ban
  • ban duration: 5 minutes

The important line is:

action = route

This blocks the entire source IP address, not only Node-RED.

As a result, the banned IP loses access to:

  • Nginx
  • Node-RED
  • SSH
  • other services on the same host

Advantages:

  • Very simple.
  • Easy to understand.
  • Strong response to repeated failed logins.
  • No nftables multiport configuration is required.

Disadvantage:

  • The administrator can accidentally ban their own IP and lose SSH access.

This variant was tested successfully.

================================================
VARIANT 2
HTTP + Nginx + Fail2ban Ban Only Port 80

Architecture:

Internet / LAN
|
v
Nginx :80
|
v
Node-RED :1880

Fail2ban still watches:

POST /auth/token ... 403

However, instead of blocking the entire source IP, the ban is applied only to TCP port 80.

nftables is required.

Example action:

action = nftables[type=multiport, port="80", protocol="tcp"]

Result:

  • HTTP access through Nginx port 80 is blocked.
  • SSH port 22 remains accessible.
  • Direct Node-RED port 1880 remains accessible if Node-RED is still listening on all interfaces.

Advantages:

  • Safer for remote administration.
  • Accidental login failures do not lock the administrator out of SSH.

Disadvantages:

  • More complex than Variant 1.
  • Direct port 1880 must still be closed separately.

This variant was also tested successfully.

================================================
VARIANT 3
HTTPS + Nginx + Fail2ban Ban Only Port 443

Architecture:

Internet
|
v
Nginx HTTPS :443
|
v
Node-RED :1880

A self-signed certificate is used.

Example Nginx configuration:

server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;

ssl_certificate /etc/nginx/ssl/nodered.crt;
ssl_certificate_key /etc/nginx/ssl/nodered.key;

access_log /var/log/nginx/access.log;

location / {
    proxy_pass http://127.0.0.1:1880;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

}

Fail2ban action:

action = nftables[type=multiport, port="443", protocol="tcp"]

Example jail:

[nodered]
enabled = true
filter = nodered
backend = polling

port = 443
protocol = tcp

action = nftables[type=multiport, port="443", protocol="tcp"]

logpath = /var/log/nginx/access.log

maxretry = 5
findtime = 600
bantime = 600

Meaning:

  • 5 failed login attempts
  • within 10 minutes
  • only HTTPS port 443 is blocked
  • ban duration: 10 minutes

Result:

  • HTTPS access is blocked for the attacker.
  • SSH remains accessible.
  • Node-RED direct port 1880 remains accessible if not separately restricted.

Advantages:

  • Encrypted connection.
  • Fail2ban protects only the public web entry point.
  • SSH remains available.

Disadvantages:

  • More configuration.
  • Self-signed certificates generate browser warnings.
  • Port 1880 still needs separate protection.

This variant was tested successfully.

================================================
VARIANT 4
HTTPS + Nginx + Fail2ban + Node-RED Bound to Localhost

This is the hardened version of Variant 3.

Architecture:

Internet
|
v
Nginx HTTPS :443
|
v
127.0.0.1:1880
|
v
Node-RED

Node-RED is changed from:

0.0.0.0:1880

to:

127.0.0.1:1880

In Node-RED settings.js:

uiHost: "127.0.0.1",

Example file:

/mnt/dietpi_userdata/node-red/settings.js

After restart:

sudo systemctl restart node-red

Verification:

sudo ss -ltnp | grep -E '1880|443'

Expected result:

127.0.0.1:1880
0.0.0.0:443
[::]:443

Effect:

  • Node-RED port 1880 is no longer reachable directly from LAN or WAN.

  • Only local processes on the same machine can access Node-RED on port 1880.

  • Nginx can still proxy requests to:

    http://127.0.0.1:1880

  • All external access must go through Nginx.

Advantages:

  • Direct access to Node-RED port 1880 is completely removed.
  • Nginx becomes the single public web entry point.
  • Fail2ban protection cannot be bypassed by connecting directly to port 1880.
  • This is the strongest architecture of the four variants.

Disadvantage:

  • Slightly more configuration than Variant 1.

================================================
Summary

Variant 1:
HTTP :80
Nginx
Node-RED :1880
Fail2ban full IP ban
action = route

Variant 2:
HTTP :80
Nginx
Node-RED :1880
Fail2ban ban only port 80
nftables multiport

Variant 3:
HTTPS :443
Nginx
Node-RED :1880
Fail2ban ban only port 443
nftables multiport

Variant 4:
HTTPS :443
Nginx
Node-RED only on 127.0.0.1:1880
Fail2ban on the public Nginx entry point

Recommended progression:

  1. Start with Variant 1 because it is simple and easy to verify.
  2. Add localhost binding for Node-RED port 1880.
  3. Add HTTPS if encrypted remote access is required.
  4. Use port-specific nftables bans if preserving SSH access during a web ban is important.

================================================
Additional Observation

The Node-RED editor login can be detected reliably in Nginx logs by:

POST /auth/token ... 403

However, other Node-RED interfaces such as /ui may use different authentication mechanisms.

For example, failed access to /ui may appear as:

GET /ui ... 401

Therefore, a separate Fail2ban rule may be required if the dashboard/user interface must also be protected against repeated authentication attempts.

Wouldn’t it be easier to just use Cloudflare Access (zero trust network access)? It’s free for up to 50 users.

Not everyone wants or can use a 3rd party cloud service. It is helpful to have information on how to configure a secure proxy.

This is the part I would say is weaker. It is generally better to use an integration with NGINX to provide authentication, and also authorisation. Rather than trying to rely on Node-RED to do everything since authentication is not really a core feature of Node-RED. NGINX authentication integrations will generally have been far more extensively battle-tested.

This is certainly not so good. You should NEVER be exposing port 80 HTTP. In particular, lack of HTTPS/TLS encryption means that any login can be easily compromised.

Always redirect port 80 (http:) to port 443 (https:) and include TLS enhanced security settings in your NGINX configuration. TLS is only useful when carefully configured.