אופטימיזציית TTFB, CDN ו-Caching ב-2026: המדריך המעשי לשרת מהיר יותר

מדריך מעשי לאופטימיזציית TTFB: אסטרטגיות CDN, מדיניות Caching חכמה, HTTP/3, Early Hints ו-Edge Computing. דוגמאות קוד מעשיות להורדת זמן התגובה של השרת ושיפור חוויית המשתמש.

אופטימיזציית TTFB, CDN ו-Caching ב-2026: המדריך המעשי לשרת מהיר יותר

מבוא: הכל מתחיל בשרת

במאמרים הקודמים בסדרה שלנו על ביצועי אתרים, סקרנו לעומק את Core Web Vitals, צללנו לעולם אופטימיזציית תמונות עם AVIF ו-WebP, ופירקנו את אתגרי ה-JavaScript עם פיצול קוד ו-Tree Shaking. אבל יש שכבה אחת שדילגנו עליה עד עכשיו — השכבה שמגיעה לפני הכל: השרת עצמו.

אז חשבו על זה ככה: אתם יכולים לייעל את התמונות עד האחרון, למזער את ה-JavaScript לביט האחרון, ולהשתמש בכל טכניקת רינדור מתקדמת שקיימת — אבל אם השרת שלכם לוקח שנייה וחצי רק להגיב? הכל מתחיל מנקודת חיסרון. Time to First Byte (TTFB) מודד בדיוק את הזמן הזה — מרגע שהדפדפן שולח בקשה ועד שהבייט הראשון של התגובה מגיע חזרה. וב-2026, עם ציפיות משתמשים שרק ממשיכות לטפס, זה חשוב יותר מתמיד.

על פי נתוני Web Almanac 2025, אתרים שמשתמשים ב-CDN משיגים DNS resolution מהיר פי 2.5 מאתרים שמסתמכים על שרתי מקור בלבד. CDN מאפשר TTFB של 20-50 אלפיות שנייה לתוכן שנשמר ב-cache, לעומת 200-800 אלפיות שנייה לתגובות שרת רגילות. זה הבדל עצום.

במאמר הזה נכסה את כל מה שצריך לדעת — TTFB, אסטרטגיות CDN מודרניות, מדיניות Caching חכמה, HTTP/3 ו-QUIC, Early Hints, Edge Computing, ועוד הרבה. עם דוגמאות קוד מעשיות שתוכלו ליישם כבר היום. בואו נצלול פנימה.

חלק 1: הבנת TTFB — מה זה באמת מודד

מה קורה בין הבקשה לתגובה

TTFB נשמע כמו מדד פשוט, אבל בפועל הוא מורכב מכמה שלבים שכולם תורמים לזמן הכולל:

  1. DNS Lookup — תרגום שם הדומיין לכתובת IP (בדרך כלל 20-120ms)
  2. TCP Connection — יצירת חיבור TCP עם השרת (50-200ms בהתאם למרחק)
  3. TLS Handshake — הצפנת החיבור עם SSL/TLS (נוסף 50-150ms)
  4. Server Processing — השרת מעבד את הבקשה, מריץ קוד, פונה לדאטאבייס (משתנה מאוד)
  5. Response Transfer — הבייט הראשון של התגובה מגיע לדפדפן

הסף המומלץ של גוגל ל-TTFB הוא 800 אלפיות שנייה או פחות. אבל בואו נהיה כנים — כדי להשיג LCP טוב (מתחת ל-2.5 שניות), כדאי לשאוף ל-TTFB של 200-400ms לדפים דינמיים ו-מתחת ל-100ms לתוכן סטטי. 800ms זה סף ה"מקסימום", לא היעד.

איך מודדים TTFB בצורה נכונה

יש הבדל חשוב בין TTFB שנמדד בסביבת מעבדה (lab) לבין TTFB בשטח (field). כדי לקבל תמונה אמיתית, צריך את שניהם. הנה איך עושים את זה:

// מדידת TTFB בצד הלקוח עם Navigation Timing API
const [entry] = performance.getEntriesByType('navigation');

