Покидання WordPress: міграція мого сайту на Astro та SQLite
Роками andrew.khanoff.com працював на WordPress: PHP-FPM, база даних MySQL, купа плагінів та постійний фоновий гул «ваш сайт потребує оновлення».
Це працювало — але це було 475 МБ файлів, база даних обсягом 160 МБ із 301 таблицею та поверхня атаки, про яку я ніколи не просив (xmlrpc.php, wp-login.php, плагін тижня). Для особистого резюме, яке переважно містить текст, це багато рухомих частин для латання та хвилювання.
Тому я його замінив. Ось чому і як.
Чому відходити від WordPress
- Поверхня атаки. Статичне резюме не потребує форми входу у публічному інтернеті, системи коментарів чи віддаленого виконання коду через плагін.
- Обсяг. PHP + MySQL + ядро WordPress + плагіни, лише для рендерингу сторінки, яка змінюється кілька разів на рік.
- Контроль. Я хотів вміст у форматі, який належить мені, відрендерений кодом, який я можу прочитати, без панелі адміністратора, доступної з відкритого інтернету.
Заміна навмисно мала: єдине застосування Astro (серверно-рендерене, адаптер Node) в одному контейнері Docker, з SQLite як єдиним джерелом правди. Жодного PHP. Жодного MySQL. Адміністративна панель доступна лише за passkey та обмежена LAN на зворотньому проксі. Проєкти створюються щоденним конвеєром GitHub→локальний LLM, а аналітика є первинною з локальною базою даних GeoIP.
Правило: нічого руйнівного без шляху повернення
Уся міграція проходила поетапно, і перший етап був не кодом — це були резервні копії. Перш ніж торкатися чогось:
tarповного docroot WordPress (350 МБ стиснені).mysqldump --single-transactionбази даних WordPress (18 МБ).- Контрольні суми та тесточне розпакування, щоб довести реальність архівів.
Тільки тоді я створив новий сайт поруч з WordPress, на тимчасовому порту, не торкаючись робочого vhost. WordPress продовжував обслуговувати домен увесь час, поки створювався та заповнювався новий застосунок.
Перехід без простоїв
Сама зміна була зміною зворотного проксі, а не міграцією даних. nginx перейшов від обслуговування /var/www/andrew.khanoff.com через PHP-FPM до проксіювання нового контейнера на 127.0.0.1:8090:
location / {
proxy_pass http://127.0.0.1:8090;
}
Старий vhost спочатку було резервовано, nginx -t обмежив перезавантаження, і якщо щось виглядало неправильно, відкат був одним cp зі старої конфігурації плюс перезавантаження. Файли та база даних WordPress залишилися повністю неушкодженими — домен просто перестав на них вказувати. Час відкату: секунди.
Я запустив новий сайт у робочому режимі на деякий час, перевіривши все (публічні сторінки, вхід за passkey, конвеєр проєктів, експорт PDF, аналітика), і лише тоді видалив базу даних WordPress — після взяття ще одного свіжого дампу безпосередньо перед DROP. 475 МБ файлів WordPress були видалені останніми, після повторної перевірки резервних копій.
Що краще зараз
- Безпека: жодного публічного входу, жодного PHP, жодних плагінів. Адміністративна панель захищена WebAuthn passkeys та правилом
allow 10.0.20.0/24на nginx. HSTS увімкнено. - Продуктивність: мобільний Lighthouse має 92 / 96 / 96 / 100 (Performance / Accessibility / Best-Practices / SEO), LCP ~1.4 с. Відповідь сервера становить ~30 мс, оскільки немає танцю з обходом бази даних — лише SQLite та Astro.
- Операції: весь сайт — один контейнер і один файл SQLite. Резервні копії тривіальні. Цікава автоматизація (GitHub + локальний LLM) живе в офлайн пакетному шляху — якщо вона зламається, сайт цього не помітить.
- Власність: вміст знаходиться в SQLite та Markdown, у git репозиторії, відрендерений за допомогою ~2 тис. рядків коду, який я насправді можу прочитати.
WordPress — чудовий інструмент. Просто він був не тим обсягом механізмів для резюме, яке я хочу, щоб воно було швидким, нудним в експлуатації та безпечним, щоб його можна було залишити на місяці. Цей пост і сайт, на якому ви його читаєте, є результатом.
Прозорість: більшість дописів тут створено за допомогою ШІ, а неанглійські версії перекладені локальною LLM і вичитані. Помітили помилку? Напишіть мені.