Why your logs suddenly show 192.168.255.x — and how to fix it properly

When everything sits behind Cloudflare, real-IP handling in Nginx is usually straightforward, trust Cloudflare’s IP ranges, read CF-Connecting-IP, and $remote_addr becomes the actual visitor’s address.

That simplicity disappears the moment you introduce a Linode NodeBalancer in front of your server.

Suddenly your logs show something like:

No visitor IP. No geolocation, No rate-limit accuracy, No security context.

This article breaks down why this happens and how to build a clean, universal, future-proof Nginx real-IP setup that works for

  • Sites behind Cloudflare only
  • Sites behind Cloudflare + Linode NodeBalancer
  • Mixed environments with many domains and applications

This is the configuration I now use across my infrastructure — simple, robust, and consistent.

The Real Cause, One Missing Trusted Hop

Let’s look at what changes when you add NodeBalancer.

Before

Nginx sees

  • Source IP > Cloudflare edge
  • Visitor IP > inside header CF-Connecting-IP

Everything works After

Nginx now sees:

  • Source IP: 192.168.255.x (private LB network)
  • Cloudflare header exists, but is ignored

Why?

Because Nginx will only trust forwarded IP headers when the source is listed in set_real_ip_from.

Cloudflare is trusted, but the NodeBalancer private network isn’t, so Nginx throws away the real IP and uses the LB’s address instead.

The fix is shockingly simple, but must be done properly.

The Correct Strategy, Centralize the Trust Logic

Instead of putting real-IP logic inside each site config, it’s far cleaner to:

  • Place all trusted proxies (Cloudflare + NodeBalancer) in a single global config
  • Extract visitor IP using only one directive, globally
  • Keep per-site configs minimal and focused on proxying upstream

This will ensures

  • All domains behave consistently
  • Adding new sites requires zero IP logic
  • Changes to Cloudflare/Linode ranges only happen in one place
  • Backends always receive the correct visitor IP

Step 1, Create a Global Real-IP File

/etc/nginx/conf.d/real-ip.conf

This becomes your single source of truth.

You never repeat these lines in any site config again.

Step 2, Make Sure Nginx Loads It Globally

Your /etc/nginx/nginx.conf should have

Still inside http {}, add a readable access log format so you can verify the result

This makes debugging clear and transparent.

Step 3, Keep Per-Site Configs Clean and Minimal

Your site config should not contain any real-IP directives anymore.

A typical TLS vhost becomes

Simple, Deterministic and Zero duplication.

Step 4, If You Use NodeBalancer TCP Mode + Proxy Protocol

If you enable “TCP” + “Proxy Protocol v2” on Linode LB, update your listen directive:

But do not change the global real-IP logic.

You still want the visitor IP from

Proxy Protocol is only useful if you want the Cloudflare edge IP, which is rarely needed for apps.

Step 5, Test the Results

Reload

Then watch the logs:

You should now see:

  • $remote_addr = real visitor IP
  • $http_cf_connecting_ip = same IP
  • No more 192.168.255.x

This applies consistently across all domains, whether they use the LB or not.

Final Thoughts

Handling real IPs becomes messy only when multiple proxy layers enter the picture. The mistake most people make is scattering real-IP directives across different site configs.

That works until the first load balancer enters the system, then logs break, security breaks, and analytics break.

By centralizing trust and extracting the real client IP once, you build an Nginx setup that

  • Works cleanly with Cloudflare, Linode NodeBalancer, and with both combined
  • Avoids duplicated logic, keeps every domain consistent and makes future expansion simple

This unified approach keeps your infrastructure predictable and your logs honest, just the way it should be.

Leave A Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.