Настройка Let's Encrypt SSL/TLS для Nginx на Debian 12 и Ubuntu 24.04

10 мин чтения·Matthieu·nginxssltlslets-encryptcertbothttpssecurity|

Получение и автоматическое продление бесплатных TLS-сертификатов с помощью Certbot для Nginx на Debian 12 или Ubuntu 24.04. Настройка DNS, установка Certbot, редирект HTTP на HTTPS, усиление TLS, HTTP/2, HSTS и прекращение поддержки OCSP.

В этом руководстве разберем, как получить бесплатный TLS-сертификат от Let's Encrypt с помощью Certbot, настроить Nginx для работы по HTTPS и включить автоматическое продление. Каждый шаг содержит команду для проверки, чтобы ты мог убедиться в правильности настроек перед переходом к следующему этапу.

Если Nginx еще не установлен, начни с Установка Nginx на Debian 12 и Ubuntu 24.04 из официального репозитория. Общую картину по администрированию Nginx на VPS смотри в Администрирование Nginx на VPS.

Что нужно перед запросом сертификата?

Прежде чем Certbot сможет выпустить сертификат, твой домен должен указывать на IP-адрес сервера, а Nginx должен работать с server block (серверным блоком) для этого домена. Let's Encrypt проверяет владение доменом, отправляя HTTP-запрос на твой сервер. Если DNS не резолвится на твой VPS или Nginx не слушает порт, проверка не пройдет.

Тебе понадобится:

Настройка DNS-записей

Создай A-запись у своего DNS-провайдера:

Тип Имя Значение TTL
A example.com 203.0.113.10 300
AAAA example.com 2001:db8::1 300

Замени IP-адреса на реальные адреса своего сервера. Выставь низкий TTL (300 секунд) на время настройки, чтобы изменения распространялись быстро. Потом можно увеличить.

Проверка DNS-резолвинга

Подожди несколько минут после создания записей и проверь с локальной машины (не с сервера):

dig +short example.com A
dig +short example.com AAAA

В выводе должны быть IP-адреса твоего сервера. Если ничего не видно или IP другой, запись еще не распространилась. Подожди и попробуй снова.

Проверь, что Nginx отвечает на порту 80 с локальной машины:

curl -I http://example.com

Должен прийти ответ HTTP/1.1 200 OK с заголовком Server: nginx. Если соединение зависает, проверь правила файрвола.

Как установить Certbot на Debian 12 и Ubuntu 24.04?

Установи Certbot и его плагин для Nginx из репозитория дистрибутива через apt. Плагин для Nginx позволяет Certbot автоматически изменять серверные блоки для включения TLS.

sudo apt update
sudo apt install certbot python3-certbot-nginx -y

Проверь установку:

certbot --version

На Debian 12 установится Certbot 2.1.0. На Ubuntu 24.04, Certbot 2.9.0. Обе версии подходят для всего, что описано в этом руководстве.

Обрати внимание: если ты установил Nginx из официального репозитория nginx.org (как рекомендуется в Установка Nginx на Debian 12 и Ubuntu 24.04 из официального репозитория), плагин Certbot для Nginx будет работать без дополнительной настройки. Он находит серверные блоки в /etc/nginx/conf.d/ и /etc/nginx/sites-enabled/.

Как получить сертификат Let's Encrypt для Nginx?

Запусти certbot --nginx с указанием твоего домена. Certbot свяжется с Let's Encrypt, докажет, что ты контролируешь домен через HTTP-01 challenge (проверку), получит сертификат и изменит серверный блок Nginx для его использования. Весь процесс занимает около 30 секунд.

sudo certbot --nginx -d example.com -d www.example.com

Certbot запросит адрес электронной почты (для напоминаний о продлении) и согласие с условиями использования. Затем он:

  1. Размещает файл HTTP-01 challenge в web root (корневой директории)
  2. Просит Let's Encrypt его проверить
  3. Скачивает подписанный сертификат
  4. Изменяет серверный блок Nginx, добавляя директивы TLS
  5. Перезагружает Nginx

Проверь, что сертификат был выпущен:

sudo ls -la /etc/letsencrypt/live/example.com/

Ожидаемый вывод:

lrwxrwxrwx 1 root root  ... cert.pem -> ../../archive/example.com/cert1.pem
lrwxrwxrwx 1 root root  ... chain.pem -> ../../archive/example.com/chain1.pem
lrwxrwxrwx 1 root root  ... fullchain.pem -> ../../archive/example.com/fullchain1.pem
lrwxrwxrwx 1 root root  ... privkey.pem -> ../../archive/example.com/privkey1.pem

Это симлинки. fullchain.pem, твой сертификат плюс цепочка промежуточных CA. privkey.pem, закрытый ключ.

Проверь, что Nginx работает с новой конфигурацией:

