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 за обратным прокси. Этого достаточно для большинства контентных и коммерческих сайтов.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте