# nginx

## Що це таке

nginx — це програма з відкритим кодом, яка приймає HTTP-з'єднання і на кожен
запит або відповідає файлами з диска, або передає його іншому серверу й
повертає клієнтові отриману відповідь. Назву вимовляють як «engine x», а
власні сторінки проєкту пишуть її також NGINX. Автор програми — Ігор Сисоєв, і
перша публічна версія, 0.1.0, вийшла 4 жовтня 2004 року. Користуватися нею
можна безкоштовно за двопунктовою ліцензією BSD, а F5, Inc. продає корпоративні
дистрибутиви, підтримку й навчання.

Проєкт перелічує шість ролей: HTTP-сервер, зворотний проксі, кеш вмісту,
балансувальник навантаження, проксі для TCP і UDP та поштовий проксі. На
вебхостингу nginx зазвичай виконує одну або обидві з двох робіт: віддає
статичні файли з диска і стоїть перед сервером застосунку, де завершує TLS і
передає кожен запит усередину.

Під час запуску nginx створює один головний процес (master) і кілька робочих
(worker). Головний читає й перевіряє конфігурацію та наглядає за робочими.
Робочі процеси працюють від імені непривілейованого користувача й виконують
усю обробку запитів. Кожен із них використовує цикл подій, побудований на
epoll у Linux і kqueue у FreeBSD, тож кілька робочих процесів можуть тримати
відкритими тисячі з'єднань одночасно. Директива worker_processes задає їхню
кількість — або фіксованим числом, або значенням auto, тобто по одному на ядро
процесора, — а перелік можливостей проєкту оцінює 10 000 неактивних
keep-alive-з'єднань приблизно у 2,5 МБ пам'яті.

Конфігурація — це текстовий файл із директив. Проста директива складається з
назви й параметрів і закінчується крапкою з комою. Блокова директива обгортає
інші директиви у фігурні дужки, а блок, що може містити директиви, називають
контекстом. Контексти events і http розташовані на верхньому рівні. Усередині
http кожен блок server описує один віртуальний сервер, який обирають за
адресою й портом, де він слухає, а тоді за іменем хоста, яке запитав клієнт.
Усередині блоку server кожен блок location визначає, що робити з певною
родиною шляхів запиту.

## Чому це важливо

Вебсервер — єдина програма, з якою стикається кожен відвідувач, тож його
поведінка під навантаженням і його типові налаштування стосуються всіх. На
користь nginx говорять дві властивості.

Перша — те, як дешево він тримає повільні з'єднання. Буферизацію відповідей
від сервера за проксі ввімкнено за замовчуванням: nginx забирає відповідь
бекенда так швидко, як бекенд її видає, записує її в тимчасовий файл, коли
вона не вміщається в пам'ять, а тоді віддає клієнтові в тому темпі, який
клієнт витримує. Сервер застосунку вже вільний для наступного запиту, поки
телефон зі слабким сигналом ще завантажує попередню відповідь.

Друга — зміни конфігурації набирають чинності без утрати запитів. Відредагована
конфігурація діє лише після перезавантаження (reload) або перезапуску. Під час
reload головний процес перевіряє нову конфігурацію й пробує її застосувати.
Якщо це вдається, стартують нові робочі процеси з нею, а старі перестають
приймати з'єднання, завершують запити, які вже обробляють, і виходять. Якщо
ні, головний процес відкочує зміни й далі обслуговує зі старою конфігурацією.
Проєкт також згадує оновлення самого виконуваного файлу без переривання
клієнтів.

Платити за це доводиться мовою конфігурації: саме її правила пріоритету й
успадкування ховають більшість несподіванок. Для шляху запиту nginx спершу
знаходить location із найдовшим збіжним префіксом і запам'ятовує його, потім
перебирає location із регулярними виразами в тому порядку, в якому вони стоять
у файлі, і бере перший, що збігся. Запам'ятований префікс лишається запасним
варіантом на випадок, коли регулярні вирази не дали результату. Location,
позначений `=`, вимагає точного збігу шляху й одразу завершує пошук, а `^~` на
найдовшому префіксі велить nginx пропустити регулярні вирази. Деякі директиви,
серед них add_header і proxy_set_header, успадковуються з охопного рівня лише
тоді, коли поточний рівень не задає власних.

Сертифікати довго надходили від ACME-клієнта, що працював поруч із nginx.
Тепер проєкт пропонує також модуль ngx_http_acme_module, який постачають
окремо від основної збірки.

## Як це виглядає на практиці

Дві частини попередньої хостингової роботи компанії стосуються nginx, і в обох
він ділив сервери з Apache, а не замінював його.

Перша —
[сорок клієнтських вебсайтів, які мали бути швидкими й завжди доступними](/uk/portfolio/administered-40-websites-on-ubuntu-linux-hosting-servers-53/).
Вони стояли на серверах з Ubuntu Linux, де використовували і Apache, і nginx.
Робота полягала в тому, щоб писати їхню конфігурацію, налаштовувати швидкодію
й доглядати за ними, поки ними користувалися справжні відвідувачі, і всі сорок
лишалися високодоступними. Два вебсервери в одному парку означають кожне
правило двома діалектами: віртуальний хост Apache — це блок server у nginx, і
виправлення, знайдене для одного, треба перекласти для іншого.

