Введение

У nginx есть один главный и несколько рабочих процессов. Основная задача главного процесса - чтение и проверка конфигурации и управление рабочими процессами. Рабочие процессы выполняют фактическую обработку запросов. nginx использует модель, основанную на событиях, и зависящие от операционной системы механизмы для эффективного распределения запросов между рабочими процессами. Количество рабочих процессов задаётся в конфигурационном файле и может быть фиксированным для данной конфигурации или автоматически устанавливаться равным числу доступных процессорных ядер (worker_processes).

Как работают nginx и его модули, определяется в конфигурационном файле. По умолчанию, конфигурационный файл называется nginx.conf и расположен в каталоге /usr/local/nginx/conf, /etc/nginx или /usr/local/etc/nginx.


Запуск, остановка, перезагрузка конфигурации

Чтобы запустить nginx, нужно выполнить исполняемый файл. Когда nginx запущен, им можно управлять, вызывая исполняемый файл с параметром -s. Используйте следующий синтаксис:

nginx -s сигнал

Где сигнал может быть одним из нижеследующих:

  • stop - быстрое завершение
  • quit - плавное завершение
  • reload - перезагрузка конфигурационного файла
  • reopen - переоткрытие лог-файлов

Например, чтобы остановить процессы nginx с ожиданием окончания обслуживания текущих запросов рабочими процессами, можно выполнить следующую команду:

nginx -s quit

Изменения, сделанные в конфигурационном файле, не будут применены, пока команда перезагрузить конфигурацию не будет вручную отправлена nginx'у или он не будет перезапущен. Для перезагрузки конфигурации выполните:

nginx -s reload

Получив сигнал, главный процесс проверяет правильность синтаксиса нового конфигурационного файла и пытается применить конфигурацию, содержащуюся в нём. Если это ему удаётся, главный процесс запускает новые рабочие процессы и отправляет сообщения старым рабочим процессам с требованием завершиться. В противном случае, главный процесс откатывает изменения и продолжает работать со старой конфигурацией. Старые рабочие процессы, получив команду завершиться, прекращают принимать новые запросы и продолжают обслуживать текущие запросы до тех пор, пока все такие запросы не будут обслужены. После этого старые рабочие процессы завершаются.

Посылать сигналы процессам nginx можно также средствами Unix, такими как утилита kill. В этом случае сигнал отправляется напрямую процессу с данным ID. ID главного процесса nginx записывается по умолчанию в файл nginx.pid в каталоге /usr/local/nginx/logs или /var/run. Например, если ID главного процесса равен 1628, для отправки сигнала QUIT, который приведёт к плавному завершению nginx, нужно выполнить:

kill -s QUIT 1628

Для просмотра списка всех запущенных процессов nginx может быть использована утилита ps, например, следующим образом:

ps -ax | grep nginx

Структура конфигурационного файла

nginx состоит из модулей, которые настраиваются директивами, указанными в конфигурационном файле. Директивы делятся на простые и блочные. Простая директива состоит из имени и параметров, разделённых пробелами, и оканчивается точкой с запятой (;). Блочная директива устроена так же, как и простая директива, но вместо точки с запятой после имени и параметров следует набор дополнительных инструкций, помещённых внутри фигурных скобок ({ и }). Если у блочной директивы внутри фигурных скобок можно задавать другие директивы, то она называется контекстом (примеры: events, http, server и location).

Директивы, помещённые в конфигурационном файле вне любого контекста, считаются находящимися в контексте main. Директивы events и http располагаются в контексте main, server - в http, а location - в server.

Часть строки после символа # считается комментарием.


Раздача статического содержимого

Одна из важных задач конфигурации nginx - раздача файлов, таких как изображения или статические HTML-страницы. Рассмотрим пример, в котором в зависимости от запроса файлы будут раздаваться из разных локальных каталогов: /data/www, который содержит HTML-файлы, и /data/images, содержащий файлы с изображениями. Для этого потребуется отредактировать конфигурационный файл и настроить блок server внутри блока http с двумя блоками location.