sudo nginx -t && sudo systemctl status nginx

nginx -t проверяет синтаксис конфигурации. Если выводится test is successful, конфигурация валидна.

Что именно Certbot меняет в конфигурации Nginx?

Certbot добавляет несколько строк в твой серверный блок. Вот что он вставляет (строки с пометкой # managed by Certbot):

server {
    server_name example.com www.example.com;

    listen 443 ssl;
    listen [::]:443 ssl;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    # ... your existing location blocks ...
}

Файл options-ssl-nginx.conf содержит настройки TLS по умолчанию от Certbot. Мы заменим их на более строгие параметры в разделе по усилению безопасности ниже.

Certbot также создает второй серверный блок для редиректа HTTP на HTTPS. Мы улучшим этот редирект в следующем разделе.

Посмотреть, что именно изменилось, можно так:

sudo diff /etc/nginx/conf.d/example.com.conf /etc/nginx/conf.d/example.com.conf.bak 2>/dev/null || echo "No backup found. Certbot modifies in place."

Как настроить редирект HTTP на HTTPS в Nginx?

Весь HTTP-трафик нужно перенаправлять на HTTPS через 301-й редирект (permanent). Certbot может добавить его автоматически, но по умолчанию использует конструкцию if внутри существующего серверного блока. В Nginx это антипаттерн. Отдельный серверный блок, чище и надежнее.

Замени редирект от Certbot на отдельный серверный блок. Отредактируй конфигурационный файл (путь зависит от твоей настройки; обычно это /etc/nginx/conf.d/example.com.conf):

# HTTP -> HTTPS redirect (separate server block, not an if-statement)
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

Этот блок можно разместить в том же файле, что и HTTPS-блок, или в отдельном файле. Убедись, что ты удалил сгенерированный Certbot редирект, чтобы не было дублей.

Проверка и перезагрузка:

sudo nginx -t
sudo systemctl reload nginx

Проверь редирект с локальной машины:

curl -I http://example.com

Ожидаемый вывод:

HTTP/1.1 301 Moved Permanently
Location: https://example.com/

Как усилить настройки TLS для продакшен-сервера?

Стандартная конфигурация TLS от Certbot (options-ssl-nginx.conf) намеренно консервативна. Для продакшена нужны более строгие настройки. Мы будем следовать профилю Intermediate от Mozilla из SSL Configuration Generator, который обеспечивает баланс между безопасностью и совместимостью с клиентами вплоть до Firefox 27, Chrome 31 и Android 4.4.2.

Создай файл-сниппет, который можно подключать через include из каждого серверного блока:

sudo nano /etc/nginx/snippets/tls-params.conf

Добавь следующее:

# TLS protocol versions, TLS 1.2 and 1.3 only
# TLS 1.0 and 1.1 are deprecated (RFC 8996)
ssl_protocols TLSv1.2 TLSv1.3;

# Ciphers, Mozilla Intermediate profile (January 2026)
# Source: https://ssl-config.mozilla.org/
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;

# DH parameters, 2048-bit, RFC 7919 ffdhe2048
ssl_dhparam /etc/nginx/dhparam.pem;

# Session settings
ssl_session_timeout 1d;
ssl_session_cache shared:TLS:10m;
ssl_session_tickets off;

# HSTS, tell browsers to always use HTTPS (2 years)
# Only add includeSubDomains if ALL subdomains use HTTPS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;

# Hide Nginx version in error pages and headers
server_tokens off;

Сгенерируй файл DH-параметров (это займет несколько секунд):

sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048

Проверь, что файл создан:

sudo ls -la /etc/nginx/dhparam.pem

Теперь обнови HTTPS-блок, чтобы использовать эти настройки вместо дефолтных от Certbot. Удали строку include /etc/letsencrypt/options-ssl-nginx.conf; и строку ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;. Замени их на:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # TLS hardening (replaces Certbot defaults)
    include snippets/tls-params.conf;

    # ... your location blocks ...
}

Проверка и перезагрузка:

sudo nginx -t
sudo systemctl reload nginx

Какие версии TLS и шифры использовать?

Mozilla публикует три профиля TLS. Вот сравнение:

Профиль Протоколы Самый старый совместимый клиент Для чего
Modern Только TLS 1.3 Firefox 63, Chrome 70, Android 10 Сервисы, где все клиенты свежие
Intermediate TLS 1.2 + 1.3 Firefox 27, Chrome 31, Android 4.4 Веб-серверы общего назначения
Old TLS 1.0 + 1.1 + 1.2 + 1.3 Firefox 1, Chrome 1, IE 8 Только legacy-системы

Используй Intermediate, если нет конкретной причины выбрать другой. Он покрывает 99,9%+ актуальных браузеров и исключает слабые протоколы. TLS 1.0 и 1.1 были официально признаны устаревшими в RFC 8996 в марте 2021.

