Пост о том, как я переписал сайт с Tilda на Rust и что из этого вышло.

Как всё начиналось

Год назад мы с друзьями запустили небольшой проект CyberBusinessLeague - платформу для организации киберспортивных турниров между юридическими лицами.

На старте было много организационных задач, поэтому решили не распыляться и сделать лендинг на Tilda. Он решал свои задачи: простые страницы, анонсы, формы - и в целом прожил с нами год.

Но этим летом нас пригласили провести кибертурнир по Dota 2 на форуме «Мой бизнес 2025», и старый, угловатый сайт на Tilda перестал отвечать нашим требованиям и просто стал бить по репутации.

Решили, что пора что-то менять.

Почему вообще Rust?

Так как в команде был человек с технической экспертизой - то есть я :-)

Хотелосьпереписать сайт на чём‑то «своём»: лёгком, кастомизируемом и без зависимости от платформ. Мы рассматривали разные связки:

  • фронт - SvelteKit, Solid.js, etc

  • бэк - Go, Python и т. д.

По правде надо сказать, что я довольно давно работаю в разработке, но к фронтеду у меня было очень опосредованное отношение, верстать что-то очень простое я когда-то учился, но на этом мои полномочия все, и JS я тоже плохо знал.

Но у меня уже был опыт с Rust, и я подумал что "риск дело благородное".
Так и родилась идея сделать всё на Rust + Leptos.

Почему именно Leptos?

До этого момента у меня был опыт с Dioxus, но после небольшого ресерча стало понятно, что у Leptos лучше поддержка SSR(server side rendering), а значит меньше время до первого отображения контента и компактнее wasm-бандл. Для наших задач - идеально.

Процесс

Следующие несколько вечеров были потрачены на написание сайта.

Всего сайт содержит около 10–12 страниц, поэтому я сделал его полностью статическим.
Бэк ничего не хранит, сборка - это просто один Dockerfile с бинарником и статикой.
Leptos отлично дружит с Axum, и получается аккуратная интеграция SSR + роутинга.

Вот пример кода, который инициализирует маршрутизацию и добавляет GZIP-компрессию:

let app = Router::new()
	.leptos_routes(&leptos_options, routes, {
		let leptos_options = leptos_options.clone();
		move || shell(leptos_options.clone())
	})
	.fallback(leptos_axum::file_and_error_handler(shell))
	.with_state(leptos_options)
	.layer(CompressionLayer::new().gzip(true));

На всё ушло около 20–30 часов, из которых две трети я провёл в диалоге с Claude Code, который помогал с версткой.

Что получилось

Сайт крутится на самой слабенькой виртуалке, которую я смог найти:

  • 512 MB RAM, 1 shared vCPU, 400 ₽/мес

  • (для сравнения: Tilda - ~1200 ₽/мес)

  • CPU загрузка не превышает 3%, даже с SSR

  • RAM - до 10%

  • wasm-бандл - около 350 KB

Сравнение

С одной стороны это не то место где стоит оценивать какой-то перформанс, не те масштабы, но было интересно как поведет себя сайт полностью написанный на rust.

Было
Было

Скорость загрузки осталась примерно на том же уровне, но я неожиданно понял,что сайт на Rust получился дружелюбнее к SEO, чем версия на Tilda. И это стало для меня главным откровением - видимо, конструкторы далеко не всегда оптимизируют страницы идеально.

Стало
Стало

Стереотипы и реальные проблемы

Один из самых живучих стереотипов про Rust — это «медленная компиляция». От части так и есть, но почти всегда можно решить этот вопрос или сократить его влияние на твою работу.
В моем случае всё было гораздо проще: у Leptos есть livereload режим.
Он пересобирает проект на лету, и результат виден практически сразу.

cargo leptos watch

А вот релизная сборка действительно доставила проблем, на моем стареньком MacBook Air M1 я получал ошибку линковки:

ld: Assertion failed: (name.size() <= maxLength), function makeSymbolStringInPlace, file SymbolString.cpp, line 74.

Это означает, что линковщик ld пытается создать объект с именем, которое превышает максимально допустимую длину. Leptos в процессе сборки генерирует довольно длинные имена на которые ругается линкер и это довольно известная проблема в сообществе.
Попытка заменить его на clang ничего не дала.

[target.aarch64-apple-darwin]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

В прочем у меня еще были идеи попробовать альтернативные линкеры mold или lld, или уменьшить оптимизации, но в угоду скорости я не стал разбираться с этой проблемой, а просто собирал релиз в Docker - там всё проходило без проблем.

Итог

Спустя некоторое количество вечером мы получили вполне работоспособный сайт, который отвечает нашим требованиям в данный момент.

Конечно, Rust не предлагает того же уровня инфраструктуры и готовых компонентов, как мир JS, но писать на нём в разы интереснее. Ну и в целом проект был сделан больше как баловство, но для меня было интересно увидеть некоторое «сравнение» и ответить себе на вопрос «А можно ли?».

Не смотря на то что сама по себе целесообразность всего мероприятия довольно сомнительная.

После этого опыта я понял, что, пожалуй, не стал бы учить React/Vue/etc только ради фронта, потому что rust так же может решать часть этих задач. К слову у меня ни разу за пару лет использования rust не возникало ситуации, чтобы я не понимал что делает мой код, а вот с python, go — у меня такие эпизоды были.

Жизнь слишком коротка, чтобы тратить ее исключительно рационально, иногда нужно что‑то сделать потому что просто хочется.

Закончить хочется словами классика:

Никогда не забывайте заниматься фигнёй. Потому, что фигня — это оценочное суждение людей, которые не видят в вашей деятельности выгоды.
Но, занимаясь фигнёй, вы исследуете мир, открываете новые пространства и способны найти ту форму взаимодействия с миром, которая сделает вас счастливым.

Всем спасибо!

Комментарии (5)


  1. Ydav359
    21.11.2025 10:25

    сайт на Rust получился дружелюбнее к SEO, чем версия на Tilda

    Конструкторы вообще очень сильно ограничивают то, что можно было бы прописать руками в meta и т.д.


    1. Hovs Автор
      21.11.2025 10:25

      Вы правы! Ну вот мне интуитивно казалось, что они должны были эту проблему решать лучше, чем я ожидал, поэтому и удивился.


  1. Hovs Автор
    21.11.2025 10:25

    Вы правы! Ну вот мне интуитивно казалось, что они должны были эту проблему решать лучше, чем я ожидал, поэтому и удивился.


  1. cupraer
    21.11.2025 10:25

    Вы не создали сайт на расте. Вы создали генератор статического сайта на расте. Которых, наверное, и так — вагон и маленькая тележка, в принципе, но я лично уважаю велосипедостроение.

    не стал бы учить React/Vue/etc только ради фронта, потому что rust так же может решать часть этих задач

    Не может. То, что некоторые альтернативно одаренные товарищи считают, что статику тоже лучше делать реактивной, — ничего не означает.


  1. DanriWeb
    21.11.2025 10:25

    Статья классная — в целом всё верно, а нюансы вроде SEO/SSR и экосистемы Rust vs JS тут и так думаю знающим понятны. Важнее, что это честный опыт «сделал потому что хотел», а такие истории всегда приятно читать :)