Порядок обработки правил в конфигурации nginx

В этой статье разбирается порядок применения правил nginx для обработки путей, блоков location, условий if, директив rewrite и других директив и параметров. Значение порядка применения определяет, какое правило сработает первым. В общем случае, первое совпавшее правило применяется, и обработка на этом заканчивается.

1. Иерархия контекстов

http {
    server {
        location /path {
            # Обработка запроса
        }
    }
}

Порядок наследования:

  1. Директивы в http (самый низкий приоритет)

  2. Директивы в server (переопределяют http)

  3. Директивы в location (самый высокий приоритет)

2. Порядок выбора location блоков

2.1. Приоритеты типов location (от высшего к низшему)

# 1. Точное совпадение (=) - НАИВЫСШИЙ ПРИОРИТЕТ
location = /exact/path {
    # Только для /exact/path
}

# 2. Приоритетный префикс (^~) 
location ^~ /static/ {
    # Любой путь, начинающийся с /static/
    # Если совпало, регулярные выражения НЕ проверяются
}

# 3. Регулярные выражения (~ и ~*) - ПОРЯДОК ОБЪЯВЛЕНИЯ
location ~ \.php$ {        # регистрозависимый
    # Обрабатывается в порядке объявления
}

location ~* \.(jpg|png)$ {  # регистронезависимый
    # Обрабатывается в порядке объявления
}

# 4. Стандартный префикс - САМЫЙ НИЗКИЙ ПРИОРИТЕТ
location / {
    # Любой путь, начинающийся с /
    # Выбирается самый длинный совпадающий префикс
}

2.2. Полный алгоритм выбора location

location = /exact {
    # Шаг 1: Проверка точных совпадений (=)
    # Если найдено - немедленное использование
}

location ^~ /prefix {
    # Шаг 2: Проверка приоритетных префиксов (^~)
    # Если найдено - регулярные выражения НЕ проверяются
}

location ~ \.php$ {
    # Шаг 3: Проверка регистрозависимых regex (~)
    # Проверяются в порядке объявления
}

location ~* \.html$ {
    # Шаг 4: Проверка регистронезависимых regex (~*)
    # Проверяются в порядке объявления
}

location / {
    # Шаг 5: Выбор префиксного совпадения
    # Выбирается самый длинный совпадающий префикс
}

3. Обработка директивы if

3.1. Приоритет и порядок выполнения

# ВАЖНО: if выполняется ВНУТРИ выбранного location
# НЕ влияет на выбор location

location / {
    # 1. Сначала проверяются условия if
    if ($request_uri ~ ^/admin) {
        # 2. Если true - выполняются директивы внутри
        return 403;
    }
    
    # 3. Затем выполняется остальной код
    try_files $uri $uri/ /index.php;
}

3.2. Правила выполнения if

# Избегайте if в location, если возможно!
# if может создавать неожиданное поведение

location / {
    # Правильное использование: проверка существования файла
    if (!-e $request_filename) {
        rewrite ^/(.*)$ /index.php?q=$1 last;
    }
    
    # Правильное использование: проверка переменных
    if ($http_user_agent ~ MSIE) {
        rewrite ^(.*)$ /msie/$1 break;
    }
    
    # ОПАСНО: try_files внутри if
    if ($request_uri ~ ^/test) {
        try_files $uri =404;  # НЕ РАБОТАЕТ КАК ОЖИДАЕТСЯ
    }
    
    # ОПАСНО: proxy_pass внутри if
    if ($http_user_agent ~ iPad) {
        proxy_pass http://backend;  # МОЖЕТ ВЫЗВАТЬ ОШИБКИ
    }
}

4. Порядок обработки rewrite правил

4.1. Цикл обработки rewrite

server {
    # rewrite выполняется в таком порядке:
    
    # 1. Сначала server-level rewrite (в порядке объявления)
    rewrite ^/old/(.*)$ /new/$1 permanent;  # Вне location
    
    location / {
        # 2. Затем location-level rewrite (в порядке объявления)
        rewrite ^/page/(.*)$ /index.php?page=$1 last;
        
        # 3. Затем обработка внутри блока
        try_files $uri $uri/ =404;
    }
}

4.2. Флаги rewrite

# last - останавливает обработку, ищет новый location
rewrite ^/old/(.*)$ /new/$1 last;

# break - останавливает обработку, продолжает в текущем location
rewrite ^/images/(.*)$ /files/$1 break;

# redirect - возвращает 302
rewrite ^/old$ /new redirect;

# permanent - возвращает 301
rewrite ^/old$ /new permanent;

5. Обработка query параметров

5.1. Сохранение параметров

# По умолчанию параметры сохраняются
rewrite ^/page/(.*)$ /index.php?page=$1;  # /page/123?x=1 → /index.php?page=123&x=1

# Удаление параметров (добавление ?)
rewrite ^/page/(.*)$ /index.php?page=$1?;  # /page/123?x=1 → /index.php?page=123

