DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 966,904 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Arseny Zinchenko
Arseny Zinchenko

Posted on • Originally published at rtfm.co.ua on

NGINX: IP Geolocation by Cloudflare and β€œnested” if conditions

Among other features provided by Cloudflare, it can add a special header with a country value, from where a visitor came.

As a Ukrainian, I’d like to ban all visitors from russia, but:

  1. redirect all visitors from Russian IPs to another web domainβ€Šβ€”β€Šrusski-voenny-korabl-idi-nahuy.com
  2. during this, I’d like to filter requests and leave requests from the russian Yandex search system unimpacted, so it will proceed to return my RTFM blog in its search results for russian auditory.

Cloudflare IP Geolocation and NGINX

The simplest part here is to configure the filter by the header from Cloudflare.

At first, enable the CF-IPCountry header.

Go to your Cloudflare Dashboard > Network, activate the option, it’s free even in its basic plan:

Then, in your virtualhost config for NGINX, in the server {} block use the proxy_set_header to create a variable `$http_cf_ipcountry:


...
proxy_set_header CF-IPCountry $http_cf_ipcountry;
...

NGINX and β€œnested” if

And the second part is more interesting.

So, we need to check two conditions:

  1. is a visitor from russia
  2. is it a common visitor, or a Yandex search bot?

In NGINX we can not use really nested conditions with if, e.g:


...
if ($country_allowed = no) {
if ($http_user_agent !~* (YandexBot) ) {
rewrite ^ [https://russki-voenny-korabl-idi-nahuy.com](https://russki-voenny-korabl-idi-nahuy.com) break;
}
}
...

But what we can do here, is to use variables and dedicated if.

Let’s check.

Enable a VPN, and check where are you now, for example using the https://www.iplocation.net service:

GBβ€Šβ€”β€ŠGreat Britain.

In the nginx.conf you can also enable an extended log format for access_log to see more information, and check the values of the $http_cf_ipcountry and a new variable $visitor that we will add below:


...
log_format main_ext '$time_local client: $remote_addr fwd_for: $http_x_forwarded_for '
'status: $status user_agent: "$http_user_agent" '
'server: "$server_name" country: $http_cf_ipcountry visitor: $visitor';
...

Next, create conditions and run tests. After tests will be successful, we will change conditions to region RU and β€œif not equal” Yandex (see Yandex robots in server logs).

So:

  1. create a variable called $http_cf_ipcountry from the CF-IPCountry header;
  2. in the first ifcondition check the value of the $http_cf_ipcountry: for now, check for the GB and create a variable $visitor that will keep a value rus, if the region was == CA;
  3. in the second if condition check the User-Agent, and if it's == Firefox then add another value to the $visitor variable - "redirect;
  4. in the third if condition checks the value of the $visitor variable, and if it is equal to the rusredirect string, then make a rewrite to the russki-voenny-korabl-idi-nahuy.com domain;

Also, you can set the rewrite_log parameter to on, and set the error_log to the notice level, so you'll be able to check if rewrites are working as expected.

This is how my config looked during the tests:

`
...
rewrite_log on;
access_log /var/log/nginx/rtfm.co.ua-access-ext.log main_ext;
error_log /var/log/nginx/rtfm.co.ua-error.log notice;
...
proxy_set_header CF-IPCountry $http_cf_ipcountry;

if ($http_cf_ipcountry = GB) {
    set $visitor rus;
}

if ($http_user_agent ~* (Firefox) ) {
    set $visitor "${visitor}redirect";
}

if ($visitor = "rusredirect") {
    rewrite ^ [https://russki-voenny-korabl-idi-nahuy.com](https://russki-voenny-korabl-idi-nahuy.com) break;
}
Enter fullscreen mode Exit fullscreen mode

...
`

Check the config file and reload nginx:


root@rtfm-do-production-d10:~# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
root@rtfm-do-production-d10:~# systemctl reload nginx

Go to the blog with the Firefox browser:

access_log:


root@rtfm-do-production-d10:~# tail -f /var/log/nginx/rtfm.co.ua-access-ext.log | grep GB
02/Apr/2022:06:22:29 +0000 client: 162.158.91.190 fwd_for: 146.70.46.20,146.70.46.20 status: 302 user_agent: β€œMozilla/5.0 (X11; Linux x86_64; rv:98.0) Gecko/20100101 Firefox/98.0” server: β€œrtfm.co.ua” country: GB visitor: rusredirect

error_log:


root@rtfm-do-production-d10:~# tail -f /var/log/nginx/rtfm.co.ua-error.log | grep β€˜rewritten redirect’
2022/04/02 06:22:29 [notice] 21018#21018: *33766595 rewritten redirect: β€œhttps://russki-voenny-korabl-idi-nahuy.com", client: 162.158.91.190, server: rtfm.co.ua, request: β€œGET / HTTP/1.1”, host: β€œrtfm.co.ua”

Got a visitor from the country: GB with the user_agent: "Firefox", and got a redirect: "https://russki-voenny-korabl-idi-nahuy.com" - "It works!" (c).

Now, change the conditions to the final version with the RU region and Yandex for User-Agent:

  • set the region: if ($http_cf_ipcountry = RU)
  • and change the ~* (Firefox) to the not-equal, i.e. !~* (Yandex) - if ($http_user_agent !~* (Yandex) )`:
...
    proxy_set_header CF-IPCountry $http_cf_ipcountry;

    if ($http_cf_ipcountry = RU) {
        set $visitor rus;
    }

    if ($http_user_agent !~* (Yandex) ) {
        set $visitor "${visitor}redirect";
    }

    if ($visitor = "rusredirect") {
        rewrite_log on;
        rewrite ^ [https://russki-voenny-korabl-idi-nahuy.com](https://russki-voenny-korabl-idi-nahuy.com) break;
    }
...
Enter fullscreen mode Exit fullscreen mode

Done.

Originally published at RTFM: Linux, DevOps, and system administration.


Latest comments (0)

About Real-time

Join DEV and MongoDB to build a front-end application using MongoDB Atlas. Change streams to display live updates as your database changes for your entry in the DEV x MongoDB Atlas Hackathon 2022.

β†’ Join the Hackathon