Переменные в сервере Angie используются для расширенного управления обработкой запросов, более гибких конфигураций, а также получения подробной информации о клиентах и запросах. Постараемся разобраться со всем спектром возможностей переменных в Angie.
Навигация по циклу
Настройка location в Angie. Разделение динамических и статических запросов.
Перенаправления в Angie: return, rewrite и примеры их применения.
Сжатие текста в Angie: статика, динамика, производительность.
Работа с переменными в Angie.
Видеоверсия
Для вашего удобства подготовлена видеоверсия этой статьи, доступна на Rutube, VKVideo и YouTube.
Как работают переменные
Переменные в Angie во многом схожи с переменными в скриптовых языках. С их помощью можно реализовать конфигурацию с элементами программного кода и конструкций if-then. Переменные объявляются на этапе конфигурации, содержат строки и устанавливаются для каждого запроса. Область видимости — глобальная в рамках запроса. Все переменные можно разделить на три типа:
встроенные переменные — определяются модулями Angie, описаны в справочнике директив;
пользовательские переменные — определяются в конфигурации через директивы
map, set, geo, split_clients.позиционные переменные — определяются в случае совпадения регулярного выражения (
$1, $2и т.д.)
При этом, значения переменных вычисляются только в момент использования, поэтому сам факт определения пользовательских переменных через map или geo не добавляет накладных расходов на обработку запроса, если они не используются.
Отдельно можно выделить группы переменных, у которых фиксирована часть имени, а другая определяется свойствами запроса, например: $arg_*, $http_* и т.д.
Обращение к переменным всегда происходит со знаком доллара: $uri, $host и так далее.
Сразу стоит сказать, что экранирование знака доллара в значении переменной не поддерживается. Для использования знака доллара в строках с переменными нужно использовать директиву geo, для получения переменной с нужным значением:
http { geo $doll { default "$"; } }
Переменные в директивах
По умолчанию переменные нельзя указывать в качестве значений директив. Все случаи, где использование переменных разрешено, описаны в документации директив. Например, рассмотрим директиву add_header в HTTP. Для отладки при работе с переменными можно вывести значение строки с переменной:
server { add_header x-ruri "request_uri: $request_uri"; add_header x-uri "uri: $uri"; } curl --head http://locahost?ddd ... x-ruri: request_uri: /?ddd x-uri: uri: / ...
Как видно из примера, допустимы смешанные строки состоящие из переменных и обычных символов. Если необходимо отделить имя переменной от обычной строки, можно использовать фигурные скобки:
${uri}_test
Одним из самых мощных инструментов работы с переменными является модуль Map, к которому мы переходим далее.
Определение переменных через map
Встроенный модуль Map позволяет создавать пользовательские переменные на основе других переменных и строк. Например, нужно получить переменную с признаком “бот” в зависимости от HTTP-заголовка User-agent:
http { map $http_user_agent $limit_search_bots { default 0; ~*(google|wget|bing|yandex|msnbot|apachebench|curl) 1; "bot" 2; } }
Обратите внимание, что использование map допустимо только в контекстах http или stream. Директива map имеет два параметра: строка с исходными переменными ($http_user_agent) и результирующая переменная ($limit_search_bots). В блоке определяются различные значения результирующей переменной. Значение default (0) будет установлено, если нет ни одного совпадения с исходными параметрами в других вариантах.
Вторая строка блока представляет собой регулярное выражение без учета регистра (оператор ~*), с которым будет происходить сопоставление строки-переменной $http_user_agent. В случае совпадения с регулярным выражением переменная $limit_search_bots примет значение 1.
Третья строка это простое сравнение строки (bot) со значением переменной $http_user_agent без учёта регистра. В случае совпадения переменная $limit_search_bots примет значение 2.
Директива map лишь определяет переменную. Использовать результирующую переменную можно при записи в лог (указать в формате или реализовать условное логирование), в значении других директив, а также применить в блоке if модуля Rewrite. Рассмотрим последний вариант:
server { if ($limit_search_bots = 1) { return 421; } }
Здесь блок if имеет сравнение переменной $limit_search_bots с единицей. В случае успешного сравнения применяется конфигурация в блоке, то есть возврат кода ответа 421. Внутри блока рекомендуется использовать только директивы модуля Rewrite для сохранения предсказуемого поведения конфигурации сервера. Например, можно заменить return на перенаправление через rewrite или отправить обработку в именованную локацию через goto.
Директива map поддерживает подключение списков значений из внешних файлов через параметр include, что позволяет улучшить читаемость конфигурации с большими списками:
map $uri $redir { default ""; include /etc/angie/redir.map; } # cat /etc/angie/redir.map ... /old_content /new-page; /legacy/api /new/api; "~^/old_blog/([0-9]+)/(.*)$" /blog/$1/$2; ... server { if ($redir) { return 301 $redir; } }
В этом примере мы определили список адресов для перенаправления и целевые адреса для них. Если совпадения по $uri в списке нет, то переменная получает значение пустой строки и перенаправление не происходит.
Еще одной опцией map является свойство volatile. При активации этой опции, переменная не будет кэшироваться на время запроса, а будет вычисляться каждый раз при обращении к ней. Такое поведение может быть полезно, если вы изменяете переменную в процессе обработки запроса и не хотите допускать кэширование значения. Например, некоторые встроенные переменные допускают изменение и следующая конструкция будет иметь смысл:
http { map $uri $is_test { volatile; default 0; "/test/" 1; "/ddd" 2; } server { location /test/ { rewrite ^ /ddd?$is_test last; add_header x-test "$is_test"; proxy_pass http://localhost:8080; } location / { add_header x-test-root "$is_test"; proxy_pass http://localhost:8080; } } }
Сначала мы определили переменную $is_test через map с атрибутом volatile. Она зависит от обработанного адреса страницы ($uri). Запрос, попадая в локацию /test/, перенаправляется в location /. При этом значение переменной $is_test в первом случае (в директиве rewrite) будет “1”, а во втором при выводе заголовка уже “2”. То есть, значение переменной изменяется в процессе выполнения одного запроса.
Определение адреса проксируемого сервера через переменные
В случае с проксированием есть возможность определить адрес или группу серверов через переменную. Для этого создадим переменную:
http { map $uri $host_dest { default back_def; "~^/admin/" back_adm; "~^/api/" back_api; } }
Теперь у нас есть переменная $host_dest, которая содержит название группы серверов для проксирования. Применить эту переменную несложно:
http { upstream back_def { server 192.168.0.100; } upstream back_adm { server 192.168.0.110; } upstream back_api { server 192.168.0.120; } server { location / { proxy_pass $host_dest; } } }
Приведём пример для контекста stream с модулем SSL_preread, где с использованием директивы ssl_preread можно получить версию протокола TLS и реализовать проксирование нескольких протоколов с помощью одного TCP-порта, например, так:
stream { map $ssl_preread_protocol $upstream { "" 192.168.122.142:22; "TLSv1.2" 192.168.122.142:443; "TLSv1.3" 192.168.122.142:443; default 192.168.122.142:443; } server { listen 8443; ssl_preread on; proxy_pass $upstream; } }
При наличии валидных значений версии протокола TLS проксирование производится по адресу 192.168.122.142:443, а для пустого значения версии адрес 192.168.122.142:22 (SSH).
Использование модуля и директивы geo
Модуль geo решает простую задачу: установить значение переменной в зависимости от IP-адреса клиента. Например, с помощью geo можно реализовать простые правила доступа к веб-приложению.
http { geo $block_access { default 0; 1.2.3.4 1; 196.186.1.0/24 1; 200.1.0.0/16 1; 2001:0db8::/32 1; } server { if ($block_access) { return 403; } } }
По умолчанию адрес клиента берётся из переменной $remote_addr, но можно задать произвольную переменную.
http { geo $http_x_forwarded_for $block_access { default 0; 1.2.3.4 1; 196.186.1.0/24 1; 200.1.0.0/16 1; 2001:0db8::/32 1; } }
Итоги
Мы разобрали множество способов применения переменных в Angie. Они позволяют создать гибкие конфигурации и частично кастомизировать поведение сервера. При грамотном использовании переменные решают широкий спектр задач для веб-сервера и балансировщика.

