21 / 36
Что такое Time to First Byte (TTFB) и как его оптимизировать?
Полный ответ
TTFB складывается из нескольких этапов:
JavaScript
Браузер Сервер
| |
| --- DNS Lookup ---------------------------> |
| --- TCP Handshake ------------------------> |
| --- TLS Handshake (если HTTPS) ----------> |
| --- Отправка HTTP-запроса ----------------> |
| [Обработка на сервере] |
| <-- Первый байт ответа ------------------- |
| |
| <====== Всё это TTFB ===============> |
Измерение TTFB через Performance API:
JavaScript
// Через Navigation Timing API
const [entry] = performance.getEntriesByType('navigation');
const ttfb = entry.responseStart - entry.requestStart;
console.log(`TTFB: ${ttfb.toFixed(2)} ms`);
// Более детальная разбивка
const dns = entry.domainLookupEnd - entry.domainLookupStart;
const tcp = entry.connectEnd - entry.connectStart;
const tls = entry.secureConnectionStart > 0
? entry.connectEnd - entry.secureConnectionStart
: 0;
const serverTime = entry.responseStart - entry.requestStart;
console.log(`DNS: ${dns.toFixed(2)} ms`);
console.log(`TCP: ${tcp.toFixed(2)} ms`);
console.log(`TLS: ${tls.toFixed(2)} ms`);
console.log(`Server processing: ${serverTime.toFixed(2)} ms`);
Способы оптимизации:
JavaScript
// 1. Серверный кеш -- самый большой выигрыш
// Express + node-cache
import NodeCache from 'node-cache';
const cache = new NodeCache({ stdTTL: 300 }); // 5 минут
app.get('/api/products', (req, res) => {
const cached = cache.get('products');
if (cached) {
return res.json(cached); // мгновенный ответ из кеша
}
const products = db.query('SELECT * FROM products');
cache.set('products', products);
res.json(products);
});
JavaScript
// 2. Оптимизация БД-запросов
// ❌ N+1 запросов -- каждый пользователь порождает отдельный запрос
app.get('/api/posts', async (req, res) => {
const posts = await db.query('SELECT * FROM posts LIMIT 20');
for (const post of posts) {
post.author = await db.query(
'SELECT * FROM users WHERE id = ?', [post.authorId]
);
}
res.json(posts);
});
// ✅ Один запрос с JOIN
app.get('/api/posts', async (req, res) => {
const posts = await db.query(`
SELECT p.*, u.name as authorName, u.avatar as authorAvatar
FROM posts p
JOIN users u ON p.authorId = u.id
LIMIT 20
`);
res.json(posts);
});
HTML
<!-- 3. Ресурсные подсказки в HTML -->
<!-- DNS prefetch для сторонних доменов -->
<link rel="dns-prefetch" href="https://api.example.com">
<!-- Preconnect -- DNS + TCP + TLS заранее -->
<link rel="preconnect" href="https://api.example.com" crossorigin>
<!-- 103 Early Hints -- сервер отправляет подсказки до основного ответа -->
Nginx
# 4. Настройка CDN / nginx для кеширования
# nginx.conf
proxy_cache_path /var/cache/nginx levels=1:2
keys_zone=app_cache:10m max_size=1g inactive=60m;
server {
location /api/ {
proxy_cache app_cache;
proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout updating;
proxy_pass http://backend;
}
}
📌 Реальные кейсы
Диагностика медленного TTFB:
JavaScript
// Скрипт для мониторинга TTFB в production
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'navigation') {
const ttfb = entry.responseStart - entry.requestStart;
// Отправляем метрику в аналитику
if (ttfb > 800) {
sendToAnalytics('slow_ttfb', {
ttfb,
url: entry.name,
transferSize: entry.transferSize,
serverTiming: entry.serverTiming,
});
}
}
}
});
observer.observe({ type: 'navigation', buffered: true });
Server-Timing заголовок для отладки серверной части:
JavaScript
// Сервер показывает, на что ушло время
app.get('/api/data', async (req, res) => {
const start = performance.now();
const dbStart = performance.now();
const data = await db.query('SELECT ...');
const dbTime = performance.now() - dbStart;
const cacheStart = performance.now();
await redis.set('key', JSON.stringify(data));
const cacheTime = performance.now() - cacheStart;
res.set('Server-Timing', [
`db;dur=${dbTime.toFixed(1)};desc="Database"`,
`cache;dur=${cacheTime.toFixed(1)};desc="Cache write"`,
`total;dur=${(performance.now() - start).toFixed(1)}`,
].join(', '));
res.json(data);
});
// Видно в DevTools на вкладке Network (Timing)
Резюме
TTFB -- первая метрика в цепочке загрузки страницы. Если TTFB медленный, все остальные метрики (FCP, LCP) будут плохими автоматически. Основные точки оптимизации: серверный кеш, CDN, оптимизация запросов к БД, preconnect/dns-prefetch, HTTP/2 и 103 Early Hints.