Пошук уроків, статей та іншого контенту
Налаштуєте моніторинг доступності, ресурсів сервера та ключових показників Next.js-застосунку.
Моніторинг Next.js-застосунку варто поділити на три групи показників:
Доступність
чи відповідає застосунок на HTTP-запити;
HTTP-статус відповіді;
час відповіді;
кількість помилок 5xx.
Ресурси сервера
використання пам’яті процесом Node.js;
розмір JavaScript heap;
час роботи процесу;
використання CPU.
Показники користувацького досвіду
LCP — час відображення найбільшого елемента;
CLS — стабільність макета;
INP — швидкість реакції на взаємодію;
FCP — час першого відображення;
TTFB — час до отримання першого байта.
Моніторинг має не просто збирати дані, а допомагати відповідати на практичні запитання:
застосунок доступний для користувачів?
чи не закінчується пам’ять?
чи не зросла затримка відповіді?
Для перевірки доступності створимо спеціальний endpoint /api/health.
Він має:
виконуватися швидко;
не залежати від сторінки інтерфейсу;
повертати статус 200, якщо процес працює;
не містити приватних даних або секретів;
повертати заголовок Cache-Control: no-store, щоб перевірка не обслуговувалася з кешу.
Для App Router створіть файл app/api/health/route.ts:
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export function GET() {
const memory = process.memoryUsage();
const cpu = process.cpuUsage();
return NextResponse.json(
{
status: "ok",
service: "nextjs-app",
uptimeSeconds: Math.floor(process.uptime()),
memory: {
rssMb: Number((memory.rss / 1024 / 1024).toFixed(2)),
heapUsedMb: Number((memory.heapUsed / 1024 / 1024).toFixed(2)),
heapTotalMb: Number((memory.heapTotal / 1024 / 1024).toFixed(2)),
},
cpu: {
userSeconds: Number((cpu.user / 1_000_000).toFixed(2)),
systemSeconds: Number((cpu.system / 1_000_000).toFixed(2)),
},
timestamp: new Date().toISOString(),
},
{
status: 200,
headers: {
"Cache-Control": "no-store",
},
},
);
}Після запуску застосунку endpoint буде доступний за адресою:
/api/healthПеревірити його локально можна командою:
curl -i http://localhost:3000/api/healthПриклад відповіді:
{
"status": "ok",
"service": "nextjs-app",
"uptimeSeconds": 431,
"memory": {
"rssMb": 92.41,
"heapUsedMb": 38.12,
"heapTotalMb": 49.5
},
"cpu": {
"userSeconds": 12.43,
"systemSeconds": 2.18
},
"timestamp": "2026-08-17T10:20:00.000Z"
}Метод process.memoryUsage() повертає кілька значень:
rss — загальний обсяг пам’яті, зайнятий процесом Node.js;
heapUsed — пам’ять JavaScript, яка зараз використовується;
heapTotal — пам’ять, виділена під JavaScript heap.
Для виявлення витоків пам’яті важливо спостерігати за значенням heapUsed у часі. Одне вимірювання не показує проблему. Якщо після кожного запиту або періоду навантаження це значення стабільно зростає, застосунок може утримувати непотрібні об’єкти.
process.cpuUsage() повертає накопичене використання CPU поточним процесом у мікросекундах. Для моніторингу його слід знімати періодично та порівнювати різницю між вимірюваннями.
Зовнішній сервіс моніторингу має періодично виконувати HTTP-запит до endpoint:
GET https://your-domain.example/api/healthНаприклад, перевірку можна виконувати кожні 30–60 секунд.
Мінімальні правила перевірки:
очікувати статус 200;
встановити тайм-аут, наприклад 5–10 секунд;
вимірювати час відповіді;
створювати сповіщення після кількох послідовних невдалих перевірок;
не створювати сповіщення через одну випадкову мережеву помилку.
Корисно розділяти два стани:
застосунок недоступний — немає відповіді або повертається 5xx;
застосунок працює повільно — відповідь є, але її час перевищує допустимий поріг.
Не використовуйте для перевірки доступності головну сторінку, якщо вона виконує складні запити. Важка сторінка може бути повільною через проблему з базою даних, хоча сам HTTP-сервер ще доступний.
Показники з /api/health можна передавати до системи моніторингу. Зазвичай відстежують:
поточне та максимальне значення rss;
heapUsed і співвідношення heapUsed / heapTotal;
кількість перезапусків процесу;
час роботи процесу;
частоту відповідей із помилками 5xx;
час відповіді endpoint.
Конкретні пороги залежать від розміру сервера, але як початкові можна використати такі правила:
сповіщати, якщо endpoint недоступний протягом 2–3 перевірок;
сповіщати, якщо 5xx-відповіді перевищують 5% запитів за кілька хвилин;
сповіщати, якщо rss тривалий час наближається до доступної пам’яті сервера;
сповіщати, якщо час відповіді перевищує встановлений поріг;
окремо відстежувати часті перезапуски процесу.
Поріг має враховувати нормальну поведінку застосунку. Наприклад, короткий стрибок пам’яті під час збірки або запуску не обов’язково є аварійною ситуацією.
Endpoint зі статистикою сервера не повинен повертати змінні середовища, токени, налаштування бази даних або stack trace. Якщо детальні показники не мають бути публічними, доступ до endpoint потрібно обмежити на рівні інфраструктури або проксі.
Next.js може повідомляти про основні Web Vitals у браузері через useReportWebVitals.
Створіть клієнтський компонент app/web-vitals.tsx:
"use client";
import { useReportWebVitals } from "next/web-vitals";
export function WebVitals() {
useReportWebVitals((metric) => {
void fetch("/api/web-vitals", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating,
id: metric.id,
navigationType: metric.navigationType,
path: window.location.pathname,
userAgent: navigator.userAgent,
timestamp: new Date().toISOString(),
}),
keepalive: true,
});
});
return null;
}Підключіть компонент у app/layout.tsx:
import type { Metadata } from "next";
import { WebVitals } from "./web-vitals";
export const metadata: Metadata = {
title: "Моніторинг Next.js",
description: "Приклад збору Web Vitals",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="uk">
<body>
<WebVitals />
{children}
</body>
</html>
);
}useReportWebVitals викликає передану функцію, коли браузер вимірює відповідний показник. Важливо не блокувати відображення сторінки очікуванням відправлення метрики. Саме тому запит виконується без await.
Створіть файл app/api/web-vitals/route.ts:
import { NextResponse } from "next/server";
const allowedMetrics = new Set(["CLS", "FCP", "INP", "LCP", "TTFB"]);
export async function POST(request: Request) {
let body: unknown;
try {
body = await request.json();
} catch {
return NextResponse.json(
{ error: "Некоректний JSON" },
{ status: 400 },
);
}
if (
typeof body !== "object" ||
body === null ||
!("name" in body) ||
!("value" in body) ||
typeof body.name !== "string" ||
typeof body.value !== "number" ||
!allowedMetrics.has(body.name)
) {
return NextResponse.json(
{ error: "Некоректна метрика" },
{ status: 400 },
);
}
console.info(
JSON.stringify({
type: "web-vital",
name: body.name,
value: body.value,
receivedAt: new Date().toISOString(),
}),
);
return new Response(null, { status: 204 });
}У цьому прикладі метрика записується в журнал сервера. У production-застосунку журнали зазвичай передаються до системи збору логів або спеціального сховища метрик.
Не варто зберігати в метриці повний URL із query-параметрами, якщо вони можуть містити персональні дані або токени. Для аналізу зазвичай достатньо нормалізованого шляху сторінки.
LCP показує, скільки часу потрібно для відображення найбільшого видимого елемента сторінки.
Високе значення може бути пов’язане з:
повільною відповіддю сервера;
великим зображенням;
блокувальними ресурсами;
повільним завантаженням шрифтів;
великою кількістю JavaScript.
CLS показує, наскільки несподівано зміщується макет під час завантаження.
Причинами можуть бути:
зображення без заданих розмірів;
пізня поява банерів;
динамічно вставлений контент;
шрифти, які змінюють розміри тексту після завантаження.
INP характеризує затримку реакції інтерфейсу на взаємодію користувача.
Велике значення часто означає, що головний потік браузера зайнятий виконанням JavaScript. Для Next.js це може бути наслідком надмірної кількості клієнтських компонентів або важких обробників подій.
FCP показує, коли користувач уперше побачив частину вмісту сторінки.
TTFB показує час до отримання першого байта відповіді. Для Next.js він може погіршуватися через:
повільне виконання серверного компонента;
повільний запит до зовнішнього сервісу;
холодний запуск середовища виконання;
перевантаження сервера.
Оцінювати Web Vitals потрібно за великою кількістю реальних сесій, а не за одним локальним запуском. Важливими є медіана та високі перцентилі, наприклад p75.
Для пошуку причин проблеми корисно пов’язувати між собою:
час перевірки доступності;
HTTP-статус;
тривалість запиту;
використання пам’яті;
помилки сервера;
Web Vitals;
версію або ідентифікатор релізу.
Наприклад, якщо після нового релізу одночасно зросли TTFB, heapUsed і кількість 5xx, проблема, ймовірно, пов’язана із серверною частиною або залежністю, а не лише з браузером користувача.
Логи мають бути структурованими. JSON-записи простіше фільтрувати та групувати, ніж довільні текстові повідомлення.
Перед розгортанням перевірте:
/api/health повертає 200 після успішного запуску;
endpoint не кешується;
endpoint не містить секретів;
моніторинг перевіряє правильний домен;
задано тайм-аут запиту;
налаштовані сповіщення про недоступність;
Web Vitals надсилаються лише після завантаження сторінки;
помилки відправлення метрик не ламають інтерфейс;
у метрики не потрапляють персональні дані;
після релізу можна визначити версію застосунку, яка обробила запит.
Головна сторінка може мати складну логіку, запити до бази даних і великі ресурси. Її помилка не завжди означає, що весь процес недоступний.
Краще мати окремий легкий endpoint.
Cache-Control: no-storeПроксі або CDN можуть повернути стару успішну відповідь, навіть якщо застосунок уже недоступний. Для health check потрібно заборонити кешування.
Значення пам’яті та CPU можуть бути корисними для моніторингу, але stack trace, змінні середовища й конфігурація інфраструктури не повинні повертатися клієнту.
Мережеві збої трапляються. Сповіщення після однієї невдалої перевірки створюють багато шуму. Використовуйте кілька послідовних невдалих перевірок і період відновлення.
Сервер може працювати нормально, але сторінка може повільно реагувати в браузері. Тому ресурсні показники потрібно доповнювати Web Vitals.
Не надсилайте в метрики query-параметри, cookies, токени або довільний текст із введення користувача. Метрика повинна містити лише дані, потрібні для аналізу продуктивності.
Створіть окремий /api/health для перевірки доступності Next.js-застосунку.
Вимірюйте статус, час відповіді, uptime, пам’ять і використання CPU.
Налаштуйте зовнішню перевірку endpoint через короткі інтервали.
Використовуйте useReportWebVitals для збору LCP, CLS, INP, FCP і TTFB.
Передавайте метрики до централізованого сховища або системи журналювання.
Налаштовуйте сповіщення за трендами та перцентилями, а не за одиничними значеннями.
Не розкривайте через діагностичні endpoint секрети й персональні дані.