NGINX: IP Geolocation от Cloudflare и вложенные if

Автор: | 02/04/2022

Среди прочих плюшек, которые предоставляет Cloudflare, есть возможность передачи заголовка с указанием страны, из которой пришёл посетитель.

После долгих и безуспешных попыток как-то адекватно настроить GeoIP v2 для NGINX, IP Geolocation от Cloudflare стал просто подарком.

Собственно, что хочется сделать:

  1. перенаправлять всех посетителей с российских IP на другой домен
  2. при этом фильтровать запросы от Yandex, и ему отдавать содержимое этого блога, что бы посетители из россии продолжали видеть этот блог в выдаче поисковика

Cloudflare IP Geolocation и NGINX

Самая простая часть – это настроить фильтрацию по заголовку, который передаёт Cloudflare.

Для начала, включаем передачу самого заголовка CF-IPCountry.

Переходим в Cloudflare Dashboard > Network, активируем опцию – она бесплатна даже в базовом пакете:

Далее, в файле настроек виртуалхоста в блоке server {} используем заголовок через proxy_set_header:

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

NGINX и “вложенные” if

А далее задача поинтереснее.

Нам надо проверить два условия:

  1. пришёл ли посетитель из россии
  2. пришёл ли простой посетитель – или бот Яндекса?

В NGINX мы не можем использовать вложенные if, т.е. нельзя использовать конструкцию типа:

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

Но что мы можем сделать – так это использовать переменные и несколько if.

Включаем VPN, проверяем в какую локацию нас занесло, например через https://www.iplocation.net:

GB – Great Britain.

В nginx.conf можно добавить расширенный формат access_log:

...
    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';
...

Переходим к формированию условий для редиректа и тестированию. Когда всё проверим – изменим условия на RU и “если не равно” Yandex (см. Yandex robots in server logs).

Итак:

  1. создаём переменную $http_cf_ipcountry из заголовка CF-IPCountry;
  2. в первом условии if проверяем $http_cf_ipcountry: пока используем регион GB; создадим переменную $visitor, в которую занесём значение rus, если регион == GB;
  3. во втором условии if – проверяем User-Agent, и если он == Firefox – то добавляем к значению переменной $visitor значение “redirect;
  4. в третьем if проверяем значение $visitor, и есло оно равно rusredirect, то выполняем rewrite на домен russki-voenny-korabl-idi-nahuy.com;

Кроме того, можно включить опцию rewrite_log в on, и error_log установить на уровень notice, что бы увидеть срабатывание rewrite.

Получается:

...
    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 break;
    }
...

Проверяем корректность конфига, перезагружаем конфиги nginx:

[simterm]

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

[/simterm]

И заходим на сам блог:

access_log:

[simterm]

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

[/simterm]

error_log:

[simterm]

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"

[/simterm]

Посетитель пришёл из country: GB с user_agent: "Firefox", и получил redirect: "https://russki-voenny-korabl-idi-nahuy.com" – всё работает.

Меняем условия:

  • меняем регион – if ($http_cf_ipcountry = RU)
  • и меняем ~* (Firefox) на не-равно, т.е. !~* (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 break;
    }
...

Готово.