Tutorial : NodeBB Scaling-clustering Redis + Proxied Cloudflare Free plan : integration guide
-
Also posted here : https://community.nodebb.org/topic/19292/nodebb-scaling-clustering-redis-proxied-cloudflare-free-plan-integration-guide
Hello,
This guide covers a modern setup (2026+) running NodeBB in cluster mode with Redis behind nginx and Cloudflare’s free plan in proxied mode. The documentation here is over 10 years old and don’t account for cluster mode, Redis pub/sub, or how Cloudflare’s proxied mode interacts with
socket.iosession stickiness. This guide fills that gap.Also, the official NodeBB nginx documentation will cause silent
socket.iofailures without the fixes described here.
– See here : https://community.nodebb.org/topic/19225/websocket-socket.io-403-xhr-poll-error-behind-cloudflare-cluster-scaling-redis/5Prerequisites:
- NodeBB multi-process cluster with Redis pub/sub running, nginx configured as reverse proxy.
– See official documentation here : https://docs.nodebb.org/configuring/scaling/ - Cloudflare in proxied mode (orange cloud) pointing to your server IP.
- A valid SSL certificate installed on your server (covered in step 1 below)
Why the official NodeBB nginx doc breaks with Cloudflare
The official docs recommend
ip_hashfor session stickiness. This silently breaks behind Cloudflare because Cloudflare uses multiple outgoing IPs for the same end user. Thesocket.iopolling request and the WebSocket upgrade can arrive from two different CF IPs, routing them to different cluster nodes. The second node has no session →400 Bad Request.
Step 1 : SSL: Let’s Encrypt certificate + Cloudflare Full (Strict) mode
Why Full (Strict) and not just Full or Flexible?
Cloudflare offers four SSL modes:
Mode Visitor → CF CF → your server Risk Off HTTP HTTP Everything in plaintext Flexible HTTPS HTTP CF to server is unencrypted — never use this Full HTTPS HTTPS Accepts any cert, including self-signed — not validated Full (Strict) HTTPS HTTPS Requires a valid, trusted cert — recommended Full (Strict) is the only mode that validates the certificate on your server. Without it, Cloudflare will happily connect to your server over HTTPS with an expired or self-signed cert, which provides no real security for the CF → origin leg.
Install Certbot and obtain a certificate
# Install Certbot with the nginx plugin sudo apt install certbot python3-certbot-nginx -y # Obtain a certificate for your domain # (nginx must already be running and port 80 reachable from the internet) sudo certbot --nginx -d your-domain.com -d www.your-domain.comCertbot will automatically edit your nginx vhost to add the SSL configuration and set up an HTTP → HTTPS redirect.
If Cloudflare is already in proxied mode (orange cloud), Certbot’s HTTP-01 challenge still works because Cloudflare forwards HTTP traffic to your server. No need to temporarily disable the proxy.
Verify auto-renewal
# Test the renewal process (dry run — no cert is actually renewed) sudo certbot renew --dry-runCertbot installs a systemd timer automatically. Certificates are renewed 30 days before expiry.
Configure Cloudflare SSL mode
In your Cloudflare dashboard, go to SSL/TLS → Overview and select Full (Strict).
Also enable these recommended options under SSL/TLS → Edge Certificates:
- Always Use HTTPS → On
- Minimum TLS Version → TLS 1.2
- Opportunistic Encryption → On
- TLS 1.3 → On
- Automatic HTTPS rewrites → On
Update your nginx vhost to listen on 443
After Certbot runs, your vhost should contain (Certbot adds this automatically):
server { listen 443 ssl; server_name your-domain.com; ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # ... rest of your NodeBB proxy config } server { listen 80; server_name your-domain.com; return 301 https://$host$request_uri; }With Cloudflare in proxied mode, the redirect from port 80 to 443 in nginx is technically handled by Cloudflare before it reaches your server (via the “Always Use HTTPS” rule above). Keep it in nginx anyway as a safety net for direct server access.
Step 2 : Nginx: trust Cloudflare’s real IP headers
By default nginx sees Cloudflare’s edge IPs, not your visitors’ IPs. You need to tell nginx which IP ranges to trust as a proxy, so
$http_x_forwarded_forresolves correctly for the upstream hash in step 3.Add this in you nodebb nginx virtualhost configuration :
# Cloudflare IPv4 ranges (update periodically from https://www.cloudflare.com/ips-v4) set_real_ip_from 173.245.48.0/20; set_real_ip_from 103.21.244.0/22; set_real_ip_from 103.22.200.0/22; set_real_ip_from 103.31.4.0/22; set_real_ip_from 141.101.64.0/18; set_real_ip_from 108.162.192.0/18; set_real_ip_from 190.93.240.0/20; set_real_ip_from 188.114.96.0/20; set_real_ip_from 197.234.240.0/22; set_real_ip_from 198.41.128.0/17; set_real_ip_from 162.158.0.0/15; set_real_ip_from 104.16.0.0/13; set_real_ip_from 104.24.0.0/14; set_real_ip_from 172.64.0.0/13; set_real_ip_from 131.0.72.0/22; # Cloudflare IPv6 ranges set_real_ip_from 2400:cb00::/32; set_real_ip_from 2606:4700::/32; set_real_ip_from 2803:f800::/32; set_real_ip_from 2405:b500::/32; set_real_ip_from 2405:8100::/32; set_real_ip_from 2a06:98c0::/29; set_real_ip_from 2c0f:f248::/32; real_ip_header CF-Connecting-IP;Using
CF-Connecting-IPinstead ofX-Forwarded-Foris safer - Cloudflare guarantees this header contains exactly the real visitor IP with no spoofing risk.
Step 3 : Nginx: fix upstream session stickiness
The problem:
User → CF IP 1 → ip_hash → :4567 ✓ (session created) User → CF IP 2 → ip_hash → :4568 ✗ (400 — no session!)The fix:
User real IP → consistent hash → always :4567 ✓In your NodeBB nginx vhost, update the upstream block like this :
upstream io_nodes { # BEFORE (breaks with Cloudflare): # ip_hash; # AFTER — hash on the real visitor IP forwarded by Cloudflare hash $http_x_forwarded_for consistent; server 127.0.0.1:4567; server 127.0.0.1:4568; server 127.0.0.1:4569; }Make sure WebSocket proxying is present in your
location /socket.io/block:location /socket.io/ { proxy_pass http://io_nodes/socket.io/; proxy_redirect off; 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; proxy_set_header X-Forwarded-Proto $scheme; }
Step 4 : NodeBB config.json: origins & trust proxy
Two settings are required: tell NodeBB to trust the proxy headers, and restrict
socket.ioto your domain to prevent 403 origin errors.{ "url": "https://your-domain.com", "socket.io": { "origins": "https://your-domain.com:*" }, "trust proxy": true, "secret": "...", "database": "mongo", "port": [4567, 4568, 4569], "redis": { } }Without
"trust proxy": true, NodeBB ignoresX-Forwarded-Protoand may treat requests as HTTP even when the user is on HTTPS, causing CSRF and cookie issues behind Cloudflare.
Step 5 : Cloudflare: WAF rules (bypass
socket.io& API)Cloudflare’s WAF can interfere with
socket.io'slong-polling and your API endpoints. Create a WAF skip rule to bypass managed rules for these paths.Go to Security → WAF → Custom rules and create a rule with this expression:
(http.request.uri.path contains "/socket.io") or (http.request.uri.path contains "/api/")Set the action to Skip → All remaining custom rules (and optionally also skip managed rulesets). Name it something like
NodeBB - bypasssocket.ioand API.On the free Cloudflare plan you get 5 custom WAF rules.
This single rule covers both
socket.ioand API.
Step 6 : Cloudflare: Cache rules (bypass
socket.io)Cloudflare must never cache
socket.iotraffic. Go to Caching → Cache Rules and create a rule with this expression:(http.request.uri.path contains "/socket.io/") or (http.request.uri.path contains "/api/")Set the cache status to Bypass.
Optional: add a second cache rule to Cache Everything for
/assets/and/uploads/paths to offload static files to Cloudflare’s CDN and reduce load on your server.
Step 7 : Reload & verify
# Test nginx config first sudo nginx -t # If OK, reload (zero downtime) sudo systemctl reload nginx # Restart NodeBB cluster cd /path/to/nodebb ./nodebb restartOpen your forum in a browser, open DevTools → Network, filter by
socket.io. You should see:GET /socket.io/?transport=polling → 200 GET /socket.io/?transport=websocket → 101 Switching ProtocolsAll
socket.io400errors should be gone. If you still see occasional 400s during a node restart, they are expected and transient -socket.iowill reconnect automatically.
Summary checklist
What Where Scaling-Clustering Redis server Let’s Encrypt certificate via Certbot server SSL mode set to Full (Strict) Cloudflare dashboard Always Use HTTPS + TLS 1.2 minimum Cloudflare dashboard Nginx real IP from Cloudflare ranges /etc/nginx/conf.d/cloudflare-realip.confUpstream: ip_hash→hash $http_x_forwarded_for consistentnginx vhost socket.ioorigins +trust proxyin config.jsonNodeBB WAF skip rule for /socket.ioand/api/Cloudflare dashboard Cache bypass rule for /socket.ioand/api/Cloudflare dashboard - NodeBB multi-process cluster with Redis pub/sub running, nginx configured as reverse proxy.
-
undefined DownPW referenced this topic
-
undefined DownPW marked this topic as a regular topic
-
@downpw this is a great walk through which I’m certain others will find extremely useful.
Thanks
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register LoginRelated Topics
-
-
-
Smart Widgets
Solved Configure -
NodeBB upgrade now cant post
Solved Bugs -
-
-
NodeBB 1.19.3
Solved Performance -