Во-первых, создайте каталог /data/www и положите в него файл index.html с любым текстовым содержанием, а также создайте каталог /data/images и положите в него несколько файлов с изображениями.

Далее, откройте конфигурационный файл. Конфигурационный файл по умолчанию уже включает в себя несколько примеров блока server, большей частью закомментированных. Для нашей текущей задачи лучше закомментировать все такие блоки и добавить новый блок server:

http {
    server {
    }
}

В общем случае конфигурационный файл может содержать несколько блоков server, различаемых по портам, на которых они слушают, и по имени сервера. Определив, какой server будет обрабатывать запрос, nginx сравнивает URI, указанный в заголовке запроса, с параметрами директив location, определённых внутри блока server.

В блок server добавьте блок location следующего вида:

location / {
    root /data/www;
}

Этот блок location задаёт "/" в качестве префикса, который сравнивается с URI из запроса. Для подходящих запросов добавлением URI к пути, указанному в директиве root, то есть, в данном случае, к /data/www, получается путь к запрашиваемому файлу в локальной файловой системе. Если есть совпадение с несколькими блоками location, nginx выбирает блок с самым длинным префиксом. В блоке location выше указан самый короткий префикс, длины один, и поэтому этот блок будет использован, только если не будет совпадения ни с одним из остальных блоков location.

Далее, добавьте второй блок location:

location /images/ {
    root /data;
}

Он будет давать совпадение с запросами, начинающимися с /images/ (location / для них тоже подходит, но указанный там префикс короче).

Итоговая конфигурация блока server должна выглядеть следующим образом:

server {
    location / {
        root /data/www;
    }

    location /images/ {
        root /data;
    }
}

Это уже работающая конфигурация сервера, слушающего на стандартном порту 80 и доступного на локальном компьютере по адресу http://localhost/. В ответ на запросы, URI которых начинаются с /images/, сервер будет отправлять файлы из каталога /data/images. Например, на запрос http://localhost/images/example.png nginx отправит в ответ файл /data/images/example.png. Если же этот файл не существует, nginx отправит ответ, указывающий на ошибку 404. Запросы, URI которых не начинаются на /images/, будут отображены на каталог /data/www. Например, в результате запроса http://localhost/some/example.html в ответ будет отправлен файл /data/www/some/example.html.

Чтобы применить новую конфигурацию, запустите nginx, если он ещё не запущен, или отправьте сигнал reload главному процессу nginx, выполнив:

nginx -s reload

В случае, если что-то работает не как ожидалось, можно попытаться выяснить причину с помощью файлов access.log и error.log из каталога /usr/local/nginx/logs или /var/log/nginx.


Настройка простого прокси-сервера

Одним из частых применений nginx является использование его в качестве прокси-сервера, то есть сервера, который принимает запросы, перенаправляет их на проксируемые сервера, получает ответы от них и отправляет их клиенту.

Мы настроим базовый прокси-сервер, который будет обслуживать запросы изображений из локального каталога и отправлять все остальные запросы на проксируемый сервер. В этом примере оба сервера будут работать в рамках одного экземпляра nginx.

Во-первых, создайте проксируемый сервер, добавив ещё один блок server в конфигурационный файл nginx со следующим содержимым:

server {
    listen 8080;
    root /data/up1;

    location / {
    }
}

Это будет простой сервер, слушающий на порту 8080 (ранее директива listen не указывалась, потому что использовался стандартный порт 80) и отображающий все запросы на каталог /data/up1 в локальной файловой системе. Создайте этот каталог и положите в него файл index.html. Обратите внимание, что директива root помещена в контекст server. Такая директива root будет использована, когда директива location, выбранная для выполнения запроса, не содержит собственной директивы root.

