Зачем?
Предыдущая версия сайта работала на Hugo — отличном генераторе статических сайтов. Но со временем накопилось несколько проблем:
-
PHP-зависимость для OAuth и контактов. Hugo генерирует статический HTML, а для динамических фич (авторизация через Google/Yandex, форма контактов) требовался отдельный PHP-бэкенд. Два рантайма — двойная сложность.
-
Ручная пересборка при каждом изменении. Написал статью — нужно пересобрать сайт и задеплоить. Это отбивает желание писать.
-
Дублирование кода. PHP-обработка шорткодов и Hugo-шаблоны жили в разных мирах, но по сути делали одно дело.
Захотелось чего-то более цельного. Чтобы один бинарник содержал в себе всё: и блог, и авторизацию, и отдачу статики.
Почему Rust?
Я пишу на Rust в свободное время, и мне нравится его подход: zero-cost абстракции, безопасность памяти без сборщика мусора, отличная экосистема для веба (Axum, Tera, pulldown-cmark).
Rust-сервер стартует за миллисекунды, ест минимум памяти и держит весь контент в памяти — без БД, без кешей, без внешних сервисов.
Что получилось?
Один бинарник (~25 MB) + директории = весь сайт:
content/— Markdown-файлы с TOML-фронтматтеромtemplates/— Tera-шаблоны (аналог Hugo layouts)static/— статика (CSS, JS, шрифты, SVG)sass/— SCSS (компилируется при старте через grass)i18n/— переводы (поддержка plural-форм)config.toml— конфигурация.env— OAuth-ключи
Никакого PHP. Никакой пересборки. Изменил .md файл в --dev режиме — сайт мгновенно обновился через hot reload.
Ключевые фичи
-
Динамический рендеринг. Сервер читает Markdown при старте и отдаёт HTML на лету. Никакой статической генерации — страницы формируются в момент запроса.
-
Два языка из коробки. Русский и английский.
default_languageв конфиге управляет URL-префиксами:/для основного,/en/для остальных. -
Шорткоды с разделением. Два префикса:
для страниц (требует явного разрешения во фронтматтере),{sct:...}для шаблонов (всегда активен). OAuth-кнопки в header и контакты в about реализованы как короткие вызовы с параметрами и языковой поддержкой. -
OAuth без PHP. Google и Yandex авторизация прямо из Rust-сервера. Ключи берутся из
.envфайла. CSRF-защита через constant-time сравнение (subtle::ct_eq). -
RSS и Sitemap. Три режима sitemap: индексный, per-language, единый с hreflang. RSS 2.0 с
<![CDATA[...]]>. ETag на SHA-256 для условных запросов (304 Not Modified). -
Время генерации в футере. Каждая страница показывает время рендеринга: микросекунды если меньше 1 ms, миллисекунды с сотыми если больше.
-
Полное скрытие hidden-страниц. Страницы с
hidden = "true"исключаются отовсюду: списки разделов, RSS, sitemap, теги, прямой доступ (404). Даже ассеты (картинки) скрытых разделов блокируются. -
Hot reload. В
--devрежиме watcher отслеживает изменения вcontent/иtemplates/— сайт обновляется автоматически. -
0 warnings.
cargo clippy -- -D warningsпроходит без единого предупреждения. -
Phusion Passenger. Проект совместим с Passenger 6 (Generic Language Support).
Passengerfile.jsonсmax_instances: 1(приложение stateful). Secure cookie черезX-Forwarded-Proto— корректная работа за reverse proxy.
Технический стек
| Компонент | Технология |
|---|---|
| HTTP-сервер | Axum 0.7 + Tokio |
| Шаблоны | Tera 1.20 |
| Markdown → HTML | pulldown-cmark 0.11 |
| SCSS → CSS | grass 0.13 |
| Конфигурация | TOML (serde) |
| i18n | Собственная система с plural-формами |
| OAuth | Google + Yandex (reqwest + HMAC-SHA256 JWT) |
| База данных | SQLite (sqlx) — только счётчик просмотров |
| Тесты | 24 unit + integration тестов |
| Docker | 3 compose-файла (dev/prod/runtime) + zigbuild (glibc 2.17) |
| Деплой | make dist → build.zip + Phusion Passenger |
Всё уместилось в ~9500 строк Rust-кода (35 файлов).
Результат
Сайт работает быстро, собирается без ошибок, не требует внешних зависимостей и легко разворачивается. Писать статьи стало проще: сохранил Markdown — и изменения сразу на сайте.
Бинарник кросс-компилируется под glibc 2.17 через cargo zigbuild — работает на CentOS 7+, Debian 8+ и любом современном Linux. Деплой — один build.zip (13 MB) или Docker-образ.
Это был интересный опыт. Рекомендую всем, кто думает переписать свой пет-проект на Rust — оно того стоит.