אופטימיזציה של TTFB: מ-1.2 שניות ל-150 אלפיות השנייה בשבוע
לאחרונה, בעל חנות מסחר אלקטרוני פנה אלינו עם תלונה על טעינה איטית — TTFB היה 1.2 שניות בעמוד הבית. לאחר בדיקה מקיפה, יישמנו מטמון עמוד מלא, הגדרנו Nginx FastCGI cache, אופטימיזציה של שאילתות מסד הנתונים, וחיברנו Redis. תוצאה: TTFB ירד ל-150 אלפיות השנייה, LCP השתפר ב-40%, וההמרות עלו ב-12%. במאמר זה, נפרק בדיוק אילו צעדים הובילו לתוצאה זו.
TTFB (Time to First Byte) הוא הזמן משליחת בקשה ועד קבלת הבייט הראשון של התשובה. הוא כולל פתרון DNS, הקמת חיבור (TCP+TLS), העברת בקשה, עיבוד שרת ויצירת תשובה. החלק הניתן לניהול ביותר הוא עיבוד השרת. זה בדיוק מה שאנחנו מייעלים. לפי תיעוד Web Vitals של Google, TTFB משפיע ישירות על LCP ו-INP.
למה TTFB קריטי עבור Core Web Vitals?
Google משתמשת ב-TTFB כמדד לתגובה הראשונה. אם השרת לוקח יותר מ-600 אלפיות השנייה, אפילו פרונטאנד מושלם לא יציל את LCP. ראינו זאת בעשרות פרויקטים: הפחתת TTFB מ-1 שנייה ל-200 אלפיות השנייה שיפרה את LCP ב-40%. בנוסף, שרת איטי מעכב עיבוד של בקשות משתמש, ומחמיר את INP (Interaction to Next Paint).
איך לאבחן TTFB באמצעות Chrome DevTools ו-WebPageTest?
- Chrome DevTools (כרטיסיית Network): מדוד Waiting (TTFB) עבור כל בקשה. סנן לפי סוג מסמך.
- WebPageTest: מספק waterfall מפורט המציג DNS, TCP, TLS, בייט ראשון. מציין כמה זמן לקח לשרת.
- Lighthouse: הערכה כוללת עם המלצות.
- פתרונות RUM (Yandex.Metrica, Google Analytics): מציגים ערכי TTFB אמיתיים עבור משתמשים.
אנחנו תמיד מתחילים עם נתוני RUM כדי לקבל תמונה אובייקטיבית.
מטמון — הכלי המרכזי
מטמון עמוד מלא ברמת האפליקציה
הדרך היעילה ביותר היא לשמור HTML מוכן במטמון עבור כל המשתמשים הלא מחוברים. הנה middleware עבור Laravel:
class FullPageCache {
private const TTL = 300; // 5 минут
public function handle(Request $request, Closure $next): Response
{
if (!$this->isCacheable($request)) {
return $next($request);
}
$key = $this->cacheKey($request);
if (Cache::has($key)) {
return response(Cache::get($key))
->header('X-Cache', 'HIT')
->header('Content-Type', 'text/html; charset=UTF-8');
}
$response = $next($request);
if ($response->getStatusCode() === 200) {
Cache::put($key, $response->getContent(), self::TTL);
}
return $response->header('X-Cache', 'MISS');
}
private function isCacheable(Request $request): bool
{
return $request->isMethod('GET') && !auth()->check() && !$request->hasCookie(session()->getName());
}
private function cacheKey(Request $request): string
{
return 'fpc:' . sha1($request->fullUrl());
}
}גישה זו נותנת TTFB < 50 אלפיות השנייה עבור עמודים במטמון. המפתח הוא להגדיר נכון invalidation כאשר התוכן משתנה. אנחנו משתמשים באירועים (Model events) כדי לנקות את המטמון של כתובות ה-URL המתאימות.
Nginx FastCGI Cache — מהיר יותר מ-PHP+Redis
מטמון ברמת nginx עובד לפני יצירת PHP, מה שנותן רווח נוסף של 10-20 אלפיות השנייה:
fastcgi_cache_path /var/cache/nginx/fcgi levels=1:2 keys_zone=LARAVEL:10m inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
server {
location ~ \.php$ {
fastcgi_cache LARAVEL;
fastcgi_cache_valid 200 5m;
fastcgi_cache_valid 404 1m;
fastcgi_cache_bypass $cookie_laravel_session $http_authorization;
fastcgi_no_cache $cookie_laravel_session $http_authorization;
add_header X-Fastcgi-Cache $upstream_cache_status;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
}עבור invalidation אנחנו משתמשים במודול ngx_cache_purge — שולחים בקשת PURGE לכתובת ה-URL.
אופטימיזציה של שאילתות מסד נתונים
שאילתות איטיות הן הסיבה השנייה בשכיחותה ל-TTFB גבוה. טעות אופיינית היא בעיית N+1:
// Медленно
$products = Product::all();
foreach ($products as $product) {
echo $product->category->name;
}
// Быстро
$products = Product::with(['category', 'images', 'brand'])->get();
אנחנו מפרופילים את כל השאילתות דרך class FullPageCache { private const TTL = 300; // 5 минут public function handle(Request $request, Closure $next): Response { if (!$this->isCacheable($request)) { return $next($request); } $key = $this->cacheKey($request); if (Cache::has($key)) { return response(Cache::get($key)) ->header('X-Cache', 'HIT') ->header('Content-Type', 'text/html; charset=UTF-8'); } $response = $next($request); if ($response->getStatusCode() === 200) { Cache::put($key, $response->getContent(), self::TTL); } return $response->header('X-Cache', 'MISS'); } private function isCacheable(Request $request): bool { return $request->isMethod('GET') && !auth()->check() && !$request->hasCookie(session()->getName()); } private function cacheKey(Request $request): string { return 'fpc:' . sha1($request->fullUrl()); } } ומתעדים את אלה שלוקחות יותר מ-100 אלפיות השנייה. אנחנו מוסיפים אינדקסים, מחליפים לולאות מקוננות בשאילתות בודדות עם JOINs.
Redis לאחסון נתונים במטמון
שמור תוצאות של שאילתות תכופות — עץ קטגוריות, מוצרים מובילים, מונים:
$categories = Cache::remember('categories:tree', 3600, function () {
return Category::with('children')->whereNull('parent_id')->orderBy('sort_order')->get();
});
$stats = Cache::remember('product:stats:' . $productId, 300, function () use ($productId) {
return [
'views' => ProductView::where('product_id', $productId)->count(),
'sales' => OrderItem::where('product_id', $productId)->sum('quantity'),
'wishlist' => WishlistItem::where('product_id', $productId)->count(),
];
}); אופטימיזציות נוספות
DNS ורשת
השתמש ב-fastcgi_cache_path /var/cache/nginx/fcgi levels=1:2 keys_zone=LARAVEL:10m inactive=60m max_size=1g; fastcgi_cache_key "$scheme$request_method$host$request_uri"; server { location ~ \.php$ { fastcgi_cache LARAVEL; fastcgi_cache_valid 200 5m; fastcgi_cache_valid 404 1m; fastcgi_cache_bypass $cookie_laravel_session $http_authorization; fastcgi_no_cache $cookie_laravel_session $http_authorization; add_header X-Fastcgi-Cache $upstream_cache_status; fastcgi_pass unix:/run/php/php8.2-fpm.sock; } } עבור משאבי צד שלישי כדי להפחית זמן DNS+TCP. הפעל CDN (Cloudflare, Vercel) — הם מפחיתים זמן השהיה באמצעות שרתים מבוזרים גיאוגרפית.
OPcache עבור PHP
הגדרות ייצור:
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.jit=tracing
opcache.jit_buffer_size=64Mקומפילציית JIT מאיצה את ביצוע סקריפטים של PHP ב-20-50%, ומפחיתה ישירות את TTFB.
השוואה בין שיטות מטמון
| שיטה | TTFB (אופייני) | מורכבות יישום | Invalidation |
|---|---|---|---|
| מטמון עמוד מלא (PHP) | < 50 אלפיות השנייה | בינוני | לפי אירועים |
| Nginx FastCGI Cache | < 30 אלפיות השנייה | בינוני | בקשת PURGE |
| מטמון נתונים Redis | 1-5 אלפיות השנייה (לשאילתה) | נמוך | TTL או מפתחות |
| CDN + סטטי | 10-50 אלפיות השנייה (במקור) | נמוך | לפי URL |
מה כלול בעבודה שלנו
- בדיקה — מדידת TTFB בכל סוגי העמודים, ניתוח מבנה המטמון, פרופיל מסד נתונים.
- עיצוב — בחירת אסטרטגיית מטמון עבור המחסנית הספציפית.
- יישום — פריסת מטמון עמוד מלא, מטמון nginx, אופטימיזציה של שאילתות, הגדרת Redis.
- בדיקות — בדיקות A/B לפני/אחרי, אימות invalidation נכון.
- תיעוד — תוכנית מטמון עבור צוות הפיתוח.
- בדיקה לאחר שישה חודשים — ניטור ירידה בביצועים.
לוח זמנים משוער
- אבחון וניצחונות מהירים (OPcache, אינדקסים): 1-2 ימים.
- מטמון עמוד מלא + מטמון Nginx: 2-3 ימים.
- אופטימיזציה עמוקה של DB ו-Redis: 3-5 ימים.
- CDN וכיוונון עדין: 1-2 ימים.
לוח זמנים סופי — בין 2 ימי עסקים ל-2 שבועות בהתאם למורכבות.
ערכי TTFB יעד לפי סוג עמוד
| סוג עמוד | עם מטמון | ללא מטמון |
|---|---|---|
| עמוד הבית | < 50 אלפיות השנייה | < 300 אלפיות השנייה |
| קטלוג | < 50 אלפיות השנייה | < 400 אלפיות השנייה |
| עמוד מוצר | < 50 אלפיות השנייה | < 200 אלפיות השנייה |
| נקודות קצה API | — | < 100 אלפיות השנייה |
אנחנו מבטיחים הפחתת TTFB ל-500 אלפיות השנייה בכל העמודים ושיפור LCP ב-30-50%. לקוחות חוסכים עד 40% על אירוח בזכות מטמון, וההשקעה באופטימיזציה מחזירה את עצמה תוך 3-6 חודשים. צור קשר לבדיקה חינמית של האתר שלך. קבל ייעוץ על אופטימיזציית TTFB — ננתח את הפרויקט שלך ונציע צעדים ספציפיים.







