Перевести сайт з HTTP на HTTPS — справа двох хвилин. Прикрутив сертифікат, увімкнув редирект, готово. Приблизно так я це собі й уявляв, коли в понеділок затіяв міграцію з HTTP на HTTPS на nginx. Під кінець тижня за плечима були mixed content, redirect loop, сертифікат, що протух о третій ночі в суботу, і рішення про HSTS preload, яке вже не відкотити. Дві хвилини перетворилися на дводенне навчання, якого я не планував.
Ось що відбувається, коли робиш це по-справжньому.
Схема виглядає елементарно
Nginx стоїть скраю як reverse proxy. SSL termination — на ньому ж. Let's Encrypt видає сертифікат. Додаєш пару security-заголовків і вважаєш, що все безпечно. На папері — чотири кроки. За кожним ховається своя пастка.
- Ланцюжок сертифікатів має бути в правильному порядку. Помилишся — і половина клієнтів довіряє серту, а друга половина ловить помилки, які ти не відтвориш у себе на ноутбуці.
- Security-заголовки — HSTS, Content-Security-Policy, X-Frame-Options — кожен змінює поведінку браузера так, що це вилазить лише на проді.
- Автопродовження — це cron, який пишеш один раз і більше в нього не заглядаєш. Ось тут і капкан.
Пастки, що зжерли мені тиждень
Mixed content. Замочок зник, бо одна картинка все ще вантажилася по http://. Одна-єдина. Зашита в inline CSS, і браузер не каже, який саме ресурс, — він мовчки знижує твій значок замка і пише warning у консоль, яку ніхто не тримає відкритою. Mixed content ховається в інлайнових стилях і сторонніх скриптах, про які ти давно забув.
Redirect loops. Я відправляв HTTP на HTTPS, а щось далі по ланцюжку повертало назад на HTTP, і браузер крутився до безкінечності. Так буває, коли nginx термінує TLS, а застосунок за ним усе ще думає, що віддає звичайний HTTP, і городить свій редирект. X-Forwarded-Proto існує саме для цього — і я це вивчив через петлю.
Протухлий серт. Не на налаштуванні — а місяці по тому. Ось про цю частину забувають майже всі. Тебе роняє не первинний конфіг. Тебе роняє друге чи третє продовження, коли cron тихо падає, а ніхто не перевіряє — ну навіщо перевіряти те, що минулого разу спрацювало. Сайт лягає суботнього ранку, і ти о сьомій ранку в трусах дебажиш Let's Encrypt. Спитай, звідки я знаю.
HSTS preload — двері без ручки зі зворотного боку
Ось це налякало найдужче. HSTS каже браузерам: «зі мною лише по HTTPS, і крапка». Preload-список зашиває це правило прямо всередину самого браузера. Відправляєш домен, почуваєшся відповідальним і захищеним — і розумієш, що кнопки «скасувати» більше немає. Зламаєш HTTPS після цього — і сайт не «показує страшний warning». Він реально недоступний. Браузери просто відмовляються під'єднуватися. Відправляй домен туди лише коли справді впевнений.
Який вигляд має «зроблено правильно»
Коли я перестав вгадувати і почав це інженерити, результат вийшов чистий:
- A+ на SSL Labs — проти C, який дефолтний конфіг nginx видає з коробки.
- 100% покриття HTTPS, нуль mixed content, нуль помилок безпеки в браузері.
- Різниця між тим C і цим A+ — приблизно 20 рядків конфіга: TLS 1.2 і вище, сильні cipher suites, HSTS, OCSP stapling. Той самий nginx, той самий сертифікат — просто не дефолти.
І те, що мені найдорожче: runbook. Кожна пастка задокументована, продовження перевірене до того, як воно реально знадобилося, на строк життя серта повішено моніторинг — щоб наступний збій був алертом, а не аварією. Інженер, якому це дістанеться, не проклинатиме ланцюжки сертифікатів усі вихідні.
HTTPS — це підлога, а не стеля
«Просто» в інфраструктурі означає «просто, якщо ти вже один раз зробив усі помилки». Для решти HTTPS — це система, а не галочка: управління сертифікатами, автоматизація продовження, харденінг заголовків, ланцюжки редиректів — усе це інженерять, а не «вмикають тумблером».
HTTPS — не фіча, яку ти викочуєш. Це той мінімум, який ти винен користувачам у 2026-му. І, чесно, ось цей розрив — ставитися до нього як до системи, а не як до перемикача — і є більша частина того, що відрізняє того, хто робить сайти, від того, хто робить інфраструктуру.
Яка міграція звучала «просто», доки ти реально за неї не взявся?