const ttfb = entry.responseStart - entry.requestStart;
const dnsTime = entry.domainLookupEnd - entry.domainLookupStart;
const tcpTime = entry.connectEnd - entry.connectStart;
const tlsTime = entry.secureConnectionStart > 0
  ? entry.connectEnd - entry.secureConnectionStart
  : 0;
const serverTime = entry.responseStart - entry.requestEnd;

console.log(`TTFB: ${ttfb.toFixed(2)}ms`);
console.log(`DNS: ${dnsTime.toFixed(2)}ms`);
console.log(`TCP: ${tcpTime.toFixed(2)}ms`);
console.log(`TLS: ${tlsTime.toFixed(2)}ms`);
console.log(`Server Processing: ${serverTime.toFixed(2)}ms`);

כלי נוסף ששווה להכיר הוא Server-Timing header — הוא מאפשר לשרת לדווח על תזמונים פנימיים ישירות לדפדפן, מה שחוסך המון ניחושים בדיבאג:

// NGINX - הוספת Server-Timing header
// add_header Server-Timing "db;dur=23.5, render;dur=45.2, total;dur=68.7";

// הגדרת Server-Timing דינמית ב-Node.js
app.use((req, res, next) => {
  const start = performance.now();

  res.on('finish', () => {
    const duration = performance.now() - start;
    res.setHeader('Server-Timing', `total;dur=${duration.toFixed(1)}`);
  });

  next();
});

בדפדפן, אתם יכולים לראות את ה-Server-Timing בלשונית Network של DevTools. ברגע שמתרגלים לזה, קשה לעבוד בלי — זה נותן שקיפות מלאה על מה בדיוק לוקח זמן בצד השרת.

חלק 2: אופטימיזציית שרת המקור

אופטימיזציית דאטאבייס

מניסיון, בהרבה מקרים צוואר הבקבוק הגדול ביותר ב-TTFB הוא הדאטאבייס. שאילתות לא מיועלות, חוסר באינדקסים, וחיבורים שלא מנוהלים כראוי — כל אלה מוסיפים עשרות עד מאות אלפיות שנייה לכל בקשה. וזה מצטבר מהר.

// Node.js - שימוש ב-Connection Pool עם pg (PostgreSQL)
import { Pool } from 'pg';

const pool = new Pool({
  host: process.env.DB_HOST,
  port: 5432,
  database: process.env.DB_NAME,
  max: 20,                    // מקסימום חיבורים
  idleTimeoutMillis: 30000,   // סגירת חיבורים לא פעילים
  connectionTimeoutMillis: 2000, // timeout לחיבור חדש
});

// שימוש ב-prepared statements למניעת parsing חוזר
const getUser = {
  name: 'get-user',
  text: 'SELECT id, name, email FROM users WHERE id = $1',
  values: [],
};

app.get('/api/user/:id', async (req, res) => {
  const start = performance.now();

  const result = await pool.query({
    ...getUser,
    values: [req.params.id],
  });

  const dbTime = performance.now() - start;
  res.setHeader('Server-Timing', `db;dur=${dbTime.toFixed(1)}`);
  res.json(result.rows[0]);
});

Application-Level Caching

שכבת cache ברמת האפליקציה יכולה לחסוך פניות לדאטאבייס ולחתוך את ה-TTFB בצורה דרמטית. Redis הוא הפתרון הפופולרי ביותר — וביושר, לטובה. הוא מציע תגובות של מיקרושניות, וההגדרה שלו ממש לא מסובכת:

import Redis from 'ioredis';

const redis = new Redis({
  host: process.env.REDIS_HOST,
  port: 6379,
  maxRetriesPerRequest: 3,
});

// Cache-aside pattern עם TTL
async function getCachedData(key, fetchFn, ttlSeconds = 300) {
  // ניסיון לקרוא מה-cache
  const cached = await redis.get(key);
  if (cached) {
    return JSON.parse(cached);
  }

  // אם לא נמצא — שליפה מהמקור ושמירה ב-cache
  const data = await fetchFn();
  await redis.setex(key, ttlSeconds, JSON.stringify(data));
  return data;
}