Список шифров в нашем сниппете использует только AEAD-шифры (GCM и ChaCha20-Poly1305). ssl_prefer_server_ciphers off позволяет клиенту выбрать предпочтительный шифр. Это рекомендация Mozilla, потому что современные клиенты делают лучший выбор, чем статическая серверная настройка.

Поддерживает ли Let's Encrypt OCSP stapling?

Нет. Let's Encrypt отключил свой OCSP-сервис 6 августа 2025 года. Сертификаты, выпущенные после мая 2025, не содержат URL OCSP-респондера. Статус отзыва теперь публикуется только через CRL (Certificate Revocation Lists, списки отозванных сертификатов). Если ты используешь только сертификаты Let's Encrypt, удали все директивы ssl_stapling из конфигурации Nginx.

Хронология:

  1. Декабрь 2024. Let's Encrypt объявил план по прекращению OCSP.
  2. 30 января 2025. Сертификаты с расширением OCSP Must-Staple начали выдавать ошибки.
  3. 7 мая 2025. Новые сертификаты перестали включать URL OCSP-респондера. Вместо них добавлены URL CRL.
  4. 6 августа 2025. OCSP-сервис полностью остановлен. Все ранее выпущенные сертификаты с OCSP URL к этому моменту истекли.

Если в каком-то гайде или сниппете видишь ssl_stapling on; или ssl_stapling_verify on;, для пользователей Let's Encrypt это устаревший совет. Эти директивы безвредны (Nginx молча их игнорирует, когда нет URL OCSP-респондера), но создают лишний шум. Удали их.

Если используешь сертификаты от другого CA, который по-прежнему предоставляет OCSP, эти директивы остаются актуальными для тех сертификатов.

Почему Let's Encrypt отказался от OCSP? Две причины. OCSP создавал угрозу приватности: каждый раз, когда браузер проверял отзыв сертификата через OCSP, CA узнавал, какой сайт посещается и с какого IP. На пике OCSP-сервис Let's Encrypt обрабатывал 340 миллиардов запросов в месяц. Переход на CRL устраняет эту утечку. CRL загружаются браузерами целиком, поэтому отдельный запрос к CA при каждом посещении не нужен.

Как включить HTTP/2 в Nginx?

HTTP/2 мультиплексирует запросы через одно соединение, снижая задержки. Если ты установил Nginx из официального репозитория nginx.org (версия 1.25.1 или новее), включи HTTP/2 с помощью директивы http2 внутри серверного блока.

Добавь http2 on; внутри HTTPS-блока:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;

    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include snippets/tls-params.conf;

    # ... your location blocks ...
}

Директива http2 on; заменила устаревший синтаксис listen 443 ssl http2; в Nginx 1.25.1. Старый синтаксис по-прежнему работает, но выводит предупреждения об устаревании в лог ошибок. Если у тебя пакетный Nginx из репозитория дистрибутива (1.22 на Debian 12, 1.24 на Ubuntu 24.04), используй старый синтаксис listen 443 ssl http2;.

Проверка и перезагрузка:

sudo nginx -t
sudo systemctl reload nginx

Проверь, что HTTP/2 активен:

curl -I --http2 -s https://example.com | head -1

Ожидаемый вывод:

HTTP/2 200

Если видишь HTTP/1.1, проверь, что http2 on; находится внутри правильного серверного блока и что твоя версия Nginx его поддерживает.

Как работает автоматическое продление сертификатов?

Сертификаты Let's Encrypt действуют 90 дней. Certbot устанавливает systemd timer (certbot.timer), который дважды в день проверяет, не истекает ли какой-либо сертификат в ближайшие 30 дней. Если да, продление происходит автоматически. Настраивать cron-задачу не нужно.

Проверь, что таймер активен:

systemctl status certbot.timer

Должен быть статус Active: active (waiting) и строка со временем следующего срабатывания.

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

systemctl list-timers certbot.timer

Здесь показаны поля NEXT и LAST, время следующего и последнего запуска.

Тестовое продление без реального продления

Запусти dry-run, чтобы проверить, что процесс продления работает от начала до конца:

sudo certbot renew --dry-run

Эта команда связывается со staging-сервером Let's Encrypt и имитирует полное продление. Если выводится Congratulations, all simulated renewals succeeded, настройка верна.

Настройка deploy hook для перезагрузки Nginx

Когда Certbot продлевает сертификат, Nginx нужно перезагрузить, чтобы он подхватил новые файлы. Настрой deploy hook, который срабатывает только при успешном продлении:

sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Добавь:

#!/bin/bash
/usr/bin/systemctl reload nginx

Сделай файл исполняемым:

