Wildcard-сертификат: когда он нужен, чем опасен и как выпустить через Let's Encrypt по DNS-01
Один сертификат на все поддомены звучит удобно, пока не выясняется, что он не покрывает сам домен и второй уровень вложенности, а при утечке единственного ключа менять придётся всё сразу. Разбираем по RFC 9525 и требованиям CA/Browser Forum, что закрывает звёздочка, когда выгоднее SAN, почему Let's Encrypt выдаёт wildcard только по DNS-01 и какие российские DNS поддержаны в acme.sh.
Wildcard-сертификат — TLS-сертификат, в котором имя записано со звёздочкой вместо первой метки (*.example.ru), и который поэтому подходит для любого поддомена ровно одного уровня: shop.example.ru, api.example.ru, любой-новый.example.ru. Он не покрывает сам example.ru (его добавляют в сертификат отдельным именем) и не покрывает поддомены глубже — a.b.example.ru под *.example.ru не подходит. Публичные wildcard-сертификаты подтверждаются только через DNS: УЦ требует доказать контроль над зоной, а не над одним веб-сервером.
Что покрывает *.example.ru, а что нет
Звёздочка заменяет ровно одну DNS-метку, и только самую левую. Так требует RFC 9525 (ноябрь 2023 года, заменил RFC 6125): в имени допустим один wildcard-символ, он должен быть целой левой меткой и совпадает только с одной меткой проверяемого имени. Всё остальное клиент обязан игнорировать.
На практике: apex под звёздочку не попадает — его добавляют отдельным SAN-именем; второй уровень вложенности не попадает — для *.dev.example.ru нужен отдельный wildcard; частичные шаблоны вроде f*.example.ru RFC 6125 ещё допускал, RFC 9525 объявил невалидными, а публичные УЦ их и раньше не выпускали — по Baseline Requirements Wildcard Domain Name это строго «*.» плюс полное имя.
| Имя в браузере | Покрыт сертификатом на *.example.ru? | Почему |
|---|---|---|
| shop.example.ru | Да | Одна метка на месте звёздочки |
| www.example.ru | Да | www — обычная метка, исключений для неё нет |
| example.ru | Нет | Звёздочке нечего заменять; нужен отдельный SAN example.ru |
| a.b.example.ru | Нет | Wildcard совпадает только с одной меткой, здесь их две |
| api.dev.example.ru | Нет | Нужен второй wildcard *.dev.example.ru |
Типовой состав wildcard-сертификата — два имени в SAN: DNS:*.example.ru, DNS:example.ru. В один сертификат Let's Encrypt помещает до 100 имён (профиль classic), так что несколько wildcard-имён — *.example.ru и *.stage.example.ru — укладываются свободно.
Wildcard, SAN или отдельные сертификаты: что выбрать
Wildcard оправдан, когда поддомены появляются без вашего участия: тенанты SaaS, превью-окружения на каждую ветку, стенды разработчиков. Если список хостов известен и меняется раз в месяц, SAN-сертификат даёт тот же один файл на всех серверах, но не вручает «мастер-ключ» от всей зоны. Отдельный сертификат на каждый хост — самый безопасный вариант; с ACME-клиентом разница в трудозатратах стремится к нулю, и решающим становится радиус поражения при утечке ключа.
| Критерий | Wildcard (*.example.ru + example.ru) | SAN / multi-domain | Отдельный на каждый хост |
|---|---|---|---|
| Поддомены неизвестны заранее | Да, новый хост первого уровня работает сразу | Нет, каждое новое имя — перевыпуск | Нет, каждое новое имя — новый выпуск |
| Подтверждение в Let's Encrypt | Только DNS-01 | HTTP-01, TLS-ALPN-01 или DNS-01 | HTTP-01, TLS-ALPN-01 или DNS-01 |
| Стоимость | В Let's Encrypt бесплатно; у коммерческих УЦ дороже одиночного | Бесплатно, до 100 имён в сертификате | Бесплатно, лимит 50 сертификатов на домен в неделю |
| Радиус поражения при утечке ключа | Вся зона, включая будущие поддомены | Все имена из списка | Один хост |
| Отзыв | Бьёт по всем сервисам сразу | По всем именам в сертификате | Локальный |
| EV-валидация | Запрещена EV Guidelines | Возможна у коммерческих УЦ | Возможна у коммерческих УЦ |
Правило: wildcard — для динамических поддоменов за одним TLS-терминатором, SAN — для фиксированного набора хостов на одном сервере, отдельные — для разных машин. Базовый выпуск по HTTP-01 разобран в статье про Let's Encrypt; дальше речь только о wildcard.
Чем опасен wildcard: один ключ на всю зону
Главный риск — не сертификат, а закрытый ключ, который приходится копировать на каждый сервер зоны. Утечка с самого слабого из них (забытый стенд, сервер подрядчика, бэкап без шифрования) даёт злоумышленнику валидный TLS для любого имени зоны, включая хосты, которых у вас ещё нет. RFC 9525 предупреждает прямо: wildcard-сертификат «автоматически ручается за все хосты домена», и компрометация любого из них компрометирует остальные.
Второй риск — отзыв: при утечке недоступными становятся все сервисы сразу. Третий — ограничения снаружи. EV-сертификаты с wildcard не выпускаются в принципе: EV Guidelines CA/Browser Forum говорят, что «wildcard-символы в доменных именах не допускаются для EV TLS-сертификатов» (исключение только для .onion); Let's Encrypt EV и OV не выдаёт вообще. Отдельные старые клиенты и встроенные устройства не понимают звёздочку — при интеграциях это стоит проверить.
Забытый поддомен со старым приложением останется «зелёным» в браузере и невидимым в CT-логах. Без отдельного списка хостов ни отзыв, ни ротация ключа не будут полными.
Почему Let's Encrypt выдаёт wildcard только по DNS-01
HTTP-01 доказывает контроль над одним веб-сервером на порту 80, а wildcard утверждает контроль над всеми будущими именами зоны. Документация Let's Encrypt формулирует жёстко: HTTP-01 «не может использоваться для выпуска wildcard-сертификатов», TLS-ALPN-01 — тоже, и только DNS-01 «позволяет выпускать сертификаты с wildcard-именами». Это общее правило: бюллетень SC45 CA/Browser Forum с 1 декабря 2021 года запретил HTTP-методы валидации для wildcard всем публичным УЦ.
Механика DNS-01: клиент получает от УЦ токен, вычисляет из него и ключа аккаунта значение и публикует его в TXT-записи _acme-challenge.example.ru. Для пары «*.example.ru + example.ru» нужны два разных TXT-значения на одном имени одновременно. CNAME с _acme-challenge на отдельную зону (например, acme-dns) — легальный способ не давать ACME-клиенту доступ к основной зоне; как устроены TXT и CNAME, напоминает статья о типах DNS-записей. Цена метода — ключи от DNS на сервере продления; Let's Encrypt называет это минусом прямо: «хранить учётные данные API на веб-сервере рискованно».
Какие DNS-провайдеры поддержаны в certbot и acme.sh
У certbot 13 официальных DNS-плагинов, и российских среди них нет: cloudflare, digitalocean, dnsimple, dnsmadeeasy, gehirn, google, linode, luadns, nsone, ovh, rfc2136, route53, sakuracloud. Для российских DNS основной инструмент — acme.sh: в его wiki dnsapi нумерация хуков на сентябрь 2026 года доходит до 228, среди них Selectel, Yandex Cloud, Timeweb Cloud, Reg.ru, Beget, NIC.RU и Yandex 360 (старый хук dns_yandex для pdd.yandex.ru не работает).
| DNS-провайдер | certbot | acme.sh, хук | Переменные окружения |
|---|---|---|---|
| Selectel | Нет | dns_selectel | SL_Ver=v2, SL_Login_ID, SL_Project_Name, SL_Login_Name, SL_Pswd (v1 с SL_Key устарел) |
| Yandex Cloud DNS | Нет | dns_yc | YC_Folder_ID, YC_SA_ID, YC_SA_Key_ID, YC_SA_Key_File_Path (роль dns.editor) |
| Timeweb Cloud | Нет | dns_timeweb | TW_Token (JWT из панели) |
| Reg.ru | Нет | dns_regru | REGRU_API_Username, REGRU_API_Password; IP сервера — в белый список API |
| Beget | Нет | dns_beget | BEGET_User, BEGET_Password |
| NIC.RU | Нет | dns_nic | NIC_ClientID, NIC_ClientSecret, NIC_Username, NIC_Password |
| Cloudflare | certbot-dns-cloudflare | dns_cf | CF_Token с правом Zone:DNS:Edit на одну зону, CF_Account_ID |
Если провайдера нет ни в одном списке (типично для DNS «в комплекте» с хостингом), варианта три: делегировать _acme-challenge через CNAME в зону с API, перенести зону целиком или обновлять TXT руками каждые 60 дней.
Как выпустить wildcard в certbot: ручной режим и DNS-плагин
Ручной режим (--manual) годится для первого выпуска, но не продлевается сам: по документации certbot такие сертификаты «не поддерживают автоматическое продление без скрипта аутентификации» (--manual-auth-hook). Wildcard в certbot поддерживается с версии 0.22.0 — с марта 2018 года, когда Let's Encrypt запустил ACMEv2.
# Ручной режим: certbot попросит создать TXT-записи и подождёт sudo certbot certonly --manual --preferred-challenges dns \ -d '*.example.ru' -d example.ru # пример вывода (сокращён) Please deploy a DNS TXT record under the name: _acme-challenge.example.ru. with the following value: Kq3nT9xY0cJ2mVb8fH1rWZa6LpUeD4sG7oNiB5tRkXw Before continuing, verify the TXT record has been deployed. # Второе имя даст второе значение для ТОГО ЖЕ имени — обе записи # должны существовать одновременно. Проверить перед Enter: dig +short TXT _acme-challenge.example.ru @ns1.example-dns.ru
Автоматический путь — DNS-плагин. Для Cloudflare это пакет certbot-dns-cloudflare: файл с токеном обязан иметь права 600, а пауза на распространение записи по умолчанию 10 секунд — для медленных зон её поднимают до 60–120.
# Токен Cloudflare с правом Zone:DNS:Edit на одну зону printf 'dns_cloudflare_api_token = <токен>\n' \ | sudo tee /root/.secrets/certbot/cloudflare.ini sudo chmod 600 /root/.secrets/certbot/cloudflare.ini sudo certbot certonly --dns-cloudflare \ --dns-cloudflare-credentials /root/.secrets/certbot/cloudflare.ini \ --dns-cloudflare-propagation-seconds 60 \ -d '*.example.ru' -d example.ru \ --deploy-hook 'systemctl reload nginx'
Перезагрузку nginx вешают на --deploy-hook, а не на код выхода: certbot renew возвращает 0 и тогда, когда продлевать было нечего.
Как выпустить wildcard в acme.sh с российским DNS
acme.sh — shell-скрипт без зависимостей, хуки под Selectel, Yandex Cloud и Timeweb идут в комплекте. Две вещи, о которые спотыкаются: по умолчанию acme.sh (версия 3.1.6 на сентябрь 2026 года) заказывает сертификат у ZeroSSL, поэтому Let's Encrypt указывают явно через --server letsencrypt; учётные данные нужны только при первом выпуске — скрипт сохраняет их в ~/.acme.sh/account.conf.
# Установка (ставит cron-задачу на ежедневную проверку продлений) curl https://get.acme.sh | sh -s email=admin@example.ru # Selectel, API v2: сервисный пользователь проекта export SL_Ver="v2" SL_Login_ID="123456" SL_Project_Name="dns-acme" export SL_Login_Name="acme-user" SL_Pswd="<пароль>" acme.sh --issue --server letsencrypt --dns dns_selectel \ -d example.ru -d '*.example.ru' # Yandex Cloud DNS: сервисный аккаунт с ролью dns.editor export YC_Folder_ID="b1g..." YC_SA_ID="aje..." YC_SA_Key_ID="ajk..." export YC_SA_Key_File_Path="/root/.yc/private.key" acme.sh --issue --server letsencrypt --dns dns_yc \ -d example.ru -d '*.example.ru' # Timeweb: export TW_Token="..." и --dns dns_timeweb по той же схеме, # Cloudflare: CF_Token, CF_Account_ID и --dns dns_cf # Положить файлы в nginx и перезагружать его после каждого продления acme.sh --install-cert -d example.ru \ --key-file /etc/nginx/ssl/example.ru.key \ --fullchain-file /etc/nginx/ssl/example.ru.crt \ --reloadcmd "systemctl reload nginx"
Если серверы провайдера обновляются медленно и проверка падает на TXT-записи, добавьте --dnssleep 120: acme.sh не будет опрашивать публичные резолверы, а просто подождёт указанное число секунд.
Зачем wildcard-домену запись CAA
CAA — DNS-запись со списком удостоверяющих центров, которым разрешено выпускать сертификаты на домен; Let's Encrypt проверяет её перед каждым выпуском. Для wildcard есть отдельный тег issuewild: если он задан, для *.example.ru действует он, а не issue — так можно разрешить wildcard только одному УЦ и только по DNS-01.
; Зона example.ru example.ru. CAA 0 issue "letsencrypt.org" example.ru. CAA 0 issuewild "letsencrypt.org; validationmethods=dns-01" example.ru. CAA 0 iodef "mailto:security@example.ru" # Проверить, что записи видны снаружи dig +short CAA example.ru
УЦ ищет CAA снизу вверх — от запрошенного имени к родительским — и останавливается на первой найденной записи, поэтому CAA на example.ru покрывает и *.example.ru, и поддомены без собственной записи.
Сколько живёт сертификат и когда продлевать
Wildcard от Let's Encrypt живёт 90 дней в профиле classic, 45 дней в tlsserver и 160 часов в shortlived — столько же, сколько обычный сертификат. Let's Encrypt рекомендует продлевать, когда осталась треть срока, то есть на 60-й день; клиенты с поддержкой ARI (RFC 9773; в acme.sh включена без флагов) получают окно продления от самого УЦ.
У коммерческих УЦ сроки сокращаются по расписанию бюллетеня SC-081v3 CA/Browser Forum (принят в апреле 2025 года): 398 дней до 15 марта 2026 года, затем 200, с 15 марта 2027 года — 100, с 15 марта 2029 года — 47 дней. Параллельно сокращается срок переиспользования подтверждения контроля над доменом: 200, 100 и 10 дней. При 10 днях ручной wildcard практически невозможен.
| Кто и когда | Максимальный срок | Переиспользование DNS-валидации | Что это значит для wildcard |
|---|---|---|---|
| Let's Encrypt, classic | 90 дней | Авторизация — 30 дней | Продление на 60-й день; в ручном режиме — 6 раз в год руками |
| Let's Encrypt, shortlived | 160 часов | Авторизация — 7 часов | Продление каждые 3 дня; такому сертификату по BR не нужна информация об отзыве |
| Коммерческие УЦ с 15.03.2026 (сейчас) | 200 дней | 200 дней | Два выпуска в год вместо одного (398 дней до этой даты) |
| С 15.03.2027 | 100 дней | 100 дней | Четыре выпуска в год, каждый с новой DNS-валидацией |
| С 15.03.2029 | 47 дней | 10 дней | Только полная автоматизация DNS-01 |
Что ломается при смене DNS-провайдера
Продление wildcard зависит не от веб-сервера, а от API DNS, поэтому переезд зоны ломает его тихо: сертификат работает ещё до 60 дней, cron запускается и падает на TXT-записи, а заметно это становится по истечении срока. Типовой сценарий — сменили регистратора или хостинг, переключили NS, а в ~/.acme.sh/account.conf или /root/.secrets остались токены старого провайдера.
Чек-лист переезда: перенести CAA-записи (новые панели часто их не импортируют, и выпуск падает с ошибкой CAA), переписать хук и учётные данные, перевыпустить сертификат сразу после переключения NS, убрать старые TXT _acme-challenge — Let's Encrypt советует чистить их, чтобы ответ не разрастался. Если _acme-challenge делегирован через CNAME в отдельную зону, переезд основной зоны его не затрагивает — главный аргумент за такую схему.
Где хранить один ключ на много серверов
Одна пара «ключ + цепочка» должна оказаться на всех серверах зоны и обновляться там каждые 60 дней синхронно. Чем меньше копий ключа, тем меньше радиус поражения — лучшее место для wildcard-ключа то, откуда его не нужно копировать.
| Схема | Копий ключа | Как доставляется продление | Когда подходит |
|---|---|---|---|
| Выпуск на каждом сервере независимо | По одной на сервер, ключи разные | Каждый сервер сам ходит в DNS API и ACME | 2–5 серверов; токен DNS на каждом, лимит 5 дубликатов в неделю |
| Выпуск на одной машине, копирование по SSH | N + 1 | --deploy-hook или --reloadcmd с rsync и reload на приёмниках | Небольшой парк с единым SSH-доступом; токен DNS на одной машине |
| Центральный TLS-терминатор (nginx, HAProxy, Envoy) | 1–2 | Продление только на терминаторах; бэкенды — по HTTP или внутреннему CA | Основной вариант: ключ не покидает периметр |
| Управляемый балансировщик или CDN с ACME | 0 у вас | Провайдер выпускает и продлевает сам | Когда допустимо доверить ключ провайдеру |
Как проверить, что wildcard отдаётся правильно
Проверять нужно каждый поддомен с указанием SNI: без -servername сервер может отдать сертификат по умолчанию. Смотрите на Subject Alternative Name (там должны быть и *.example.ru, и example.ru) и на Verify return code.
# Какой сертификат отдаётся для конкретного поддомена
openssl s_client -servername shop.example.ru \
-connect shop.example.ru:443 </dev/null 2>/dev/null \
| openssl x509 -noout -dates -ext subjectAltName
# пример вывода
notBefore=Sep 2 07:14:51 2026 GMT
notAfter=Dec 1 07:14:50 2026 GMT
X509v3 Subject Alternative Name:
DNS:*.example.ru, DNS:example.ru
# Имя второго уровня wildcard не покроет — это ожидаемая ошибка
openssl s_client -servername a.b.example.ru -connect example.ru:443 \
-verify_hostname a.b.example.ru </dev/null 2>/dev/null \
| grep 'Verify return code'
# пример вывода
Verify return code: 62 (hostname mismatch)
Полный разбор ручной проверки — в статье как проверить SSL-сертификат сайта. В nginx один wildcard-сертификат обслуживает сколько угодно server-блоков, а поддомены в server_name можно задать регулярным выражением:
# /etc/nginx/conf.d/example.ru.conf
server {
listen 443 ssl;
http2 on;
# поддомены — регулярным выражением, apex и www — в своём блоке
server_name ~^(?<tenant>[a-z0-9-]+)\.example\.ru$;
ssl_certificate /etc/nginx/ssl/example.ru.crt;
ssl_certificate_key /etc/nginx/ssl/example.ru.key;
root /var/www/tenants/$tenant;
}
nginx -t && systemctl reload nginx
Как выпустить wildcard через certbot и DNS-01
- Убедитесь, что у DNS-провайдера есть API и под него есть плагин certbot или хук acme.sh; если нет — делегируйте _acme-challenge.example.ru через CNAME в зону с API.
- Создайте токен с минимальными правами: только редактирование DNS одной зоны (в Cloudflare — Zone:DNS:Edit, в Yandex Cloud — сервисный аккаунт с ролью dns.editor).
- Добавьте в зону CAA-записи issue и issuewild с letsencrypt.org, чтобы никакой другой УЦ не мог выпустить wildcard на ваш домен.
- Установите certbot и DNS-плагин (например, python3-certbot-dns-cloudflare), положите учётные данные в файл с правами 600.
- Запустите certbot certonly с плагином и двумя именами: -d '*.example.ru' -d example.ru; для медленного DNS поднимите --dns-*-propagation-seconds до 60–120.
- Проверьте результат: openssl x509 -ext subjectAltName на /etc/letsencrypt/live/example.ru/fullchain.pem покажет оба имени.
- Подключите сертификат в nginx, добавьте --deploy-hook 'systemctl reload nginx' и выполните certbot renew --dry-run — продление должно пройти без человека.
- Поставьте внешний мониторинг срока действия на каждый публичный поддомен: продление могло пройти, а копия на одном из серверов — нет.
Последний пункт — не формальность: автоматизация wildcard ломается на стыках — сменился токен, переехала зона, сервер не получил новый файл. Пруфен проверяет SSL-сертификат отдельно для каждого поддомена из вашего списка и предупреждает до истечения срока, а не когда браузеры уже показывают ошибку. Параллельная проверка DNS покажет, что зона после переезда отвечает не так, как ожидалось, — то есть причину, по которой следующее продление не пройдёт.
Пруфен выполняет SSL- и DNS-проверки из 20 точек мониторинга в России, Беларуси, Казахстане и Испании и уведомляет в Email, Telegram или Макс, когда сертификат на любом из поддоменов подходит к концу срока или перестал проходить проверку; если ресурс так и не поднялся, подключается голосовой звонок. Бесплатного плана на 5 проверок хватает на основной домен и четыре поддомена.
Частые вопросы
Покрывает ли wildcard сам домен второго уровня, то есть example.ru?
Нет. По RFC 9525 звёздочка должна заменить ровно одну метку, а у example.ru слева от зоны меток нет. Поэтому wildcard почти всегда выпускают с двумя именами в SAN: *.example.ru и example.ru — в certbot и acme.sh это два флага -d в одной команде.
Можно ли одним wildcard закрыть несколько уровней поддоменов?
Нет: *.example.ru совпадает только с одной меткой, а a.b.example.ru содержит две. Двойная звёздочка (*.*.example.ru) стандартом запрещена. Решение — добавить в тот же сертификат второй wildcard, например *.dev.example.ru: каждое wildcard-имя считается одним из 100 допустимых.
Как получить wildcard бесплатно?
Через Let's Encrypt по DNS-01: нужен DNS-провайдер с API и клиент под него — certbot с DNS-плагином или acme.sh с хуком (Selectel, Yandex Cloud, Timeweb, Reg.ru, Beget, NIC.RU). Сертификат живёт 90 дней и продлевается автоматически; без API его выдадут и в ручном режиме, но TXT-записи придётся обновлять руками каждые 60 дней.
Почему wildcard продлевался год, а после переезда DNS перестал?
Потому что продление по DNS-01 обращается к API старого провайдера, чьи токены остались в конфигурации клиента, а зона теперь у другого; ошибка не проявляется до планового продления. После переезда перепишите хук, проверьте CAA у нового провайдера и сразу выполните принудительное продление: certbot renew --force-renewal или acme.sh --renew -d example.ru --force.