// שימוש
app.get('/api/products/:category', async (req, res) => {
  const { category } = req.params;
  const cacheKey = `products:${category}`;

  const products = await getCachedData(
    cacheKey,
    () => db.query('SELECT * FROM products WHERE category = $1', [category]),
    600  // 10 דקות TTL
  );

  res.json(products);
});

דחיסת תגובות — Brotli ו-Gzip

דחיסת תגובות HTTP מצמצמת את כמות הנתונים שעוברים ברשת. Brotli מציע דחיסה טובה ב-15-25% מ-Gzip, ונתמך בכל הדפדפנים המודרניים. אין ממש סיבה לא להפעיל את זה:

# NGINX - הגדרת דחיסה עם Brotli ו-Gzip fallback
# Brotli
brotli on;
brotli_comp_level 6;
brotli_types text/html text/css text/javascript
             application/javascript application/json
             application/xml image/svg+xml;

# Gzip כגיבוי
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types text/html text/css text/javascript
           application/javascript application/json
           application/xml image/svg+xml;

טיפ חשוב: עבור קבצים סטטיים, אפשר (ורצוי) לבצע דחיסה מראש ברמת Brotli 11 — הדחיסה המקסימלית — ולשמור את הקבצים הדחוסים מראש. ככה השרת רק מגיש קבצים מוכנים, בלי לבזבז CPU על דחיסה בזמן אמת:

# דחיסה מראש של קבצים סטטיים
find dist -type f \( -name "*.js" -o -name "*.css" -o -name "*.html" -o -name "*.svg" \) \
  -exec brotli -k -q 11 {} \;

# NGINX - הגשת קבצים דחוסים מראש
location /assets/ {
  brotli_static on;
  gzip_static on;
  expires 1y;
  add_header Cache-Control "public, immutable";
}

חלק 3: HTTP/3, QUIC ופרוטוקולים מודרניים

למה HTTP/3 משנה את כללי המשחק

HTTP/3, שמבוסס על פרוטוקול QUIC של Google, הוא שינוי מהותי באופן שבו דפדפנים ושרתים מתקשרים. במקום TCP הישן והטוב, הוא משתמש ב-UDP עם הצפנה מובנית. ומה זה נותן בפועל?

  • חיבור מהיר יותר: HTTP/3 משלב את ה-TLS handshake עם יצירת החיבור. במקום 2-3 סבבי נסיעה (round trips) — רק סבב אחד, או אפילו אפס (0-RTT) לחיבורים חוזרים
  • אין Head-of-Line Blocking: ב-HTTP/2, אובדן חבילה אחת בחיבור TCP חוסם את כל הזרמים. ב-HTTP/3 כל זרם עצמאי — אובדן חבילה בזרם אחד לא משפיע על אחרים. זה הבדל גדול
  • ביצועים טובים יותר ברשתות לא יציבות: מושלם לרשתות סלולריות שבהן חבילות הולכות לאיבוד. דוח Akamai מ-2025 מראה שיפור latency של 30% במובייל
  • Migration ללא ניתוק: QUIC תומך ב-connection migration — כשמשתמש עובר מ-WiFi לסלולרי, החיבור פשוט ממשיך בלי להתחיל מחדש (וזה מרגיש כמו קסם)

נכון לאוקטובר 2025, HTTP/3 מכסה כ-35% מתעבורת האינטרנט העולמית על פי Cloudflare, וכל הדפדפנים המובילים תומכים בו כברירת מחדל. מחקר של Google הראה ירידה ממוצעת של 8% בזמני טעינה בדסקטופ ועד 16% למשתמשים האיטיים ביותר. ברצינות, אם אתם עדיין לא על HTTP/3 — כדאי לשקול מחדש.

הפעלת HTTP/3 ב-NGINX