sudo chmod 700 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Проверь права доступа:

ls -la /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Ожидаемый вывод показывает -rwx------ (только root может читать и выполнять).

Директория deploy запускает скрипты только после успешного продления, а не при каждом срабатывании таймера. Это избавляет от лишних перезагрузок.

Как проверить правильность настройки TLS?

После выполнения всех шагов выше, запусти эти команды для проверки. Каждая из них проверяет отдельный аспект настройки.

Проверка цепочки сертификатов с помощью OpenSSL

С локальной машины:

openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates -subject -issuer

Ожидаемый вывод:

notBefore=Mar 19 00:00:00 2026 GMT
notAfter=Jun 17 00:00:00 2026 GMT
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R12

Обрати внимание: дата notAfter должна быть примерно через 90 дней от выпуска. Издатель должен быть Let's Encrypt. Текущие RSA-промежуточные сертификаты: R12 и R13. Для сертификатов ECDSA ищи E7 или E8.

Проверка версии TLS и используемого шифра

openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | grep -E "Protocol|Cipher"

Ожидаемый вывод:

    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384

Если видишь TLSv1.2, это тоже нормально. Зависит от того, какую версию предпочитает твой локальный OpenSSL.

Проверка HTTPS-заголовков

curl -I https://example.com

Ищи:

HTTP/2 200
strict-transport-security: max-age=63072000; includeSubDomains

Если strict-transport-security отсутствует, проверь строку add_header в TLS-сниппете. Параметр always гарантирует отправку заголовка даже при ошибочных ответах.

Сводка команд для проверки

Команда Что проверяет На что смотреть
dig +short example.com DNS-резолвинг IP твоего сервера
curl -I http://example.com HTTP-редирект 301 с Location: https://
curl -I https://example.com HTTPS + заголовки HTTP/2 200, заголовок HSTS
openssl s_client -connect ... Цепочка сертификатов, версия TLS Издатель Let's Encrypt, TLSv1.2 или 1.3
certbot renew --dry-run Процесс продления all simulated renewals succeeded
systemctl status certbot.timer Таймер автопродления active (waiting)

Проверка через SSL Labs

Для внешнего аудита отправь свой домен в SSL Labs Server Test. С конфигурацией из этого руководства ты должен получить оценку A или A+. Для A+ нужен HSTS, который мы включили в TLS-сниппете.

Полная конфигурация Nginx

Вот итоговый серверный блок со всеми настройками из этого руководства:

# HTTP -> HTTPS redirect
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

# HTTPS server block
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;

    server_name example.com www.example.com;

    # Let's Encrypt certificate
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # TLS hardening
    include snippets/tls-params.conf;

    root /var/www/example.com/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    # Deny access to hidden files
    location ~ /\. {
        deny all;
    }
}

И TLS-сниппет в /etc/nginx/snippets/tls-params.conf:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
ssl_dhparam /etc/nginx/dhparam.pem;

ssl_session_timeout 1d;
ssl_session_cache shared:TLS:10m;
ssl_session_tickets off;

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
server_tokens off;

Дополнительные заголовки безопасности (Content-Security-Policy, X-Frame-Options, Permissions-Policy) смотри в Укрепление безопасности Nginx на Ubuntu и Debian.

Что-то пошло не так?

Certbot выдает «Could not automatically find a matching server block» Certbot ищет директиву server_name, совпадающую с указанным через -d доменом. Убедись, что файл серверного блока находится в /etc/nginx/conf.d/ или /etc/nginx/sites-enabled/ и содержит server_name example.com;.

Certbot выдает «Connection refused» или «Challenge failed» Порт 80 должен быть открыт. Проверь файрвол:

sudo ufw status          # if using UFW
sudo nft list ruleset    # if using nftables

Также проверь, что Nginx слушает порт 80:

sudo ss -tlnp | grep ':80'

«SSL: error» в логе ошибок Nginx после изменения TLS-настроек Скорее всего, синтаксическая ошибка в строке шифров или неверный путь к файлу. Проверь:

sudo nginx -t
sudo journalctl -u nginx -n 20 --no-pager

Браузер показывает «NET::ERR_CERT_DATE_INVALID» Возможно, сертификат истек. Проверь срок действия:

sudo certbot certificates

Если сертификат просрочен, принудительно продли:

sudo certbot renew --force-renewal

certbot renew --dry-run завершается с ошибкой Частая причина: DNS домена больше не указывает на этот сервер или порт 80 заблокирован. Для продления Certbot нужен HTTP-01 доступ.

HTTP/2 не работает Проверь версию Nginx: nginx -v. Если версия ниже 1.25.1, используй listen 443 ssl http2; вместо отдельной директивы http2 on;. Если версия 1.25.1+, убедись, что http2 on; находится внутри блока server (а не http или location).