Обычный HTTP-challenge для Let's Encrypt не поддерживает Wildcard-сертификаты (*.example.com). Для Wildcard нужен DNS-01 challenge — подтверждение через TXT-запись в DNS.
Как работает DNS-01 challenge
- Certbot запрашивает сертификат у Let's Encrypt
- Let's Encrypt даёт случайный токен
- Certbot просит добавить TXT-запись:
_acme-challenge.example.com - Let's Encrypt проверяет TXT-запись через DNS
- Сертификат выдаётся
Вручную через certbot
# Запрос Wildcard сертификата
certbot certonly --manual --preferred-challenges dns -d "*.example.com" -d "example.com"
# Certbot покажет TXT-запись для добавления:
# _acme-challenge.example.com. 300 IN TXT "TOKEN"
# Добавьте запись в DNS, затем нажмите Enter
# Сертификат будет в /etc/letsencrypt/live/example.com/
Автоматически через acme.sh + Cloudflare API
># Установка acme.sh
curl https://get.acme.sh | sh
# Настройка Cloudflare API
export CF_Token="YOUR_CLOUDFLARE_API_TOKEN"
export CF_Account_ID="YOUR_ACCOUNT_ID"
# Выпуск Wildcard сертификата
~/.acme.sh/acme.sh --issue --dns dns_cf -d "*.example.com" -d "example.com" --keylength ec-256
# Установка в Nginx
~/.acme.sh/acme.sh --install-cert -d "*.example.com" --key-file /etc/nginx/ssl/example.key --fullchain-file /etc/nginx/ssl/example.crt --reloadcmd "systemctl reload nginx"
Поддерживаемые DNS-провайдеры в acme.sh
acme.sh поддерживает автоматическое обновление через API более 100 DNS-провайдеров: Cloudflare, Route 53, DigitalOcean, Namecheap, Google Cloud DNS и другие.
Альтернатива acme.sh: certbot с плагином certbot-dns-cloudflare
Если привычнее работать через certbot, а не через acme.sh, DNS-01 для Cloudflare доступен через официальный плагин — не нужно вручную писать скрипт с curl-запросами к API.
# Установка плагина
apt install python3-certbot-dns-cloudflare -y
# Файл с токеном, права должны быть 600
echo "dns_cloudflare_api_token = YOUR_TOKEN" > /root/.secrets/cloudflare.ini
chmod 600 /root/.secrets/cloudflare.ini
# Выпуск Wildcard-сертификата
certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d "*.example.com" -d "example.com"
certbot сам добавляет и удаляет TXT-запись через API, ждёт распространения и умеет продлевать сертификат по cron без дополнительных скриптов поверх acme.sh.
Типичные ошибки DNS-01 challenge
| Сообщение | Причина |
|---|---|
| DNS problem: NXDOMAIN looking up TXT | TXT-запись не добавлена вовсе или добавлена в зоне другого домена либо поддомена |
| DNS problem: query timed out | Слишком большой TTL у зоны или локальный резолвер ещё не увидел изменение — нужно подождать распространения записи |
| CF_Token: command not found | Переменные окружения CF_Token и CF_Account_ID не экспортированы в текущей сессии — забыт export или скрипт запущен из cron без них |
| Multiple validation errors: unauthorized | Старая TXT-запись от предыдущей попытки не удалена; DNS вернул сразу несколько записей _acme-challenge, а Let's Encrypt проверяет только одну |
| Verify error при корректной TXT-записи | Запись ещё не разошлась по всем авторитетным NS-серверам — acme.sh не подождал достаточно, нужно увеличить --dnssleep |
Когда DNS-challenge не подходит
- Нужен сертификат на один конкретный поддомен без wildcard — обычный HTTP-01 challenge через certbot --nginx проще и не требует доступа к DNS API
- DNS управляется регистратором без API или API недоступен в acme.sh — вручную обновлять TXT-запись перед каждым продлением непрактично и рискует пропустить срок
- Сертификат нужен получить мгновенно, за секунды — DNS-01 всегда ждёт распространения записи, минимум десятки секунд, а часто и минуты
- Нужен сертификат для голого IP-адреса — Let's Encrypt не выпускает сертификаты на IP ни через HTTP-, ни через DNS-challenge
Проверка результата
Прежде чем нажимать Enter в certbot или ждать ответа acme.sh, убедитесь, что запись реально видна публичным DNS — это экономит время на повторные попытки. Подробности продления и автопролонгации — в статьях про настройку certbot для wildcard и про автопролонгацию Let's Encrypt.
# Проверить, что TXT-запись реально видна публичным DNS
dig +short TXT _acme-challenge.example.com @8.8.8.8
# Проверить срок действия уже выпущенного сертификата
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -enddate