Акция Панель управления ispmanager для VDS — первый месяц бесплатно
до 31.07.2026 Подробнее
Выберите продукт

Let’s Encrypt HTTP-01 в Nginx: почему ACME challenge даёт 403/404/timeout и как быстро починить

HTTP-01 проверка Let’s Encrypt часто ломается из‑за редиректа на HTTPS, неверного webroot, закрытого порта 80, AAAA/IPv6 или конфликта location в Nginx. Ниже — быстрые проверки снаружи, логи и рабочие конфиги для 403/404/timeout.
Let’s Encrypt HTTP-01 в Nginx: почему ACME challenge даёт 403/404/timeout и как быстро починить

Что именно проверяет Let’s Encrypt в HTTP-01

При проверке домена методом HTTP-01 центр сертификации ожидает, что ваш сервер отдаст «токен» по URL вида http://example.com/.well-known/acme-challenge/XYZ. ACME-клиент (Certbot, acme.sh, lego и другие) либо создаёт файл в каталоге сайта (webroot), либо временно поднимает обработчик, а Let’s Encrypt делает обычный HTTP-запрос по порту 80.

Ключевой момент: проверка начинается именно по HTTP. Редирект на HTTPS допустим, но он не должен ломать путь, уводить на другой хост/порт, попадать под авторизацию или правила WAF/CDN.

Почти все проблемы 403/404/timeout сводятся к одному: запрос к /.well-known/acme-challenge/ не попадает туда, где лежит токен, или вообще не доходит до вашего Nginx (DNS, IPv6, firewall, прокси).

Симптомы: 403, 404 и timeout — в чём разница

403 Forbidden

Запрос дошёл до сервера (или до внешнего прокси/WAF), но доступ запрещён. Типовые причины:

  • на /.well-known или на весь сайт висит запрет (deny all) или базовая авторизация;
  • проблемы с правами/доступом по пути до каталога webroot;
  • путь попал в другой location, который намеренно возвращает 403;
  • CDN/WAF блокирует запросы к /.well-known.

404 Not Found

Запрос дошёл, но файл не найден. Почти всегда это «ошибка маршрутизации»:

  • на HTTP (порт 80) используется не тот root;
  • ACME-клиент в режиме webroot пишет токен в другой каталог;
  • запрос перехватывает другой server_name (дефолтный сервер, соседний сайт);
  • приложение/фреймворк перехватывает /.well-known и отдаёт свой 404.

Timeout (Connection timed out / Timeout during connect)

Let’s Encrypt не смог подключиться к вашему серверу по HTTP. На практике это:

  • порт 80 закрыт в firewall (на сервере или в панели провайдера);
  • Nginx не слушает 80/tcp;
  • DNS указывает на другой IP;
  • есть запись AAAA, но IPv6 не настроен или по IPv6 закрыт порт 80;
  • порт 80 занят другим сервисом или проброшен «не туда» (NAT, балансировщик, контейнеры).

Проверка DNS A/AAAA и тестовый запрос curl к /.well-known/acme-challenge

Если нужен предсказуемый контроль сети и конфигурации (включая IPv4/IPv6, firewall и Nginx), удобнее выпускать и продлевать сертификаты на VDS.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Быстрая проверка «снаружи»: что видит Let’s Encrypt

Задача диагностики — пройти цепочку «DNS → IP → порт 80 → правильный server block → правильный файл». Лучше сначала добиться, чтобы тестовый файл стабильно отдавался по HTTP, и только потом гонять ACME-клиента.

1) Проверяем A/AAAA и куда реально резолвится домен

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

Если есть AAAA, Let’s Encrypt может пойти по IPv6. Тогда Nginx должен слушать [::]:80, а firewall должен пропускать 80/tcp по IPv6. Если вы не используете IPv6 осознанно, лучше привести запись AAAA и IPv6-настройки в соответствие (или временно убрать AAAA на время выпуска).

2) Проверяем, что порт 80 доступен

ss -lntp | grep ':80 '

Проверка с внешней стороны (с другого сервера/домашнего интернета):

nc -vz example.com 80

Если тут таймаут, это ещё не история про location: сначала чинится сеть, DNS, порт и маршрутизация.

3) Проверяем конкретно ACME URL тестовым файлом

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

mkdir -p /var/www/example.com/.well-known/acme-challenge
printf 'acme-test' > /var/www/example.com/.well-known/acme-challenge/ping

И проверьте отдачу по HTTP:

curl -i http://example.com/.well-known/acme-challenge/ping

