Client IP is sometimes undefined in a Nodejs app hosted in Ubuntu with CloudFlare and Nginx

I have rather an unusual problem with my app. The app heavily depends on the client’s IP which is sometimes undefined . What I mean is, if there are, say 10 visits to my app in a day, I can see 7 of my clients’ IP but 3 of them returns undefined.

The app doesn’t have a problem which I can say for sure because it was working perfectly fine when it was hosted in a shared hosting with CloudFlare and without Nginx. But this issue started since I migrated to Ubuntu 20.04 VPS and with Nginx and CloudFlare.

I followed this tutorial from CloudFlare hoping that it would solve the problem but it didn’t. Now I don’t know what I should do to fix this. So here I am hoping someone would figure it out.

Here’s how I am getting the client IP in an app endpoint:

const clientIp = req.header( 'cf-connecting-ip' ) || req.header( 'X-Real-IP' );

Here’s how the nginx.conf file looks like:

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; 
    ssl_prefer_server_ciphers on;

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

    gzip on;
    
    proxy_hide_header X-Powered-By;
    add_header X-Frame-Options SAMEORIGIN;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Here’s the app config file:

server {
    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://localhost:3000;
        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 $http_cf_connecting_ip;
        proxy_set_header X-Forwarded-For $http_x_forwarded_for;
        proxy_cache_bypass $http_upgrade;
    }

    location /blog/ {
        access_log /var/log/nginx/blog-example.org_access.log;
        error_log /var/log/nginx/blog-example.org_error.log;

        root /var/www/html/example.com;
        index index.php;

        if (!-f $request_filename) {
                rewrite [^/]$ $uri/ permanent;
        }

        try_files $uri $uri/ /blog/index.php?$args;

        location ~ \.php {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_index index.php;
            fastcgi_pass  unix:/var/run/php/php7.4-fpm.sock;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $fastcgi_path_info;
            include fastcgi_params;
        }
    }

    #CLOUDFLARE
    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 104.16.0.0/13;
    set_real_ip_from 104.24.0.0/14;
    set_real_ip_from 108.162.192.0/18;
    set_real_ip_from 131.0.72.0/22;
    set_real_ip_from 141.101.64.0/18;
    set_real_ip_from 162.158.0.0/15;
    set_real_ip_from 172.64.0.0/13;
    set_real_ip_from 173.245.48.0/20;
    set_real_ip_from 188.114.96.0/20;
    set_real_ip_from 190.93.240.0/20;
    set_real_ip_from 197.234.240.0/22;
    set_real_ip_from 198.41.128.0/17;
    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;

    listen [::]:443 ssl http2 ipv6only=on; # managed by Certbot
    listen 443 ssl http2; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    # include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
}

server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80 default_server;
    listen [::]:80 default_server;

    server_name example.com www.example.com;
    return 404; # managed by Certbot
}

I don’t know if this is the reason or not but I am getting GET requests to my homepage with this: /xdebug_session_start=phpstorm .

Besides that, I have analyzed the request headers when client ip was undefined . Here are 4 request headers:

headers: {
    connection: 'upgrade',
    host: '63.250.33.76',
    'user-agent': 'Mozilla/5.0 (compatible; CensysInspect/1.1; +https://about.censys.io/)',
    accept: '*/*',
    'accept-encoding': 'gzip'
}
headers: { 
    connection: 'upgrade', 
    host: '63.250.33.76' 
}
headers: {
    connection: 'upgrade',
    host: 'www.example.com',
    'user-agent': 'Expanse indexes the network perimeters of our customers. If you have any questions or concerns, please reach out to: [email protected]'
}
headers: {
	connection: 'upgrade',
	host: 'this-is-my-own-domain.com',
	'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36',
'accept-encoding': 'gzip'
}

The fourth header’s host is one my own domains which is associated with my VPS. I am confused to how that is making a request to my app.

How can I fix this? It would help me immensely if someone could point me to the right direction.

That would suggest these requests did not come via Cloudflare but the clients connected directly to your server. Your headers samples would also confirm that as there are requests with IP addresses, which will never work in a Cloudflare context.

Just make sure that your server only accepts connections from the addresses which you already configured in your web server for the address rewrites.

Sorry to ask you this as I am a beginner in managing my own server, but can you please give me an example? Or tell me how I can achieve that?

The OP above shows all the config that I have for this app of mine. Can you check and tell me what to add and in which file?

Thanks in advance.

I am afraid server administration is beyond the scope of the forum and better discussed at StackExchange or Reddit for example, but you would essentially configure your network or system firewall to only accept aforementioned IP addresses. Alternatively you can also require Cloudflare to use client certificate authentication. https://developers.cloudflare.com/ssl/origin-configuration/authenticated-origin-pull has all on that.

Thank you so much.

@sandro Hey I had another question. Do I need SSL from ‘Let’s Encrypt’ if I setup which you suggested?

Yes, you always need a valid certificate on your server. You can also check out Cloudflare’s own Origin certificates, but your server always needs to be properly configured.

The authentication I refered to is completely unrelated to that.

Thanks so much mate.

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