Далее, используйте конфигурацию сервера из предыдущего раздела и видоизмените её, превратив в конфигурацию прокси-сервера. В первый блок location добавьте директиву proxy_pass, указав протокол, имя и порт проксируемого сервера в качестве параметра (в нашем случае это http://localhost:8080):

server {
    location / {
        proxy_pass http://localhost:8080;
    }

    location /images/ {
        root /data;
    }
}

Мы изменим второй блок location, который на данный момент отображает запросы с префиксом /images/ на файлы из каталога /data/images так, чтобы он подходил для запросов изображений с типичными расширениями файлов. Изменённый блок location выглядит следующим образом:

location ~ \.(gif|jpg|png)$ {
    root /data/images;
}

Параметром является регулярное выражение, дающее совпадение со всеми URI, оканчивающимися на .gif, .jpg или .png. Регулярному выражению должен предшествовать символ ~. Соответствующие запросы будут отображены на каталог /data/images.

Когда nginx выбирает блок location, который будет обслуживать запрос, то вначале он проверяет директивы location, задающие префиксы, запоминая location с самым длинным подходящим префиксом, а затем проверяет регулярные выражения. Если есть совпадение с регулярным выражением, nginx выбирает соответствующий location, в противном случае берётся запомненный ранее location.

Итоговая конфигурация прокси-сервера выглядит следующим образом:

server {
    location / {
        proxy_pass http://localhost:8080/;
    }

    location ~ \.(gif|jpg|png)$ {
        root /data/images;
    }
}

Этот сервер будет фильтровать запросы, оканчивающиеся на .gif, .jpg или .png, и отображать их на каталог /data/images (добавлением URI к параметру директивы root) и перенаправлять все остальные запросы на проксируемый сервер, сконфигурированный выше.


Настройка проксирования FastCGI

nginx можно использовать для перенаправления запросов на FastCGI-серверы. На них могут исполняться приложения, созданные с использованием разнообразных фреймворков и языков программирования, например, PHP.

Базовая конфигурация nginx для работы с проксируемым FastCGI-сервером включает в себя использование директивы fastcgi_pass вместо директивы proxy_pass, и директив fastcgi_param для настройки параметров, передаваемых FastCGI-серверу. Представьте, что FastCGI-сервер доступен по адресу localhost:9000. Взяв за основу конфигурацию прокси-сервера из предыдущего раздела, замените директиву proxy_pass на директиву fastcgi_pass и измените параметр на localhost:9000. В PHP параметр SCRIPT_FILENAME используется для определения имени скрипта, а в параметре QUERY_STRING передаются параметры запроса. Получится следующая конфигурация:

server {
    location / {
        fastcgi_pass localhost:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param QUERY_STRING $query_string;
    }

    location ~ \.(gif|jpg|png)$ {
        root /data/images;
    }
}

Таким образом будет настроен сервер, который будет перенаправлять все запросы, кроме запросов статических изображений, на проксируемый сервер, работающий по адресу localhost:9000, по протоколу FastCGI.


Методы обработки соединений

nginx поддерживает различные методы обработки соединений. Наличие того или иного метода зависит от используемой платформы. Если на платформе доступно сразу несколько методов, nginx обычно сам выбирает наиболее эффективный метод. Однако при необходимости можно явно выбрать метод обработки соединений с помощью директивы use.

Поддерживаются следующие методы обработки соединений:

  • select - стандартный метод. Модуль для поддержки этого метода собирается автоматически, если на платформе не обнаружено более эффективного метода. Можно принудительно разрешить или запретить сборку этого модуля с помощью параметров --with-select_module и --without-select_module.

  • poll - стандартный метод. Модуль для поддержки этого метода собирается автоматически, если на платформе не обнаружено более эффективного метода. Можно принудительно разрешить или запретить сборку этого модуля с помощью параметров --with-poll_module и --without-poll_module.

  • kqueue - эффективный метод, используемый во FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 и macOS.

  • epoll - эффективный метод, используемый в Linux 2.6+.

  • /dev/poll - эффективный метод, используемый в Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+ и Tru64 UNIX 5.1A+.

  • eventport - event ports, метод, используемый в Solaris 10+ (из-за имеющихся проблем вместо него рекомендуется использовать метод /dev/poll).


Единицы измерения в конфигурационном файле

Размеры можно указывать в байтах, килобайтах (суффиксы k и K) или мегабайтах (суффиксы m и M), например, "1024", "8k", "1m".

Интервалы времени можно задавать в миллисекундах, секундах, минутах, часах, днях и т.д., используя следующие суффиксы:

  • ms - миллисекунды
  • s - секунды
  • m - минуты
  • h - часы
  • d - дни
  • w - недели
  • M - месяцы, 30 дней
  • y - годы, 365 дней

В одном значении можно комбинировать различные единицы, указывая их в порядке от более к менее значащим, и по желанию отделяя их пробелами. Например, "1h 30m" задаёт то же время, что и "90m" или "5400s". Значение без суффикса задаёт секунды. Рекомендуется всегда указывать суффикс.

Некоторые интервалы времени можно задать лишь с точностью до секунд.


Параметры командной строки nginx

nginx поддерживает следующие параметры командной строки:

  • -? | -h - вывод справки по параметрам командной строки.
  • -c файл - использование альтернативного конфигурационного файла файл вместо файла по умолчанию.
  • -e файл - использование альтернативного лог-файла ошибок файл вместо файла по умолчанию.
  • -g директивы - задание глобальных директив конфигурации, например
    nginx -g "pid /var/run/nginx.pid; worker_processes `sysctl -n hw.ncpu`;"
  • -p префикс - задание префикса пути nginx, т.е. каталога, в котором будут находиться файлы сервера (по умолчанию - каталог /usr/local/nginx).
  • -q - вывод только сообщений об ошибках при тестировании конфигурации.
  • -s сигнал - отправка сигнала главному процессу. Аргументом сигнал может быть:
    stop - быстрое завершение
    quit - плавное завершение
    reload - перезагрузка конфигурации, старт нового рабочего процесса с новой конфигурацией, плавное завершение старых рабочих процессов
    reopen - переоткрытие лог-файлов
  • -t - тестирование конфигурационного файла: nginx проверяет синтаксическую правильность конфигурации, а затем пытается открыть файлы, описанные в конфигурации.
  • -T - то же, что и -t, а также вывод конфигурационных файлов в стандартный поток вывода.
  • -v - вывод версии nginx.
  • -V - вывод версии nginx, версии компилятора и параметров конфигурации сборки.

Как nginx обрабатывает запросы. Определение виртуального сервера по имени

nginx вначале решает, какой из серверов должен обработать запрос. Рассмотрим простую конфигурацию, где все три виртуальных сервера слушают на порту *:80:

server {
    listen      80;
    server_name example.org www.example.org;
    ...
}

server {
    listen      80;
    server_name example.net www.example.net;
    ...
}

server {
    listen      80;
    server_name example.com www.example.com;
    ...
}

В этой конфигурации, чтобы определить, какому серверу следует направить запрос, nginx проверяет только поле "Host" заголовка запроса. Если его значение не соответствует ни одному из имён серверов или в заголовке запроса нет этого поля вовсе, nginx направит запрос в сервер по умолчанию для этого порта. В вышеприведённой конфигурации сервером по умолчанию будет первый сервер, что соответствует стандартному поведению nginx по умолчанию. Сервер по умолчанию можно задать явно с помощью параметра default_server в директиве listen:

server {
    listen      80 default_server;
    server_name example.net www.example.net;
    ...
}

Как nginx обрабатывает запросы. Как предотвратить обработку запросов без имени сервера

Если запросы без поля "Host" в заголовке не должны обрабатываться, можно определить сервер, который будет их отклонять:

server {
    listen      80;
    server_name "";
    return      444;
}

Здесь в качестве имени сервера указана пустая строка, которая соответствует запросам без поля "Host" в заголовке, и возвращается специальный для nginx код 444, который закрывает соединение.


Как nginx обрабатывает запросы. Определение виртуального сервера по имени и IP-адресу

Рассмотрим более сложную конфигурацию, в которой некоторые виртуальные серверы слушают на разных адресах:

server {
    listen      192.168.1.1:80;
    server_name example.org www.example.org;
    ...
}

server {
    listen      192.168.1.1:80;
    server_name example.net www.example.net;
    ...
}

server {
    listen      192.168.1.2:80;
    server_name example.com www.example.com;
    ...
}

В этой конфигурации nginx вначале сопоставляет IP-адрес и порт запроса с директивами listen в блоках server. Затем он сопоставляет значение поля "Host" заголовка запроса с директивами server_name в блоках server, которые соответствуют IP-адресу и порту. Если имя сервера не найдено, запрос будет обработан в сервере по умолчанию. Например, запрос www.example.com, пришедший на порт 192.168.1.1:80, будет обработан сервером по умолчанию для порта 192.168.1.1:80, т.е. первым сервером, т.к. для этого порта www.example.com не указан в списке имён серверов.

Как уже говорилось, сервер по умолчанию является свойством слушающего порта, поэтому у разных портов могут быть определены свои серверы по умолчанию:

server {
    listen      192.168.1.1:80;
    server_name example.org www.example.org;
    ...
}

server {
    listen      192.168.1.1:80 default_server;
    server_name example.net www.example.net;
    ...
}

server {
    listen      192.168.1.2:80 default_server;
    server_name example.com www.example.com;
    ...
}

Как nginx обрабатывает запросы. Конфигурация простого сайта PHP

Теперь посмотрим на то, как nginx выбирает location для обработки запроса на примере обычного простого PHP-сайта:

server {
    listen      80;
    server_name example.org www.example.org;
    root        /data/www;

    location / {
        index   index.html index.php;
    }

    location ~* \.(gif|jpg|png)$ {
        expires 30d;
    }

    location ~ \.php$ {
        fastcgi_pass  localhost:9000;
        fastcgi_param SCRIPT_FILENAME
                      $document_root$fastcgi_script_name;
        include       fastcgi_params;
    }
}

nginx вначале ищет среди всех префиксных location'ов, заданных строками, максимально совпадающий. В вышеприведённой конфигурации указан только один префиксный location "/", и поскольку он подходит под любой запрос, он и будет использован, если других совпадений не будет найдено. Затем nginx проверяет location'ы, заданные регулярными выражениями, в порядке их следования в конфигурационном файле. При первом же совпадении поиск прекращается и nginx использует совпавший location. Если запросу не соответствует ни одно из регулярных выражений, nginx использует максимально совпавший префиксный location, найденный ранее.

Следует иметь в виду, что location'ы всех типов сопоставляются только с URI-частью строки запроса без аргументов. Так делается потому, что аргументы в строке запроса могут быть заданы различными способами, например:

/index.php?user=john&page=1
/index.php?page=1&user=john

Кроме того, в строке запроса можно запросить что угодно:

/index.php?page=1&something+else&user=john

Теперь посмотрим, как бы обрабатывались запросы в вышеприведённой конфигурации:

  • Запросу "/logo.gif" во-первых соответствует префиксный location "/", а во-вторых - регулярное выражение "\.(gif|jpg|png)$", поэтому он обрабатывается location'ом регулярного выражения. Согласно директиве "root /data/www" запрос отображается в файл /data/www/logo.gif, который и посылается клиенту.
  • Запросу "/index.php" также во-первых соответствует префиксный location "/", а во-вторых - регулярное выражение "\.(php)$". Следовательно, он обрабатывается location'ом регулярного выражения и запрос передаётся FastCGI-серверу, слушающему на localhost:9000. Директива fastcgi_param устанавливает FastCGI-параметр SCRIPT_FILENAME в "/data/www/index.php", и сервер FastCGI выполняет указанный файл. Переменная $document_root равна значению директивы root, а переменная $fastcgi_script_name равна URI запроса, т.е. "/index.php".
  • Запросу "/about.html" соответствует только префиксный location "/", поэтому запрос обрабатывается в нём. Согласно директиве "root /data/www" запрос отображается в файл /data/www/about.html, который и посылается клиенту.
  • Обработка запроса "/" более сложная. Ему соответствует только префиксный location "/", поэтому запрос обрабатывается в нём. Затем директива index проверяет существование индексных файлов согласно своих параметров и директиве "root /data/www". Если файл /data/www/index.html не существует, а файл /data/www/index.php существует, то директива делает внутреннее перенаправление на "/index.php" и nginx снова сопоставляет его с location'ами, как если бы такой запрос был послан клиентом. Как мы видели ранее, перенаправленный запрос будет в конечном итоге обработан сервером FastCGI.

Имена сервера

Имена сервера задаются с помощью директивы server_name и определяют, в каком блоке server будет обрабатываться тот или иной запрос. Имена могут быть заданы точно, с помощью маски или регулярного выражения:

server {
    listen       80;
    server_name  example.org  www.example.org;
    ...
}

server {
    listen       80;
    server_name  *.example.org;
    ...
}

server {
    listen       80;
    server_name  mail.*;
    ...
}

server {
    listen       80;
    server_name  ~^(?<user>.+)\.example\.net$;
    ...
}

При поиске виртуального сервера по имени, если имени соответствует несколько из указанных вариантов, например, одновременно подходят и имя с маской, и регулярное выражение, будет выбран первый подходящий вариант в следующем порядке приоритета:

  • точное имя
  • самое длинное имя с маской в начале, например "*.example.org"
  • самое длинное имя с маской в конце, например "mail.*"
  • первое подходящее регулярное выражение (в порядке следования в конфигурационном файле)

Имена с масками

Имя с маской может содержать звёздочку ("*") только в начале или в конце имени, и только на границе, определяемой точкой. Имена "www.*.example.org" и "w*.example.org" являются некорректными, но их можно задать с помощью регулярных выражений, например, "~^www\..+\.example\.org$" и "~^w.*\.example\.org$". Звёздочка может соответствовать нескольким частям имени. Имени с маской "*.example.org" соответствует не только www.example.org, но и www.sub.example.org.

Специальное имя с маской вида ".example.org" соответствует как точному имени "example.org", так и маске "*.example.org".


Имена, заданные регулярными выражениями

Регулярные выражения, используемые в nginx, совместимы с используемыми в языке программирования Perl (PCRE). Имя сервера, заданное регулярным выражением, должно начинаться с символа тильды:

server_name  ~^www\d+\.example\.net$;

в противном случае оно будет рассматриваться как точное, или же, если выражение содержит звёздочку ("*"), то как имя с маской (и, скорее всего, некорректное). Не забывайте ставить специальные символы начала ("^") и конца ("$") строки. По синтаксису они не требуются, но логически они могут быть нужны. Также заметьте, что все точки в доменных именах должны быть экранированы символом обратной косой черты. Регулярное выражение, содержащее символы "{" и "}", необходимо экранировать:

server_name  "~^(?<name>\w\d{1,3}+)\.example\.net$";

иначе nginx откажется запускаться и выдаст сообщение об ошибке:

directive "server_name" is not terminated by ";" in ...

К именованному выделению в регулярном выражении можно впоследствии обратиться через переменную:

server {
    server_name   ~^(www\.)?(?<domain>.+)$;

    location / {
        root   /sites/$domain;
    }
}

Интернационализованные имена

Для указания интернационализированных доменных имён (IDNs) в директиве server_name следует указывать Punycode-представление имени:

server {
    listen       80;
    server_name  xn--e1afmkfd.xn--80akhbyknj4f;  # пример.испытание
    ...
}

Настройка HTTPS-серверов

Чтобы настроить HTTPS-сервер, необходимо включить параметр ssl на слушающих сокетах в блоке server, а также указать местоположение файлов с сертификатом сервера и секретным ключом:

server {
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ...
}

Сертификат сервера является публичным. Он посылается каждому клиенту, соединяющемуся с сервером. Секретный ключ следует хранить в файле с ограниченным доступом (права доступа должны позволять главному процессу nginx читать этот файл). Секретный ключ можно также хранить в одном файле с сертификатом:

ssl_certificate     www.example.com.cert;
ssl_certificate_key www.example.com.cert;

при этом права доступа к файлу следует также ограничить. Несмотря на то, что и сертификат, и ключ хранятся в одном файле, клиенту посылается только сертификат.

С помощью директив ssl_protocols и ssl_ciphers можно ограничить соединения использованием только "сильных" версий и шифров SSL/TLS. По умолчанию nginx использует "ssl_protocols TLSv1 TLSv1.1 TLSv1.2" и "ssl_ciphers HIGH:!aNULL:!MD5", поэтому их явная настройка в общем случае не требуется. Следует отметить, что значения по умолчанию этих директив несколько раз менялись.


Оптимизация HTTPS-сервера

SSL-операции потребляют дополнительные ресурсы процессора. На мультипроцессорных системах следует запускать несколько рабочих процессов, не меньше числа доступных процессорных ядер. Наиболее ресурсоёмкой для процессора является операция SSL handshake, в рамках которой формируются криптографические параметры сессии. Существует два способа уменьшения числа этих операций, производимых для каждого клиента: использование постоянных (keepalive) соединений, позволяющих в рамках одного соединения обрабатывать сразу несколько запросов, и повторное использование параметров SSL-сессии для предотвращения необходимости выполнения SSL handshake для параллельных и последующих соединений. Сессии хранятся в кэше SSL-сессий, разделяемом между рабочими процессами и настраиваемом директивой ssl_session_cache. В 1 мегабайт кэша помещается около 4000 сессий. Таймаут кэша по умолчанию равен 5 минутам. Он может быть увеличен с помощью директивы ssl_session_timeout. Вот пример конфигурации, оптимизированной под многоядерную систему с 10-мегабайтным разделяемым кэшем сессий:

worker_processes auto;

http {
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;

    server {
        listen              443 ssl;
        server_name         www.example.com;
        keepalive_timeout   70;

        ssl_certificate     www.example.com.crt;
        ssl_certificate_key www.example.com.key;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         HIGH:!aNULL:!MD5;
        ...

Цепочки SSL-сертификатов

Некоторые браузеры могут выдавать предупреждение о сертификате, подписанном общеизвестным центром сертификации, в то время как другие браузеры без проблем принимают этот же сертификат. Так происходит потому, что центр, выдавший сертификат, подписал его промежуточным сертификатом, которого нет в базе данных сертификатов общеизвестных доверенных центров сертификации, распространяемой вместе с браузером. В подобном случае центр сертификации предоставляет "связку" сертификатов, которую следует присоединить к сертификату сервера. Сертификат сервера следует разместить перед связкой сертификатов в скомбинированном файле:

$ cat www.example.com.crt bundle.crt > www.example.com.chained.crt

Полученный файл следует указать в директиве ssl_certificate:

server {
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.chained.crt;
    ssl_certificate_key www.example.com.key;
    ...
}

Если сертификат сервера и связка сертификатов были соединены в неправильном порядке, nginx откажется запускаться и выдаст сообщение об ошибке:

SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed
   (SSL: error:0B080074:x509 certificate routines:
    X509_check_private_key:key values mismatch)

поскольку nginx попытается использовать секретный ключ с первым сертификатом из связки вместо сертификата сервера.

Браузеры обычно сохраняют полученные промежуточные сертификаты, подписанные доверенными центрами сертификации, поэтому активно используемые браузеры уже могут иметь требуемые промежуточные сертификаты и не выдать предупреждение о сертификате, присланном без связанной с ним цепочки сертификатов. Убедиться в том, что сервер присылает полную цепочку сертификатов, можно при помощи утилиты командной строки openssl, например:

$ openssl s_client -connect www.godaddy.com:443
...
Certificate chain
 0 s:/C=US/ST=Arizona/L=Scottsdale/1.3.6.1.4.1.311.60.2.1.3=US
     /1.3.6.1.4.1.311.60.2.1.2=AZ/O=GoDaddy.com, Inc
     /OU=MIS Department/CN=www.GoDaddy.com
     /serialNumber=0796928-7/2.5.4.15=V1.0, Clause 5.(b)
   i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
     /OU=http://certificates.godaddy.com/repository
     /CN=Go Daddy Secure Certification Authority
     /serialNumber=07969287
 1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
     /OU=http://certificates.godaddy.com/repository
     /CN=Go Daddy Secure Certification Authority
     /serialNumber=07969287
   i:/C=US/O=The Go Daddy Group, Inc.
     /OU=Go Daddy Class 2 Certification Authority
 2 s:/C=US/O=The Go Daddy Group, Inc.
     /OU=Go Daddy Class 2 Certification Authority
   i:/L=ValiCert Validation Network/O=ValiCert, Inc.
     /OU=ValiCert Class 2 Policy Validation Authority
     /CN=http://www.valicert.com//emailAddress=info@valicert.com
...

Единый HTTP/HTTPS сервер

Можно настроить единый сервер, который обслуживает как HTTP-, так и HTTPS-запросы:

server {
    listen              80;
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    ...
}

Выбор HTTPS-сервера по имени

Типичная проблема возникает при настройке двух и более серверов HTTPS, слушающих на одном и том же IP-адресе:

server {
    listen          443 ssl;
    server_name     www.example.com;
    ssl_certificate www.example.com.crt;
    ...
}

server {
    listen          443 ssl;
    server_name     www.example.org;
    ssl_certificate www.example.org.crt;
    ...
}

В такой конфигурации браузер получит сертификат сервера по умолчанию, т.е. www.example.com, независимо от запрашиваемого имени сервера. Это связано с поведением протокола SSL. SSL-соединение устанавливается до того, как браузер посылает HTTP-запрос, и nginx не знает имени запрашиваемого сервера. Следовательно, он лишь может предложить сертификат сервера по умолчанию.

Наиболее старым и надёжным способом решения этой проблемы является назначение каждому HTTPS-серверу своего IP-адреса:

server {
    listen          192.168.1.1:443 ssl;
    server_name     www.example.com;
    ssl_certificate www.example.com.crt;
    ...
}

server {
    listen          192.168.1.2:443 ssl;
    server_name     www.example.org;
    ssl_certificate www.example.org.crt;
    ...
}

SSL-сертификат с несколькими именами

Существуют и другие способы, которые позволяют использовать один и тот же IP-адрес сразу для нескольких HTTPS-серверов. Все они, однако, имеют свои недостатки. Одним из таких способов является использование сертификата с несколькими именами в поле SubjectAltName сертификата, например www.example.com и www.example.org. Однако, длина поля SubjectAltName ограничена.

Другим способом является использование wildcard-сертификата, например *.example.org. Такой сертификат защищает все поддомены указанного домена, но только на заданном уровне. Под такой сертификат подходит www.example.org, но не подходят example.org и www.sub.example.org. Два вышеуказанных способа можно комбинировать. Сертификат может одновременно содержать и точное, и wildcard имена в поле SubjectAltName, например example.org и *.example.org.

Лучше поместить сведения о файле сертификата с несколькими именами и файле с его секретным ключом на уровне конфигурации http, чтобы все серверы унаследовали их единственную копию в памяти:

ssl_certificate     common.crt;
ssl_certificate_key common.key;

server {
    listen          443 ssl;
    server_name     www.example.com;
    ...
}

server {
    listen          443 ssl;
    server_name     www.example.org;
    ...
}

Как nginx обрабатывает TCP/UDP-сессии

Обработка клиентской TCP/UDP-сессии происходит последовательными фазами:

  • Post-accept. Первая фаза после принятия клиентского соединения. В этой фазе выполняется модуль ngx_stream_realip_module.
  • Pre-access. Предварительная проверка доступа. В этой фазе выполняются модули ngx_stream_limit_conn_module и ngx_stream_set_module.
  • Access. Ограничение доступа для клиента перед обработкой данных. В этой фазе выполняется модуль ngx_stream_access_module.
  • SSL. Терминирование TLS/SSL. В этой фазе выполняется модуль ngx_stream_ssl_module.
  • Preread. Чтение первых байт данных в буфер предварительного чтения для анализа, например модулем ngx_stream_ssl_preread_module, перед их обработкой.
  • Content. Обязательная фаза, в которой происходит обработка данных, как правило проксирование на группу серверов или отправка клиенту заданного значения.
  • Log. Заключительная фаза, в которой записывается результат обработки клиентской сессии. В этой фазе выполняется модуль ngx_stream_log_module.