Nuxt — фреймворк поверх Vue, который добавляет файловый роутинг, серверный рендеринг, автоимпорты и серверный слой Nitro. В 2026 году актуальна ветка Nuxt 4: она перешла на каталог app/, а поддержка Nuxt 3 завершилась в июле 2026 года, поэтому новые проекты логично начинать с четвёртой версии.
Разберём структуру проекта, SSR и гибридный рендеринг, получение данных, серверные маршруты, SEO, сборку и эксплуатацию на сервере.
Зачем нужен серверный рендеринг
SSR отдаёт браузеру готовый HTML: страница видна до загрузки JavaScript, поисковые роботы получают содержимое, а медленные мобильные устройства не ждут гидратации. Плата — нагрузка на сервер и более сложная отладка.
- SSR полезен для контента, каталогов, интернет-магазинов и SEO-страниц.
- SPA уместен для закрытых панелей, где индексация не нужна.
- Гибридный режим позволяет смешивать оба подхода по маршрутам.
Nuxt поддерживает статическую генерацию, SSR, SPA и гибридные правила через routeRules. Выбор делается на уровне маршрута, а не всего приложения.
Создание проекта и структура
npm create nuxt@latest my-app
cd my-app
npm install
npm run dev
В Nuxt 4 исходники Vue лежат в app/: pages/, components/, layouts/, composables/, middleware/. Серверный код — в server/, общие типы и утилиты — в shared/. Такое разделение не даёт случайно импортировать серверный секрет в клиентский бандл.
app/
pages/index.vue
pages/blog/[slug].vue
components/PostCard.vue
server/
api/health.get.ts
shared/
utils/format.ts
Маршруты и данные
Файл app/pages/blog/[slug].vue создаёт динамический маршрут. Параметр доступен через useRoute(), а данные лучше загружать через useFetch или useAsyncData: они умеют работать на сервере, кэшировать ответ и передавать payload в браузер без повторного запроса.
<script setup lang="ts">
const route = useRoute()
const { data: post, error } = await useFetch(
`/api/posts/${route.params.slug}`,
{ key: `post-${route.params.slug}` }
)
if (error.value) {
throw createError({ statusCode: 404, statusMessage: 'Not found' })
}
</script>
Ключ key должен быть стабильным и уникальным. Одинаковые ключи с разными опциями в Nuxt 4 считаются ошибкой, поэтому не передавайте в useFetch случайные параметры без необходимости.
Серверные маршруты Nitro
Файлы в server/api становятся HTTP-эндпоинтами. Это удобно для проксирования внешнего API и сокрытия ключей.
export default defineEventHandler(async (event) => {
const { slug } = getQuery(event)
if (!slug) {
throw createError({ statusCode: 400, statusMessage: 'slug required' })
}
const config = useRuntimeConfig(event)
return await $fetch(`${config.apiBase}/posts/${slug}`, {
headers: { authorization: `Bearer ${config.apiToken}` }
})
})
Приватные значения задавайте через переменные окружения NUXT_API_TOKEN и runtimeConfig. Всё, что начинается с NUXT_PUBLIC_, попадает в клиентский бандл и секретом быть не может.
Гибридный рендеринг и кэш
Правила маршрутов позволяют отдать статику через CDN, а персонализированные страницы рендерить на сервере.
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true },
'/blog/**': { swr: 600 },
'/admin/**': { ssr: false },
'/api/**': { cors: true }
}
})
swr отдаёт закэшированную версию и обновляет её в фоне. Для страниц с авторизацией кэш опасен: используйте ssr: false или приватные заголовки. Проверяйте поведение через curl -I и смотрите на cache-control.
SEO и метаданные
Для заголовков и Open Graph используйте useSeoMeta. Данные должны быть доступны на сервере, иначе робот увидит пустые теги.
useSeoMeta({
title: post.value?.title,
description: post.value?.excerpt,
ogTitle: post.value?.title,
ogImage: post.value?.cover,
twitterCard: 'summary_large_image'
})
Добавьте robots.txt, карту сайта и канонические URL. Для динамических карт подойдёт серверный маршрут server/routes/sitemap.xml.ts, который собирает список страниц из базы.
Аутентификация и cookies
Сессионную cookie удобно читать через useCookie: на сервере composable берёт значение из заголовка запроса, в браузере — из document.cookie. Флаг httpOnly выставляйте при установке cookie с сервера: JavaScript такую cookie прочитать не сможет, поэтому проверку нужно дублировать в серверном middleware.
const session = useCookie('session', {
sameSite: 'lax',
secure: true,
maxAge: 60 * 60 * 24
})
if (!session.value) {
await navigateTo('/login')
}
Не доверяйте данным из cookie без проверки подписи и не определяйте права по полю, пришедшему от клиента. Для SSR проверяйте сессию на сервере через server/middleware, иначе защищённая страница успеет отрендериться до редиректа. Для CSRF используйте sameSite и проверку заголовка Origin на изменяющих запросах. Токены доступа к внешним API держите в runtimeConfig и не передавайте в браузер.
Если приложение использует JWT, помните: SSR-рендер и клиентская навигация должны проверять его одинаково, иначе после перехода по ссылке появится мигание защищённого контента.
Сборка и запуск
Продакшен-сборка создаёт каталог .output с сервером Nitro. Запускать нужно именно его, а не npm run dev.
npm run build
NITRO_PORT=3000 NITRO_HOST=127.0.0.1 node .output/server/index.mjs
curl -I http://127.0.0.1:3000
npx nuxi typecheck
На сервере удобно держать процесс под systemd или в контейнере, а перед ним — nginx с TLS. Не открывайте порт Node наружу: прокси должен передавать X-Forwarded-For и X-Forwarded-Proto, иначе редиректы и IP в логах будут неверными.
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Диагностика и типичные ошибки
- Гидратация не совпадает: проверьте данные, зависящие от
windowили времени. - 404 после деплоя: неверный
baseURLили пререндер. - Утечка секрета: значение попало в
publicилиNUXT_PUBLIC_. - Высокая нагрузка: тяжёлый SSR-запрос без кэша.
curl -s http://127.0.0.1:3000/api/health | jq
journalctl -u nuxt -n 100 --no-pager
du -sh .output .nuxt
node -e "console.log(process.version)"
Следите за версией Node: Nuxt 4 требует современный LTS. Обновляйте зависимости по одной, проверяя сборку и ключевые страницы, а не всё сразу перед пятничным релизом.
Итог
Nuxt 4 даёт Vue-разработчику полноценный серверный стек: файловый роутинг, SSR, Nitro-эндпоинты и гибкие правила кэширования. Держите клиентский и серверный код раздельно, задавайте секреты только через runtimeConfig, проверяйте гидратацию и метаданные, а в продакшене запускайте собранный .output за обратным прокси. Этого достаточно для большинства контентных и коммерческих сайтов.