Установка · Ручная настройка

Настройка nginx

На этой странице вручную пишутся файлы nginx, которые создаёт orionplace domain add: сайт на порту 80 для проверки сертификата, сертификат Let's Encrypt и HTTPS-сайт, который передаёт запросы службе витрины. В примерах — домен shop.example.com и витрина на порту 3000.

Как это работает

nginx принимает публичные запросы на портах 80 и 443. Порт 80 только отвечает на проверки сертификата и перенаправляет на HTTPS. HTTPS-сайт передаёт всё витрине на 127.0.0.1 и сообщает ей домен и адрес покупателя в заголовках запроса. Новые файлы лежат рядом с вашими сайтами и ни один из них не меняют.

1 Требования

Нужны nginx с поддержкой HTTPS, certbot и запущенная служба витрины, см. «Запуск витрины как службы». DNS-запись A домена, а также AAAA, если у сервера есть IPv6, должна указывать на этот сервер, а порты 80 и 443 должны быть открыты в файрволе сервера и в файрволе облачного провайдера.

Проверьте, что nginx поддерживает HTTPS, и посмотрите, какие файлы и папки он подключает:

bash
nginx -V 2>&1 | grep -o http_ssl_module
nginx -T 2>/dev/null | grep -E "^ *include"

Кладите файлы в папку, которую подключает блок http: /etc/nginx/conf.d на Debian, Ubuntu, семействе RHEL, Fedora, Amazon Linux и openSUSE, /etc/nginx/http.d на Alpine. В примерах — conf.d.

Arch Linux по умолчанию не подключает ни одной папки. Добавьте эту строку внутрь блока http в /etc/nginx/nginx.conf:

nginx
    include /etc/nginx/conf.d/*.conf;

2 Общие настройки

Один раз на сервер, на уровне http: этот map пропускает к витрине соединения, которые просят upgrade (WebSocket). Если в другом вашем файле уже есть такой map под другим именем, можно указать в сайте его.

/etc/nginx/conf.d/storefront-common.conf
map $http_upgrade $storefront_connection_upgrade {
    default upgrade;
    ''      close;
}

Длинные имена доменов могут остановить nginx с ошибкой could not build server_names_hash. Тогда добавьте эту строку один раз в тот же файл, если её ещё не задаёт другой ваш файл: дважды nginx её не принимает.

nginx
server_names_hash_bucket_size 128;

3 Сайт на порту 80

Создайте папку, куда certbot кладёт файлы проверки:

bash
sudo mkdir -p /var/www/letsencrypt/.well-known/acme-challenge

Затем сохраните файл сайта. Он отвечает на проверки сертификата и показывает короткое уведомление, пока HTTPS не готов:

/etc/nginx/conf.d/shop.example.com.conf
server {
    listen 80;
    listen [::]:80;
    server_name shop.example.com www.shop.example.com;
    location ^~ /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;
        default_type text/plain;
    }
    location / {
        default_type text/plain;
        return 503 "The storefront for shop.example.com is being set up.";
    }
}

Проверьте конфигурацию и загрузите её (на Alpine: sudo rc-service nginx reload):

bash
sudo nginx -t
sudo systemctl reload nginx

4 Сертификат

Получите один сертификат для домена и его имени с www. certbot кладёт файлы проверки в папку выше, поэтому nginx продолжает работать:

bash
sudo certbot certonly --webroot -w /var/www/letsencrypt \
  --cert-name shop.example.com -d shop.example.com -d www.shop.example.com \
  --agree-tos --email you@example.com

Сертификат действует 90 дней. Пакет certbot продлевает его по таймеру; на Alpine добавьте ежедневное задание cron с certbot renew --quiet. После продления nginx нужно перезагрузить, чтобы он взял новый сертификат: добавьте deploy-хук (на Alpine с rc-service nginx reload), проверьте таймер и пробное продление:

bash
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
printf '#!/bin/sh\nnginx -t && systemctl reload nginx\n' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx
sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx
systemctl list-timers | grep -i certbot
sudo certbot renew --dry-run

5 HTTPS-сайт

Замените файл сайта полной версией. Порт 80 теперь перенаправляет на HTTPS, www.shop.example.com — на shop.example.com, а HTTPS-сайт передаёт запросы витрине:

/etc/nginx/conf.d/shop.example.com.conf
server {
    listen 80;
    listen [::]:80;
    server_name shop.example.com www.shop.example.com;
    location ^~ /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;
        default_type text/plain;
    }
    location / {
        return 301 https://shop.example.com$request_uri;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name www.shop.example.com;
    ssl_certificate /etc/letsencrypt/live/shop.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/shop.example.com/privkey.pem;
    return 301 https://shop.example.com$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name shop.example.com;
    ssl_certificate /etc/letsencrypt/live/shop.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/shop.example.com/privkey.pem;
    client_max_body_size 64m;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Front-Domain $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Init-Ip $remote_addr;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $storefront_connection_upgrade;
        proxy_buffering off;
        proxy_buffer_size 32k;
        proxy_buffers 8 32k;
        proxy_busy_buffers_size 64k;
        proxy_read_timeout 120s;
    }
}
СтрокаЧто делает
Host, X-Forwarded-HostДомен, который открыл покупатель.
X-Front-DomainПо этому домену витрина выбирает ваш персональный бэкенд. nginx всегда перезаписывает его запрошенным доменом, поэтому покупатель не может прислать другое значение.
X-Forwarded-ProtoСообщает витрине, что страница открыта по HTTPS.
X-Real-IP, X-Forwarded-For, X-Init-IpАдрес покупателя; витрина передаёт его вашему бэкенду.
Upgrade, ConnectionПропускает соединения, которые просят upgrade (WebSocket), вместе с map из «Общих настроек».
proxy_buffering offОтдаёт ответ покупателю по мере поступления, не дожидаясь всей страницы.
proxy_buffer_size 32kМесто для больших заголовков ответа. Витрина на SvelteKit передаёт предзагрузку скриптов одним длинным заголовком; с размером по умолчанию 4 КБ nginx отвечает 502.
proxy_read_timeout 120sЖдёт медленный ответ до 120 секунд.
client_max_body_size 64mПринимает загрузки до 64 МБ, например фото к отзывам и аватары; по умолчанию у nginx 1 МБ.

nginx старше 1.25.1 не знает http2 on;: удалите эти строки и допишите http2 в строки listen порта 443. nginx, собранному без HTTP/2, не нужно ни то, ни другое.

nginx
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

6 Рядом с существующими сайтами

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

bash
sudo nginx -T 2>/dev/null | grep -nE "server_name|listen|# configuration file"

Каждый домен может обслуживать только один сайт. Если другой файл перечисляет shop.example.com или www.shop.example.com, nginx предупреждает conflicting server name и использует только один из них: уберите имя из одного из файлов. Маску вроде *.example.com в другом сайте менять не нужно: точные имена важнее неё.

Если другой сайт слушает конкретный адрес, например listen 203.0.113.5:443 ssl;, запросы на этот адрес nginx отдаёт только сайтам с такой же строкой listen: добавьте её и в эти блоки server. Если у другого сайта есть listen [::]:80 ipv6only=off;, удалите здесь listen 80;: та строка уже принимает и IPv4.

7 Проверка

Проверьте конфигурацию, загрузите её и откройте витрину:

bash
sudo nginx -t
sudo systemctl reload nginx
curl -I https://shop.example.com/

Ответ со статусом 200 означает, что витрина в сети. 502 — nginx не достучался до витрины: проверьте службу и её порт. 503 от витрины — домен не добавлен в проект.