# Явное добавление параметров
rewrite ^/page/(.*)$ /index.php?page=$1&foo=bar;  # /page/123 → /index.php?page=123&foo=bar

5.2. Доступ к параметрам

# Переменные для работы с параметрами
$args          # Все параметры: "x=1&y=2"
$query_string  # То же, что $args
$arg_<name>    # Конкретный параметр: $arg_x = "1"

# Изменение параметров
location / {
    if ($arg_sort) {
        set $args "${args}&order=asc";  # Добавление параметра
    }
}

6. Пример полной обработки запроса

# Запрос: GET /pin/123?x=1 HTTP/1.1

server {
    # ШАГ 1: Server-level обработка
    rewrite ^/old/(.*)$ /new/$1 permanent;
    # /pin/123?x=1 не совпадает
    
    # ШАГ 2: Выбор location (по приоритету)
    # Проверка точных совпадений
    location = /pin/123 {  # НЕ СОВПАДАЕТ (разные пути)
        # ...
    }
    
    # Проверка приоритетных префиксов
    location ^~ /pin/ {  # СОВПАДАЕТ!
        # ШАГ 3: Выполнение в location
        
        # Проверка if (выполняется после выбора location)
        if ($arg_x = "1") {
            # Если true
            return 410;  # Возвращает 410
        }
        
        # Если if не сработал
        try_files $uri $uri/ =404;
    }
    
    # Regex location (НЕ ПРОВЕРЯЮТСЯ из-за ^~)
    location ~ \.php$ {
        # ...
    }
    
    # Префиксный location (НЕ ПРОВЕРЯЕТСЯ)
    location / {
        # ...
    }
}

7. Специальные случаи и исключения

7.1. Обработка error_page

error_page 404 /404.html;  # Перенаправляет на указанную страницу

# error_page выполняется ПОСЛЕ всех остальных правил
location / {
    try_files $uri $uri/ =404;  # Если файл не найден → error_page
}

location = /404.html {
    internal;  # Только внутренние запросы
}

7.2. Обработка try_files

location / {
    # try_files проверяет в указанном порядке
    try_files $uri $uri/ /index.php?$args =404;
    # 1. Существует ли файл $uri?
    # 2. Существует ли директория $uri/?
    # 3. Если нет - перейти на /index.php с параметрами
    # 4. Если и это не работает - вернуть 404
}

7.3. Приоритет внутренних перенаправлений

# Внутреннее перенаправление через X-Accel-Redirect
# Имеет более высокий приоритет, чем обычные location

location /protected/ {
    internal;
    root /secure;
}

location /download/ {
    # После выполнения PHP, если возвращен X-Accel-Redirect,
    # запрос перенаправляется в location /protected/
    try_files $uri /download.php;
}

8. Полный список приоритетов (от высшего к низшему)

1. location = /exact        # Точное совпадение
2. error_page               # Обработка ошибок (после основной обработки)
3. X-Accel-Redirect        # Внутреннее перенаправление
4. location ^~ /prefix     # Приоритетный префикс
5. location ~ \.php$       # Регулярные выражения (в порядке объявления)
6. location ~* \.jpg$      # Регулярные выражения (регистронезависимые)
7. location /longest/      # Стандартный префикс (самый длинный)
8. location /              # Стандартный префикс (короткий)
9. try_files               # Проверка наличия файлов
10. rewrite в server       # Server-level rewrite
11. rewrite в location     # Location-level rewrite
12. if внутри location     # Условные проверки
13. return                 # Прямой возврат кода
14. proxy_pass             # Проксирование
15. fastcgi_pass          # PHP-FPM

9. Важные правила

# 1. Порядок объявления важен для regex
location ~ \.php$ { ... }    # Проверяется ПЕРВЫМ
location ~ \.html$ { ... }   # Проверяется ВТОРЫМ

# 2. location = имеет наивысший приоритет, но только для точного пути
location = / { ... }         # Только для /
location / { ... }           # Для /, /index, /page, etc.

# 3. ^~ отключает проверку regex
location ^~ /admin/ { ... }  # Все пути /admin/*, regex не проверяются

# 4. Внутри location важна последовательность
location / {
    # Сначала rewrite
    rewrite ^/page/(.*)$ /index.php?page=$1 last;
    
    # Потом if
    if ($arg_sort) {
        rewrite ^ /sort.php?$args break;
    }
    
    # Потом try_files
    try_files $uri $uri/ =404;
    
    # Потом proxy_pass
    proxy_pass http://backend;
}

Был ли наш пост полезен?

Нажмите на звезду, чтобы оценить мои труды!

Средний рейтинг: 0 / 5. Количество голосов: 0

Пока голосов нет. Проголосуй первым!

Мне жаль, что пост вам не помог 🙁

Позвольте мне исправиться.

Поделитесь, что можно улучшить?

Похожие посты