# nginx.conf - הפעלת HTTP/3 ו-QUIC
server {
    # HTTP/3 עם QUIC
    listen 443 quic reuseport;
    listen 443 ssl;

    http2 on;
    http3 on;

    ssl_certificate     /etc/ssl/certs/site.crt;
    ssl_certificate_key /etc/ssl/private/site.key;

    # פרוטוקולי TLS מודרניים
    ssl_protocols TLSv1.3;
    ssl_early_data on;  # תמיכה ב-0-RTT

    # הודעה לדפדפן שיש תמיכה ב-HTTP/3
    add_header Alt-Svc 'h3=":443"; ma=86400';

    # הגדרות QUIC
    quic_retry on;
    quic_gso on;

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

0-RTT — חיבור ללא המתנה

אחד הפיצ'רים המרשימים ביותר של QUIC הוא 0-RTT (Zero Round Trip Time). כשמשתמש מבקר באתר שלכם בפעם השנייה, הדפדפן שולח נתונים מוצפנים כבר עם חבילת החיבור הראשונה — בלי לחכות ל-handshake בכלל. זה חוסך 50-150ms בממוצע, וזה מורגש מאוד בניידים.

אבל שימו לב: 0-RTT מגיע עם סיכון קטן של replay attacks. לכן, כדאי להשתמש בו רק לבקשות GET (קריאה בלבד) ולוודא שה-backend שלכם סובלני לבקשות חוזרות. זה לא show-stopper, אבל חשוב לדעת.

חלק 4: אסטרטגיות CDN מודרניות

בחירת CDN — מעבר למטריקות בסיסיות

ב-2026, שוק ה-CDN הרבה יותר תחרותי ומתוחכם ממה שהיה. Cloudflare מפעיל רשת של מעל 330 מרכזי נתונים ביותר מ-120 מדינות, עם תמיכה מלאה ב-HTTP/3 ו-Workers לחישוב בקצה. Fastly מתמחה ב-cache purging מהיר להפליא — פחות מ-150 אלפיות שנייה גלובלית — מה שהופך אותו למצוין לאתרים שבהם עדכניות התוכן קריטית. בנוסף, Fastly משתמש ב-WebAssembly ב-Compute@Edge, עם cold starts כמעט אפסיים.

הנה כמה שיקולים שכדאי לקחת בחשבון בבחירת CDN:

  • כיסוי גיאוגרפי: אם הקהל שלכם במזרח התיכון, ודאו שיש PoPs (Points of Presence) קרובים — זה משנה הכל
  • Edge Computing: האם ה-CDN מאפשר להריץ קוד בקצה? (Workers, Edge Functions)
  • Cache Purging: כמה מהר אפשר לבטל cache? קריטי לאתרי חדשות ו-eCommerce
  • פרוטוקולים: תמיכה ב-HTTP/3, QUIC, ו-Early Hints
  • אינטגרציה: תמיכה ב-framework שלכם (Next.js, Nuxt, Astro)

Multi-Tier Caching Architecture

ארכיטקטורת cache מרובת שכבות היא הדרך הנכונה לבנות מערכת CDN רצינית. הרעיון פשוט — בודקים cache בכל שכבה לפני שמגיעים לשרת המקור:

// Cloudflare Workers - Multi-tier caching logic
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const cacheKey = new Request(url.toString(), request);

    // שכבה 1: Cloudflare Edge Cache
    const cache = caches.default;
    let response = await cache.match(cacheKey);

    if (response) {
      response = new Response(response.body, response);
      response.headers.set('X-Cache', 'HIT-Edge');
      return response;
    }

    // שכבה 2: KV Storage (distributed cache)
    const kvCached = await env.CACHE_KV.get(url.pathname, 'text');
    if (kvCached) {
      response = new Response(kvCached, {
        headers: {
          'Content-Type': 'text/html',
          'Cache-Control': 'public, max-age=300, s-maxage=3600',
          'X-Cache': 'HIT-KV',
        },
      });
      await cache.put(cacheKey, response.clone());
      return response;
    }

    // שכבה 3: Origin
    response = await fetch(request);
    const body = await response.text();

    await env.CACHE_KV.put(url.pathname, body, {
      expirationTtl: 3600,
    });

    const cachedResponse = new Response(body, {
      headers: {
        'Content-Type': 'text/html',
        'Cache-Control': 'public, max-age=300, s-maxage=3600',
        'X-Cache': 'MISS',
      },
    });
    await cache.put(cacheKey, cachedResponse.clone());

    return cachedResponse;
  },
};

