Pull to refresh

Геотаргетинг nginx, частный случай

Website development *Drupal *
Sandbox
Возникла задача сделать геотаргетинг для регионов России на новостном сайте, т.е. при заходе на главную страницу, должно происходить перенаправление на региональную страницу сайта с адресами вида: region/[номер региона], причем перенаправление клиента должно осуществляться на nginx-е без передачи данных на апач, в противном случае это лишняя ненужная нагрузка на сервер.

Средняя посещаемость ресурса 40т в сутки. Drupal, кеширующий модуль boost, создающий статичные страницы которые выдает nginx.

Поиск решения в google предлагал варианты js перенаправление на стороне клиента, либо передачу данных на апач, запрос базы данных для получения нужного урла, что изначально не устраивало.

Рассмотрев доступные базы ip адресов: www.wipmania.com/ru/base, www.maxmind.com/en/home, ipgeobase.ru, возникла «гениальная идея», что если бы в базе были необходимые нам урлы [номер региона], то счастье было бы полным.

Исходя из этого и условий, что геотаргетинг делается только для регионов России, остановился на модуле nginx geo и базе адресов от ipgeobase, так как модуль geo может принимать текстовый файл в качестве базы адресов, ну а база от ipgeobase распространяется в текстовом формате. Осталось, собственно, привести базу адресов в нужный формат…

Итак:
Отсюда ipgeobase.ru/files/db/Main/geo_files.tar.gz скачиваем базу данных, получаем архив, распаковываем и получаем 2 файла cidr_optim.txt и cities.txt.
cidr_optim.txt имеет следующий формат записи:
<начало блока> <конец блока> <блок адресов> <страна> <идентификатор города>

<начало блока> — число, полученное из первого ip адреса блока (диапазона) ip-адресов вида a.b.c.d по формуле a*256*256*256+b*256*256+c*256+d
<конец блока> — число, полученное из второго ip адреса блока (диапазона) ip-адресов вида e.f.g.h по формуле e*256*256*256+f*256*256+g*256+h
<блок адресов> — блок (диапазон) ip-адресов вида a.b.c.d — e.f.g.h, для кторого определено положение
<страна> — двухбуквенный код страны, к которой относится блок
<идентификатор города> — идентификатор города из файла cities.txt. Если вместо идентификатора стоит прочерк, значит, либо город не удалось определить, либо страна блока не Россия и не Украина.

cities.txt имеет следующий формат записи:
<идентификатор города> <название города> <название региона> <название округа> <широта центра города> <долгота центра города>
Описание файлов находится здесь ( ipgeobase.ru/Help.html#35 )

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

«блок (диапазон) ip-адресов вида a.b.c.d — e.f.g.h» -> (необходимый урл) [номер региона]

Дело осталось за малым — перевести формат:
«блок (диапазон) ip-адресов вида a.b.c.d — e.f.g.h» в формат понимаемый модулем geo:
0.0.0.0/0 (начальный адрес/битовая маска).

Тут, как ни странно, началось самое веселое. Опросил все знакомых админов, все дружно сказали что проходили то как перевести диапазон в нужный мне формат, но, за ненадобностью, все давно забыли, а вспоминать некогда. Google, который всегда нам в помощь, предлагал либо инструкции по расчету диапазона по адресу и маске, либо досконально изучить принцип формирования сетей ip4.
Для решения задачи выбрал 3-й вариант. В сети нашел ip-calculator.ru, связался с администратором домена, который любезно согласился помочь с переводом и разъяснением принципа перевода адресов в нужный формат. (еще раз спасибо).

В итоге получился файл формата «0.0.0.0/0 (необходимый урл);» c 57 тысячами строк, назовем его, скажем, geo_ru.conf.

Теперь, собственно, nginx:
в блоке http{} включаем модуль
geo $region_number {
	    default        all;
	    include        [адрес где лежит файлик]/geo_ru.conf
}

т.е. после обращения, в случае нахождения адреса клиента в файле, в переменной $region будет находиться соответствующее значение, а именно [номер региона], в противном случае 'all'. (подробнее: nginx.org/ru/docs/http/ngx_http_geo_module.html )
Далее, собственно, редирект:
в блоке server{} сайта
# установили переменную $get_redirect со значением donot_redirect
set $get_redirect donot_redirect; 
# в случае, если клиент заходит на главную страницу присваеваем переменной значение do_redirect                             
if ($uri = '/') {                                               
    set $get_redirect do_redirect;                              
} 
# если nginx не нашел адреса клиента в базе и в переменной $region_number значение 'all' , то и редиректить незачем                                                           
if ($city = 'all') {                                            
    set $get_redirect donot_redirect;                           
}  
# если уже есть кука, т.е. клиент уже заходил к нам и мы его редиректили на его регион (должна же быть возможность смотреть главную страницу)                                                             
if ($cookie_geolocate = 1) {                                    
    set $get_redirect donot_redirect;                           
} 
# ну и собственно сам редирект на нужную страницу                                                              
if ($get_redirect = do_redirect) {                                          
    rewrite ^(.*)$ http://example.com/region/$region_number redirect;    
}    

(т.е. в итоге мы получили то что хотели — переход на region/[номер региона])

Ну и последнее — чтобы клиент мог все-таки посмотреть главную страницу, в блоке
location / {} отправляем клиенту куку:
add_header Set-Cookie "geolocate=1;Path=/;Domain=.example.com;";  


Вот, собственно, и все. Надеюсь кому-нибудь поможет.
Проверить в работе можно на fedpress.ru.
Поводом для написания статьи стало то что решение, вроде бы очевидное, появилось не сразу. Буду рад комментариям, советам, уточнениям.

PS Уважаемые админы, присутствующие на хабре, напишите, пожалуйста, статью с пошаговым руководством «для чайников» о том что такое ip адреса, как расчитывать маску по диапазону и наоборот, зачем нужны <начало блока> (a*256*256*256+b*256*256+c*256+d) и <конец блока> (e*256*256*256+f*256*256+g*256+h), думаю многие были бы благодарны.
Tags:
Hubs:
Total votes 28: ↑23 and ↓5 +18
Views 15K
Comments Comments 13