Друга тривала з 2015 до 2022 року.
[Автоматизація на ACME взяла на себе сертифікати ста застосунків Docker](/uk/portfolio/automated-ssl-tls-certificate-creation-for-100-docker-41/):
вона видавала кожен сертифікат, поновлювала його ще до кінця строку дії й
установлювала на хости з Ubuntu без участі людини, і на тих хостах так само
працювали і Apache, і nginx. Для nginx оновлений файл на диску — лише половина
такої зміни. Сертифікат, указаний звичайним шляхом, nginx читає, коли
завантажує конфігурацію, а документація зазначає, що лише ім'я файлу, складене
зі змінних, змушує його завантажувати сертифікат заново під час кожного
TLS-рукостискання. Тож поновлення, яке записує нові файли, доходить до
відвідувачів лише після reload, і будь-яке автоматичне поновлення для nginx
мусить ним завершуватися.

## Де виникають проблеми

**Заголовок, заданий для всього сайту, зникає на одному шляху.** Location, що
має хоч один власний add_header, не успадковує заголовків із рівнів server чи
http, тож один заголовок кешування, доданий до окремого location, непомітно
скасовує всі заголовки безпеки над ним. proxy_set_header підкоряється тому
самому правилу. До того ж add_header діє лише для кодів успіху й
перенаправлення, як-от 200, 301 і 304, якщо не додати `always`, тож сторінки
помилок ідуть без заголовків. Тримайте спільні заголовки в одному файлі, який
підключає include на кожному рівні, що задає власні, або з версії 1.29.3
задайте `add_header_inherit merge;`, додайте `always` там, де вони потрібні
відповідям із помилками, і перевірте кожен шлях через `curl -sI`.

**На запити до одного сайту відповідає інший.** nginx спершу зіставляє адресу
й порт, а тоді заголовок Host зі server_name. Коли ім'я невідоме всім блокам
server на цьому порту, запит отримує сервер за замовчуванням — перший у
списку, якщо інший не позначено default_server. Друкарська помилка в
server_name нового сайту тихо віддає сторінки старого сусіда. Оголосіть явний
сервер за замовчуванням, який на невідомі імена відповідає `return 444`, тобто
закриває з'єднання, щоб помилка виявлялася як помилка, а не як чужий сайт.

**Завантаження файлів падають з 413 Request Entity Too Large.**
client_max_body_size за замовчуванням дорівнює 1 МБ, і nginx відхиляє більше
тіло запиту ще до того, як його побачить застосунок. Підніміть ліміт у тому
location, що приймає завантаження, а не для всього сервера, і тримайте його
узгодженим із власним лімітом застосунку.

**Бекенд будує посилання на хибний хост або отримує хибний шлях.** Якщо не
вказати інакше, nginx надсилає серверу за проксі заголовок Host, складений з
адреси в proxy_pass, наприклад `127.0.0.1:8080`, тож застосунок генерує
перенаправлення й абсолютні посилання на цю адресу. Задайте
`proxy_set_header Host $host;` у location, що проксіює запити. Шляхи залежать
від форми proxy_pass: записаний з URI, навіть з одним `/`, він замінює ту
частину шляху, що збіглася з location, а без URI передає шлях без змін.
Обирайте форму свідомо й перевірте вкладений шлях.

**Клієнти й далі отримують стару конфігурацію або старий сертифікат.**
Відредагований файл чи сертифікат, поновлений уночі, не впливає на запущений
nginx до reload, а reload зламаної конфігурації лишає на місці стару.
Перевіряйте конфігурацію перед кожним reload, тоді читайте журнал помилок і
дивіться, що клієнти отримують насправді:

```sh
nginx -t && nginx -s reload
openssl s_client -connect example.com:443 \
  -servername example.com </dev/null |
  openssl x509 -noout -enddate
```

## Що почитати далі

- [nginx documentation](https://nginx.org/en/docs/)
  — покажчик проєкту з посібниками й довідниками директив для кожного модуля.
- [Beginner's Guide](https://nginx.org/en/docs/beginners_guide.html)
  — головний і робочі процеси, перезавантаження та будова конфігурації.
- [How nginx processes a request](https://nginx.org/en/docs/http/request_processing.html)
  — як блок server обирають за адресою, портом і заголовком Host.
- [Module ngx_http_core_module](https://nginx.org/en/docs/http/ngx_http_core_module.html)
  — порядок зіставлення location, server_name і client_max_body_size.
- [Module ngx_http_proxy_module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html)
  — proxy_pass, типові заголовки, тайм-аути й буферизація відповідей.
- [Module ngx_http_headers_module](https://nginx.org/en/docs/http/ngx_http_headers_module.html)
  — add_header, коди відповідей, для яких він діє, і його правило успадкування.

<https://engineer.company/uk/glossary/nginx/>