Cache Invalidation — הבעיה הקשה ביותר

כידוע, Cache Invalidation הוא אחת משתי הבעיות הקשות ביותר במדעי המחשב (יחד עם שמות משתנים ו-off-by-one errors). בואו נדבר על אסטרטגיות שבאמת עובדות:

  • Content Hashing: לקבצים סטטיים, שימוש ב-hash בשם הקובץ (למשל main.a1b2c3.js) מאפשר cache ארוך מאוד בלי לדאוג לביטול — אם התוכן משתנה, גם השם משתנה
  • Surrogate Keys / Cache Tags: תיוג תגובות עם מזהים שמאפשרים ביטול סלקטיבי. אם מוצר מתעדכן, מבטלים רק את הדפים הרלוונטיים
  • Stale-While-Revalidate: מגישים תוכן ישן מה-cache בזמן שמבצעים רענון ברקע. המשתמש מקבל תגובה מיידית — כולם מרוצים
# Cache-Control headers - אסטרטגיה לפי סוג תוכן

# קבצים סטטיים עם hash (JS, CSS, תמונות)
Cache-Control: public, max-age=31536000, immutable

# HTML דינמי
Cache-Control: public, max-age=0, s-maxage=600, stale-while-revalidate=86400

# API responses
Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=600

# תוכן מותאם אישית (דורש אימות)
Cache-Control: private, no-cache, must-revalidate

חלק 5: מדיניות Caching מתקדמת

הבנת הדירקטיבות של Cache-Control

Cache-Control הוא ה-header החשוב ביותר בניהול cache, אבל הרבה מפתחים לא מנצלים את מלוא הפוטנציאל שלו. בואו נפרק את הדירקטיבות העיקריות:

  • max-age=N — התגובה נשארת טרייה למשך N שניות מרגע היצירה
  • s-maxage=N — אותו דבר, אבל רק עבור shared caches כמו CDN ו-proxy. גובר על max-age
  • stale-while-revalidate=N — מאפשר להגיש תגובה שפג תוקפה למשך N שניות נוספות, בזמן שמתבצע רענון ברקע
  • stale-if-error=N — מאפשר להגיש תגובה ישנה אם השרת מחזיר שגיאה. מאוד שימושי ל-resilience
  • immutable — אומר לדפדפן שהתוכן לעולם לא ישתנה, אפילו ב-refresh
  • no-store — אל תשמרו את התגובה הזו בשום cache, בשום מקום

וזה המקום לנקודה חשובה: הטעות הנפוצה ביותר שאני רואה (ואני רואה את זה הרבה) היא שימוש ב-no-cache כשהכוונה היא no-store. no-cache לא אומר "אל תעשה cache" — הוא אומר "תשמור ב-cache, אבל תבדוק עם השרת לפני כל שימוש". זה הבדל קריטי שגורם לבאגים מבלבלים.

Stale-While-Revalidate בפועל

stale-while-revalidate הוא, לדעתי, אחד הכלים החזקים ביותר שעומדים לרשותכם. הרעיון יפהפה בפשטותו: במקום שהמשתמש יחכה לתגובה חדשה מהשרת, הוא מקבל מיד את הגרסה הקודמת מה-cache, ובמקביל מתבצע רענון שקט ברקע. התוצאה? TTFB כמעט אפסי לרוב הבקשות.

# דוגמה: דף מוצר שמתעדכן כל 10 דקות
Cache-Control: public, max-age=600, s-maxage=600, stale-while-revalidate=86400, stale-if-error=259200