Ожидаемо: HTTP/1.1 200 OK и тело acme-test. Если видите 301/302 — нормально, но проверьте, что редирект сохраняет путь и ведёт на доступный адрес.

Правильный Nginx-конфиг для HTTP-01: минимально надёжный вариант

Цель простая: гарантированно отдать /.well-known/acme-challenge/ по HTTP, даже если весь сайт принудительно уходит на HTTPS. Для этого location под challenge должен стоять выше общего редиректа.

Вариант 1: отдельный server на 80 с webroot и редиректом всего остального

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

    root /var/www/example.com;

    location ^~ /.well-known/acme-challenge/ {
        default_type text/plain;
        try_files $uri =404;
        allow all;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

Почему так:

  • ^~ повышает приоритет и не даёт regex-location «перехватить» challenge.
  • try_files $uri =404; заставляет Nginx искать реальный файл и не проксировать запрос в приложение.
  • listen [::]:80; закрывает типовой кейс с AAAA/IPv6.

Вариант 2: alias вместо root (когда webroot сложный)

Если основной root у сайта другой (например, фронтенд и бэкенд разнесены) или управляется приложением, удобнее выделить отдельный каталог под challenge:

location ^~ /.well-known/acme-challenge/ {
    alias /var/lib/acme-challenges/;
    default_type text/plain;
    try_files $uri =404;
    allow all;
}

Важно: при alias путь на диске считается без префикса location. То есть файл для URL /.well-known/acme-challenge/XYZ должен лежать как /var/lib/acme-challenges/XYZ.

Если сертификат нужен для продакшена (магазин, кабинет, API), не откладывайте нормальный выпуск и продление: на странице SSL-сертификаты можно подобрать сертификат под задачу и уровень валидации.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Почему получаем 404: типовые ловушки с webroot и server_name

Неверный webroot у клиента

В режиме webroot ACME-клиент кладёт токен строго в каталог, который вы ему указали. Если в Nginx на 80 стоит другой root (или у вас несколько server blocks), Let’s Encrypt увидит 404.

Практическое правило: сначала добейтесь, чтобы тестовый файл ping отдавался по HTTP. Если ping отдаётся, а проверка Let’s Encrypt — нет, значит клиент пишет токен в другое место (проверьте параметры webroot, путь в конфиге клиента и права записи).

Запрос попадает не в тот server block

Симптом: через curl по HTTP открывается «чужой» сайт или дефолтная страница. Причины обычно такие:

  • не совпадает server_name (особенно если проверяете и example.com, и www.example.com);
  • есть дефолтный сервер на 80, который перехватывает запросы;
  • конфиг изменили, но Nginx не перезагрузили.

Подсмотреть, какие блоки реально слушают 80 и где объявлен домен:

nginx -T | grep -n "listen 80"
nginx -T | grep -n "server_name" | grep example.com

Если домен неожиданно резолвится не на тот IP, начните с ревизии DNS. В сложных случаях помогает аккуратная инвентаризация доменной зоны и записей — см. материал про перенос домена и настройку DNS (EPP, NS и типовые ошибки).

Почему получаем 403: права, запреты и «безопасные» правила

Права на каталог и выполнение по пути

Nginx должен иметь возможность пройти по всем каталогам до файла. Даже если сам файл имеет 644, но какой-то каталог выше без execute-бита, будет 403.

Быстрая диагностика прав по всему пути:

namei -l /var/www/example.com/.well-known/acme-challenge/ping

Обычно каталоги должны быть 755 (или эквивалентно по ACL), а владелец/группа — соответствовать пользователю, от которого работает Nginx (часто www-data).

Случайный запрет на /.well-known

Иногда в конфиге есть «жёсткие» блокировки скрытых файлов/каталогов. Например:

location ~ /\. {
    deny all;
}

Само по себе правило полезное, но неаккуратные regex или дополнительные ограничения могут задеть /.well-known. Проверьте, нет ли запретов на well-known и нет ли «глобальных» deny/return 403, которые срабатывают раньше нужного location.

Basic auth на весь сайт

Если на HTTP включена авторизация, Let’s Encrypt не сможет пройти. Решение — исключить challenge из auth:

location ^~ /.well-known/acme-challenge/ {
    auth_basic off;
    try_files $uri =404;
    allow all;
}

Почему timeout: firewall, порт 80 и IPv6 AAAA

Открыт ли 80/tcp в firewall

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

nft list ruleset
ufw status verbose

Не забывайте про внешние политики: в панели провайдера или в облачном firewall входящий 80/tcp тоже должен быть разрешён. Классический кейс: «HTTPS работает, а HTTP нет», потому что открыт только 443.

IPv6: есть AAAA, но сервиса нет

Очень частая история: в DNS добавили AAAA, но:

  • Nginx слушает только listen 80; без listen [::]:80;;
  • по IPv6 закрыт 80/tcp;
  • IPv6-адрес на сервере не поднят или отличается от DNS.

Если IPv6 пока не нужен, быстрый способ убрать таймаут на валидации — временно удалить AAAA запись. Если IPv6 нужен, доведите конфиг и firewall до рабочего состояния и проверьте отдачу тестового файла по IPv6.

NAT/проброс портов и контейнеры

Если вы за reverse proxy, балансировщиком или публикуете сайт из контейнера, убедитесь, что 80/tcp приходит именно на тот Nginx (или сервис), где настроен location для challenge. Важно: Let’s Encrypt проверяет публичный endpoint, а не ваш внутренний контейнерный адрес.

Логи Nginx во время валидации Let’s Encrypt: коды 200/301/403/404

Редирект на HTTPS: как сделать правильно, чтобы HTTP-01 не ломался

Типовой редирект «всё на HTTPS» выглядит так:

location / {
    return 301 https://$host$request_uri;
}

Ломается HTTP-01, когда редирект объявлен так, что перехватывает /.well-known/acme-challenge/. Правило простое: сначала отдельный location под challenge, потом общий редирект.

Ещё одна ловушка — редирект на другой хост (например, на www), где challenge не настроен. Если вы подтверждаете и «голый» домен, и www, держите одинаковую обработку challenge для обоих имён в server_name.

Диагностика по логам Nginx: что смотреть в первую очередь

Во время запуска получения/обновления сертификата откройте логи и смотрите запросы в реальном времени:

tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log

Что проверять по факту:

  • Есть ли запросы к /.well-known/acme-challenge/ именно в момент валидации.
  • Какой код ответа возвращается (200/301/403/404).
  • Какой Host пришёл (example.com или www) и соответствует ли он вашему server_name.
  • Есть ли в error.log подсказки про права, отсутствие файлов, переполнение лимитов и т.п.

Если в логах нет никаких запросов, а клиент сообщает timeout, проблема находится до Nginx: DNS, внешний firewall, IPv6/AAAA, маршрутизация.

Чек-лист «починить за 10 минут»

  1. Проверьте, что домен резолвится на правильные IP: A и (если используется) AAAA.

  2. Убедитесь, что Nginx слушает 80 и (при наличии AAAA) [::]:80.

  3. Откройте входящий 80/tcp на сервере и во внешнем firewall/панели.

  4. Создайте тестовый файл /.well-known/acme-challenge/ping и добейтесь 200 по HTTP.

  5. Добавьте отдельный location ^~ /.well-known/acme-challenge/ перед редиректом на HTTPS.

  6. Сверьте webroot у ACME-клиента с root на 80 (или используйте alias для challenge).

  7. Во время проверки смотрите access.log и error.log, чтобы увидеть реальный код и куда попал запрос.

Когда HTTP-01 лучше не мучить

HTTP-01 объективно неудобен, если порт 80 закрыт по политике, домен живёт за сложным CDN/WAF, много фронтов и нестандартная маршрутизация. Тогда чаще выбирают подтверждение через DNS. Если вы автоматизируете выпуск wildcard-сертификатов или хотите полностью убрать зависимость от 80/tcp, пригодится статья про автоматизацию DNS-01 для wildcard-сертификатов.

Итоги

Ошибки 403/404/timeout при Let’s Encrypt HTTP-01 в Nginx почти всегда чинятся «по слоям»: DNS и доступность 80 порта, затем попадание в правильный server block, затем корректный location для /.well-known/acme-challenge/, и только потом права/ограничения. Как только у вас получается стабильно отдавать тестовый файл по HTTP через curl, выпуск сертификата перестаёт быть «магией certbot» и становится обычной задачей маршрутизации запросов.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину

Ошибка mount: wrong fs type, bad option, bad superblock в Debian/Ubuntu может означать и простую опечатку в имени раздела, и пробл ...
Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление

Если XFS-раздел внезапно стал доступен только для чтения, а сервер ушёл в emergency mode, главное — не спешить. Разберём безопасны ...
Debian/Ubuntu: как исправить Failed to fetch при apt update OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Failed to fetch при apt update

Ошибка Failed to fetch при apt update в Debian и Ubuntu обычно связана не с самим APT, а с DNS, сетью, зеркалом, прокси, временем ...