Understanding Fly client IPs for IP blocking in Nginx reverse proxy

I have an Nginx reverse proxy on Fly, directing traffic to another Fly app.

I’m getting hit by a spam attack. I have been able to block by user-agent, but I need to be able to block IP addresses.

Everything I’ve read says I should be able to use nginx deny directives and a blocklist.

However, when I try this by attempting to block my own IP from hitting the specific endpoint, the IP deny rule never matches. I know the regex is matching, because deny all; does return 403. And I know I have the IP address right, because I can see my IPV4 as the leftmost value in X-Forwarded-For in my logs.

It seems I don’t understand something about how Fly + Nginx handles actual visitor IPs, and how to match them against deny rules. Any insight would be greatly appreciated!

This is the relevant part of my nginx.conf

http {
    # ... snip
    set_real_ip_from 172.16.0.0/12;  
    # I have also tried:
    # set_real_ip_from 172.17.0.0/16; 
    # and:
    # set_real_ip_from 0.0.0.0/0; 
    # and:
    # set_real_ip_from MY-APP-IPV6;
    # set_real_ip_from MY-APP-IPV4;    
    real_ip_header X-Forwarded-For; # also tried: Fly-Client-IP
    real_ip_recursive on;
    # ... snip
    server {
        location ~* members/api/send-magic-link { # have tried this in, and beside, main location / { } block
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Fly-Client-IP $remote_addr;

            deny XXX.XX.XX.XXX; # this never matches, even when I can see this IP in X-Forwarded-For in my logs
            allow all;
            # deny all; # this works
            # return 500; # this also works
            # proxy_pass $origin_url;
    }
    # ... snip
}

Hi… I’ve read that, too, so I tried it out in Debian Trixie, after years of not having touched Nginx (caveat emptor)…

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  root /var/www/html;

  index index.html index.htm index.nginx-debian.html;

  server_name _;

  set_real_ip_from 0.0.0.0/0;
  real_ip_header Fly-Client-IP;

  location / {
    try_files $uri $uri/ =404;
    deny 73.254.47.5;
  }
}

The actual client IP shows up in access.log, and it does block the banned address as intended (403 Forbidden).

[This was just a tweak of the pre-installed /etc/nginx/available-sites/default file, adding 2 real_ip directives and then deny.]

Perhaps you have Cloudflare or similar in front? That’s known to break this technique, :dragon:


Aside: You can also use nginx -V to verify that the realip module is available. I don’t think it’s compiled in by default.

1 Like

@mayailurus Aha! Thank you for your helpful reply. Given that the other directives are (I think) things I have tried, I suspect that the module is missing.

I checked that deny IP works by default, but did not verify whether the Docker image supports real_ip out of the box (I assumed it would warn if it didn’t).

I will look into this.

1 Like

Hmm, –with-http_realip_module is listed in configure arguments:, so maybe that’s not the problem.

I see your working example doesn’t have recursive mode on, I’ll try that next.

Do you know anything about the security of using 0.0.0.0./0, for IP spoofing?

I’m not behind Cloudflare (the whole reason I’m running this reverse proxy is so I don’t have to use Cloudflare :).

Aha! @mayailurus you are officially my hero.

I’m pretty sure it was the recursive part that was breaking it.

I made three changes:

  • Switched back to 0.0.0.0/0

  • Switched back to Fly-Client-IP (which I’d tried before)

  • Removed real_ip_recursive on

  • (I also removed proxy_set_header directives for Fly-Client-IP etc, since your example didn’t have them)

Now it works!

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