# פירוש:
# - 600 שניות (10 דקות): התגובה טרייה לחלוטין
# - 86400 שניות (24 שעות): אפשר להגיש גרסה ישנה בזמן רענון
# - 259200 שניות (3 ימים): אפשר להגיש גרסה ישנה אם השרת מחזיר שגיאה

Vary Header — Cache לפי הקשר

ה-Vary header אומר ל-cache לשמור גרסאות שונות של אותה URL בהתאם ל-headers של הבקשה. זה קריטי כשאתם מגישים תוכן שונה לפי שפה, מכשיר, או קידוד:

# שמירת גרסאות נפרדות לפי קידוד ושפה
Vary: Accept-Encoding, Accept-Language

# שמירת גרסאות לפי פורמט תמונה
Vary: Accept

# זהירות! Vary: User-Agent הורס את ה-cache hit rate
# כי יש אלפי User-Agent strings שונים
# עדיף להשתמש ב-Client Hints:
Vary: Sec-CH-UA-Mobile, Sec-CH-DPR

Service Worker Caching — שכבה נוספת

Service Workers נותנים שליטה מלאה על ה-cache בצד הלקוח. זו עוד שכבת הגנה שיושבת בין הדפדפן לרשת, ויכולה לתת חוויה מיידית למשתמשים חוזרים:

// service-worker.js - Stale-While-Revalidate strategy
const CACHE_NAME = 'app-cache-v1';
const CACHEABLE_URLS = ['/api/products', '/api/categories'];

self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url);

  if (event.request.method !== 'GET' ||
      !CACHEABLE_URLS.some(path => url.pathname.startsWith(path))) {
    return;
  }

  event.respondWith(
    caches.open(CACHE_NAME).then(async (cache) => {
      const cachedResponse = await cache.match(event.request);

      // רענון ברקע — תמיד
      const fetchPromise = fetch(event.request).then((networkResponse) => {
        if (networkResponse.ok) {
          cache.put(event.request, networkResponse.clone());
        }
        return networkResponse;
      });

      // אם יש ב-cache — מחזירים מיד, אם אין — מחכים לרשת
      return cachedResponse || fetchPromise;
    })
  );
});

חלק 6: Early Hints (103) — שיפור נתפס ללא עלות

מה זה Early Hints ואיך זה עובד

Early Hints היא תכונה מגניבה שמשתמשת בקוד סטטוס HTTP 103. הרעיון: לאפשר לשרת לשלוח "רמזים" לדפדפן עוד לפני שהתגובה המלאה מוכנה. בזמן שהשרת עסוק בעיבוד הבקשה, הדפדפן כבר מתחיל להוריד משאבים קריטיים — גיליונות עיצוב, פונטים, ותמונת ה-LCP.

# תגובת 103 Early Hints
HTTP/1.1 103 Early Hints
Link: </styles/main.css>; rel=preload; as=style
Link: </fonts/heebo.woff2>; rel=preload; as=font; crossorigin
Link: </images/hero.avif>; rel=preload; as=image

# ... השרת ממשיך לעבד ...

# תגובה סופית 200 OK
HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: public, max-age=300

על פי נתוני HTTP Archive 2025, אחוז האימוץ של Early Hints עדיין נמוך — רק 0.012% מבקשות CDN. זה מפתיע, כי Shopify דיווחו על שיפורים משמעותיים ב-LCP אחרי הטמעת Early Hints. הדפדפן מתחיל להוריד משאבים קריטיים עוד לפני שה-HTML מגיע — וזה זמן חינם.

הטמעה ב-NGINX

# NGINX - שליחת 103 Early Hints
server {
    listen 443 ssl;
    http2 on;

    location / {
        http2_push_preload on;

        add_header Link "</css/main.css>; rel=preload; as=style" always;
        add_header Link "</fonts/heebo-v1.woff2>; rel=preload; as=font; crossorigin" always;

        proxy_pass http://backend;
    }
}

הטמעה ב-Cloudflare

