0% прочитано

Як я продовжувала Sectigo SSL у Nginx і знайшла проблему з DCV

Під час продовження Sectigo SSL сертифікат довго залишався на DCV validation, хоча verification-файл повертав 200 OK. Розповідаю, як я знайшла старий token, розділені HTTP/HTTPS validation paths у Nginx і правильно завершила renewal.

18 серпня 2026 р. 5 хв читанняNginx

Коли я продовжувала Sectigo SSL у Nginx, renewal завис на етапі DCV validation, хоча verification URL повертав `200 OK`. Спочатку все виглядало правильно, але реальна причина виявилася значно цікавішою: старий token, різні physical sources для HTTP і HTTPS та неочевидна пастка з validation-файлом.

У чому була проблема

Зовні це виглядало як звичайне продовження сертифіката. У SSL Panel сертифікат залишався в статусі перевірки домену, а Sectigo не завершував DCV.

Найбільше мене збивало з пантелику те, що verification-файл був доступний через браузер і через curl. Я бачила 200 OK, тому перша думка була проста: якщо файл відкривається, то validation налаштований правильно.

На практиці цього виявилося недостатньо. У моєму випадку проблема була не в самому факті доступності файла, а в тому, що CA мав отримати саме актуальний validation content, а не просто будь-яку відповідь із потрібного URL.

`200 OK` ще не означає, що DCV пройде успішно. Якщо filename залишився тим самим після renewal або reissue, потрібно перевіряти не тільки статус відповіді, а й точний content validation-файла.

Що я перевірила спочатку

Спочатку я перевірила, який сертифікат реально віддає production, а не лише те, що показує SSL Panel.

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

Так я побачила, що production ще віддає старий сертифікат. Тобто навіть якщо renewal уже рухався в SSL Panel, сам сайт ще не був оновлений.

Далі я перевірила validation URL для основного домену та для `www`, окремо через HTTP і HTTPS.

bash
FILE="<VALIDATION_FILE>.txt" curl -i --max-redirs 0 \  "http://example.com/.well-known/pki-validation/$FILE" curl -i --max-redirs 0 \  "https://example.com/.well-known/pki-validation/$FILE" curl -i --max-redirs 0 \  "http://www.example.com/.well-known/pki-validation/$FILE" curl -i --max-redirs 0 \  "https://www.example.com/.well-known/pki-validation/$FILE"

На цьому етапі я отримувала саме те, що й очікувала побачити на перший погляд: 200 OK. І саме тут було легко зробити хибний висновок, що вся DCV-конфігурація вже правильна.

Але коли renewal не завершувався, стало зрозуміло, що потрібно дивитися глибше: не просто на URL, а на те, який саме physical file віддається у відповідь і який вміст реально бачить CA.

Де насправді була причина

Справжня причина виявилася подвійною.

По-перше, у мене залишився старий validation token. Filename міг збігатися з попереднім renewal, але content уже був іншим. Візуально це дуже легко пропустити, особливо коли сам URL виглядає правильним.

По-друге, HTTP і HTTPS спочатку не використовували один і той самий source of truth. Один validation endpoint віддавав файл з окремого технічного каталогу, а інший - через application path. Ззовні обидва могли виглядати робочими, але фактично CA отримував не ту саму відповідь залежно від схеми або hostname.

Саме після цього я вже перестала дивитися на 200 OK як на остаточний доказ і почала перевіряти актуальний token, physical source файла і єдину логіку для всіх DCV endpoints.

До

Validation URL повертав 200 OK, але HTTP і HTTPS могли віддавати файл із різних physical sources.

Один із них містив старий token, тому renewal не завершувався, навіть попри зовні правильну відповідь.

Після

І HTTP, і HTTPS почали віддавати validation-файл з одного окремого каталогу під контролем Nginx.

Після цього Sectigo отримував один і той самий актуальний token, а renewal уже можна було завершити без плутанини.

Скрін панелі SSL із renewal сертифіката до завершення перевірки домену
Стан сертифіката до завершення перевірки домену: renewal ще очікує на коректну DCV validation.
Скрін панелі SSL після успішного завершення renewal сертифіката
Після виправлення validation-файла та конфігурації Nginx renewal завершився, а сертифікат перейшов у робочий стан.

Як я це виправила

Рішення складалося з кількох кроків.

Спочатку я оновила validation-файл так, щоб у ньому був саме поточний token для renewal, а не залишок від попереднього випуску. Далі я прибрала неоднозначність у маршрутизації та залишила один окремий DCV path, який Nginx віддає напряму і для HTTP, і для HTTPS.

Після того, як validation став послідовним, я вже встановила новий сертифікат: перевірила, що certificate відповідає private key, коректно зібрала fullchain, виконала nginx -t і тільки після цього зробила reload.

Nginx - PKI validation location
location ^~ /.well-known/pki-validation/ {    auth_basic off;    root /var/www/ssl-validation;    default_type "text/plain";    try_files $uri =404;}

Для мене головна ідея тут проста: DCV не повинен залежати від application deployment. Коли validation path віддається напряму Nginx із окремого каталогу, це набагато надійніше й передбачуваніше.

Перевірка certificate, key і reload Nginx
openssl x509 \  -in /tmp/cert/example_com.crt \  -pubkey -noout \  | openssl pkey -pubin -outform DER 2>/dev/null \  | sha256sum openssl pkey \  -in /etc/nginx/ssl/example.com/example.com.key \  -pubout -outform DER 2>/dev/null \  | sha256sum sudo nginx -tsudo systemctl reload nginx
Не варто робити reload Nginx, поки `nginx -t` не проходить успішно або якщо новий certificate не відповідає private key. Це вже не просто дрібна помилка, а ризик зламати робочий HTTPS на production.

Що змінилося після виправлення

Після виправлення validation-файла, уніфікації DCV path і перевірки сертифіката production уже віддавав новий Sectigo SSL certificate, а renewal завершився без додаткових сюрпризів.

Що я залишила собі на майбутнє

  • Я більше не сприймаю 200 OK як достатній доказ успішної DCV validation.

  • Якщо filename validation-файла збігається з попереднім renewal, я окремо перевіряю саме content.

  • HTTP і HTTPS повинні використовувати один source of truth для /.well-known/pki-validation/.

  • DCV краще тримати поза application deployment і віддавати напряму через Nginx.

  • Перед reload я завжди перевіряю certificate, private key і fullchain, а вже потім оновлюю production.

Для мене це було хороше нагадування, що проблеми з SSL renewal часто ховаються не в самому сертифікаті, а в дрібних деталях validation і конфігурації. У цьому випадку найкориснішим виявилося не те, що я ще раз запустила renewal, а те, що я уважно перевірила, який саме файл і звідки реально віддає Nginx.