SSR для Laravel + React: Как настроить серверный рендеринг через Inertia
За последние годы я перевёл на серверный рендеринг больше десятка Inertia-проектов — от каталогов до медиаплатформ. В моих проектах после внедрения SSR LCP обычно снижался с 2,5–4 до 0,8–1,4 секунды. Конкретный результат зависит от сервера, кеширования, изображений, шрифтов и размера клиентского JavaScript-бандла.
Ниже — рабочая настройка SSR для существующих проектов на Laravel 12/13, Inertia 2.x, React 19 и Vite 7.
Зачем нужен SSR
Обычный Inertia-проект без SSR возвращает браузеру корневой элемент приложения и данные страницы. Основной React-интерфейс появляется после загрузки и выполнения JavaScript.
Google и Яндекс умеют выполнять JavaScript, но SSR позволяет сразу вернуть содержательную HTML-разметку. Это упрощает обработку страниц поисковыми системами, социальными сетями, Telegram и другими парсерами. (Google for Developers)
При SSR отдельный Node-процесс рендерит React-компоненты в HTML до отправки ответа. После загрузки страницы React выполняет гидрацию и подключает обработчики событий к уже готовой разметке.
Шаг 1. Устанавливаем зависимости
Если Inertia и Ziggy ещё не установлены:
composer require inertiajs/inertia-laravel tightenco/ziggy
npm install @inertiajs/react react react-dom @vitejs/plugin-react
Создаём middleware:
php artisan inertia:middleware
Регистрируем его в bootstrap/app.php:
<?php
use App\Http\Middleware\HandleInertiaRequests;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [
HandleInertiaRequests::class,
]);
})
->create();
Корневой шаблон resources/views/app.blade.php:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta
name="viewport"
content="width=device-width, initial-scale=1"
>
@viteReactRefresh
@vite(['resources/js/app.jsx'])
@inertiaHead
</head>
<body>
@inertia
</body>
</html>
Шаг 2. Настраиваем Ziggy
Конфигурацию Ziggy необходимо передавать через Inertia props. Не записывайте route() и данные текущего запроса в global или globalThis: Node-процесс постоянно работает и обслуживает разные запросы.
Открываем app/Http/Middleware/HandleInertiaRequests.php:
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Inertia\Middleware;
use Tighten\Ziggy\Ziggy;
class HandleInertiaRequests extends Middleware
{
protected $rootView = 'app';
public function share(Request $request): array
{
return [
...parent::share($request),
'locale' => app()->getLocale(),
'timezone' => config('app.timezone'),
'ziggy' => fn (): array =>