Cloudflare תומך ב-Early Hints באופן אוטומטי — הוא סורק את ה-Link headers ותגיות <link rel="preload"> בתגובות קודמות, שומר אותם, ושולח אותם כ-103 בבקשות הבאות. בעצם, לא צריך לשנות כלום בקוד — רק לוודא שיש preload hints ב-HTML שלכם.

חלק 7: Edge Computing ורינדור בקצה

למה Edge SSR הוא העתיד

Edge Computing מביא את הלוגיקה של השרת קרוב ככל האפשר למשתמש. במקום שבקשה תטייל כל הדרך לשרת מקור ב-US-East-1, היא מטופלת בנקודת קצה שנמצאת אולי 50 קילומטר מהמשתמש. התוצאה מדברת בעד עצמה: TTFB של 37-60ms כש-function חם, לעומת 103-900ms לשרת מקור רגיל.

על פי הערכות תעשייתיות, עד סוף 2026 Edge SSR צפוי לטפל ביותר מ-50% מעומסי ה-SSR. הפריימוורקים המובילים — Next.js, Nuxt 3 ו-Astro — כולם תומכים ב-edge rendering מהקופסה, מה שמוריד את חסם הכניסה משמעותית.

Edge SSR עם Next.js

// app/products/page.tsx - Edge Runtime ב-Next.js
export const runtime = 'edge';

export default async function ProductsPage() {
  const products = await fetch('https://api.example.com/products', {
    next: { revalidate: 300 },  // ISR - רענון כל 5 דקות
  }).then(res => res.json());

  return (
    <main>
      <h1>מוצרים</h1>
      <ul>
        {products.map(product => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </main>
  );
}

Cloudflare Workers — לוגיקה מלאה בקצה

// Edge-side personalization
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const country = request.cf?.country || 'US';
    const language = request.headers.get('Accept-Language')?.split(',')[0] || 'en';

    const cacheKey = `${url.pathname}:${country}:${language}`;
    const cached = await env.CONTENT_KV.get(cacheKey);

    if (cached) {
      return new Response(cached, {
        headers: {
          'Content-Type': 'text/html',
          'Cache-Control': 'public, max-age=300',
          'X-Edge-Location': request.cf?.colo || 'unknown',
        },
      });
    }

    const response = await fetch(request);
    const html = await response.text();

    await env.CONTENT_KV.put(cacheKey, html, { expirationTtl: 300 });

    return new Response(html, {
      headers: {
        'Content-Type': 'text/html',
        'Cache-Control': 'public, max-age=300',
      },
    });
  },
};

Nuxt 3 עם Nitro — Edge Rendering

Nuxt 3 משתמש ב-Nitro כשכבת שרת, שמאפשר deployment ישיר ל-edge providers. חברת eCommerce שעברה ל-Nuxt 3 עם Edge Rendering דיווחה על ירידה של 40% ב-TTFB ו-עלייה של 60% בדפים שנסרקו על ידי Google. המספרים מדברים.

// nuxt.config.ts - הגדרת Edge Rendering
export default defineNuxtConfig({
  nitro: {
    preset: 'cloudflare-pages',

    routeRules: {
      '/about': { prerender: true },
      '/blog/**': { swr: 3600 },
      '/products/**': { swr: 300 },
      '/api/**': {
        cache: { maxAge: 60 },
        cors: true,
      },
      '/': { swr: 600 },
    },
  },
});

חלק 8: מדידה, ניטור ואופטימיזציה מתמדת

RUM (Real User Monitoring) ל-TTFB

מדידה סינתטית עם Lighthouse ו-WebPageTest חשובה, אבל היא לא מספרת את הסיפור המלא. Real User Monitoring נותן לכם את מה שבאמת חשוב — איך TTFB נראה למשתמשים אמיתיים, ברשתות שונות, ממיקומים שונים:

// שליחת נתוני TTFB אמיתיים לשירות Analytics
function reportTTFB() {
  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      if (entry.entryType === 'navigation') {
        const ttfb = entry.responseStart - entry.requestStart;
        const dns = entry.domainLookupEnd - entry.domainLookupStart;
        const connection = entry.connectEnd - entry.connectStart;
        const serverTime = entry.responseStart - entry.requestEnd;

        navigator.sendBeacon('/api/metrics', JSON.stringify({
          metric: 'TTFB',
          value: ttfb,
          breakdown: { dns, connection, serverTime },
          url: window.location.pathname,
          connectionType: navigator.connection?.effectiveType || 'unknown',
          timestamp: Date.now(),
        }));
      }
    }
  });

  observer.observe({ type: 'navigation', buffered: true });
}

if (document.readyState === 'complete') {
  reportTTFB();
} else {
  window.addEventListener('load', reportTTFB);
}

ניטור Cache Hit Rate

שאפו ל-cache hit rate של 90% ומעלה. אם אתם מתחת לזה, כנראה שיש מה לשפר במדיניות ה-cache:

// ניתוח Cache Hit Rate מ-CDN logs
// Cloudflare Workers Analytics
export default {
  async fetch(request, env) {
    const response = await fetch(request);

    const cacheStatus = response.headers.get('CF-Cache-Status');
    // ערכים אפשריים: HIT, MISS, EXPIRED, STALE, BYPASS, DYNAMIC

    env.ANALYTICS.writeDataPoint({
      blobs: [cacheStatus, request.url],
      doubles: [cacheStatus === 'HIT' ? 1 : 0],
      indexes: [request.cf?.colo || 'unknown'],
    });

    return response;
  },
};

רשימת בדיקה — אופטימיזציית TTFB מקיפה

לפני שמסיימים, הנה רשימת בדיקה מקיפה. מומלץ לעבור עליה ולסמן — תתפלאו כמה דברים קל לשפר:

  1. שרת מקור: האם הדאטאבייס מיועל? יש connection pooling? יש application-level cache?
  2. דחיסה: האם Brotli מופעל? האם קבצים סטטיים דחוסים מראש?
  3. פרוטוקולים: האם HTTP/3 ו-QUIC מופעלים? TLS 1.3 בשימוש?
  4. CDN: האם כל התוכן הסטטי עובר דרך CDN? cache hit rate מעל 90%?
  5. Cache-Control: האם לכל סוג תוכן יש מדיניות cache מתאימה? stale-while-revalidate בשימוש?
  6. Early Hints: האם 103 Early Hints מוגדר למשאבים קריטיים?
  7. Edge Computing: האם דפים דינמיים יכולים לעבור ל-Edge SSR?
  8. ניטור: יש RUM שמודד TTFB בשטח? יש התראות לירידת ביצועים?

סיכום: מהשרת למשתמש — כל מילישנייה חשובה

אופטימיזציית TTFB זה לא עניין של טכנולוגיה אחת — זה שילוב של שרת מקור מיועל, מדיניות caching חכמה, CDN עם edge computing, ופרוטוקולים מודרניים. כל שכבה מוסיפה שיפור, וביחד ההשפעה דרמטית.

הנה סיכום מהיר של ההשפעה לפי טכניקה:

  • אופטימיזציית דאטאבייס + Redis cache: חיסכון של 50-200ms
  • דחיסת Brotli: הקטנת תעבורה ב-15-25%
  • HTTP/3 + QUIC: שיפור של 8-30% ב-latency, במיוחד במובייל
  • CDN עם edge caching: TTFB של 20-50ms במקום 200-800ms
  • Stale-While-Revalidate: TTFB כמעט אפסי לבקשות חוזרות
  • Early Hints: חיסכון של 100-300ms בהורדת משאבים קריטיים
  • Edge SSR: TTFB של 37-60ms לתוכן דינמי

במאמר הבא בסדרה, נצלול לעולם ביצועי הרינדור — Critical Rendering Path, CSS containment, ואסטרטגיות לשיפור ה-INP בצד הלקוח. בינתיים, תתחילו עם רשימת הבדיקה למעלה ותראו כמה מילישניות אתם יכולים לגלח. כל אחת מהן חשובה.

אודות הכותב Editorial Team

Our team of expert writers and editors.