הסבר על HTTP Caching Headers: Cache-Control, ETags ואסטרטגיה מעשית

Cache-Control, ETags ו-stale-while-revalidate מוסברים — עם אסטרטגיית caching מעשית לפי סוג asset שמאיצה את האתר שלך באופן מדיד עבור מבקרים חוזרים.

הבקשה הכי מהירה היא זו שלא יוצאת מהדפדפן בכלל

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

HTTP caching מאפשר לדפדפנים ולגורמים ביניים לשמור תגובות ולעשות בהן שימוש חוזר. כשעושים את זה נכון, זה יכול לחתוך את זמני טעינת הדף בחצי עבור מבקרים חוזרים, להפחית דרמטית את העומס על השרת, ולשפר ציוני Core Web Vitals — במיוחד LCP ו-FID.

אבל caching headers קל לעשות בהם טעויות. הפוסט הזה מסביר איך הם עובדים בפועל, אילו directives חשובים, ואיך לבנות אסטרטגיית caching שאפשר ליישם כבר עכשיו.

איך HTTP Caching עובד

כשדפדפן מקבל תגובה, הוא בודק אם התגובה כוללת הנחיות לגבי כמה זמן לשמור אותה. ההנחיות האלה מגיעות ב-HTTP response headers.

יש שני מנגנונים עיקריים:

  • Cache-Control — אומר לדפדפן (ולכל cache ביניים) כמה זמן לשמור תגובה ובאילו תנאים.
  • Conditional requests (ETags ו-Last-Modified) — מאפשרים לדפדפן לשאול את השרת "האם זה השתנה מאז שמשכתי את זה לאחרונה?" ולקבל תגובת 304 Not Modified קלת משקל במקום להוריד את הקובץ כולו מחדש.

שני המנגנונים האלה עובדים יחד. הבנת שניהם היא המפתח לאסטרטגיית caching מוצלחת.

Cache-Control: ה-Directives שחשוב להכיר

ה-header של Cache-Control הוא הכלי החשוב ביותר שיש לך. הנה מה שכל directive עושה בפועל.

max-age

max-age=N אומר לדפדפן להתייחס לתגובה כ"טרייה" למשך N שניות. בתוך אותו חלון זמן, הדפדפן מגיש את הקובץ ישירות מה-cache המקומי שלו — בלי שום בקשת רשת בכלל.

Cache-Control: max-age=31536000

זה שנה אחת. השתמש בזה עבור assets שיש להם hash בשם הקובץ (למשל main.a3f92b.js), כי כשהקובץ משתנה, ה-URL משתנה גם הוא — ומעקף את ה-cache באופן אוטומטי.

no-cache לעומת no-store

אלה נשמעים דומים אבל מתנהגים בצורה שונה מאוד.

  • no-cache — הדפדפן יכול לשמור את התגובה, אבל חייב לאמת אותה מול השרת לפני שמשתמש בה. זה בדרך כלל מה שרוצים עבור דפי HTML.
  • no-store — הדפדפן לא יכול לשמור את התגובה בכלל. שמור את זה לנתונים רגישים באמת, כמו API payloads פרטיים או תגובות בנקאיות.

public לעומת private

  • public — התגובה יכולה להישמר בכל cache, כולל CDN caches משותפים ו-reverse proxies.
  • private — רק הדפדפן של המשתמש הסופי יכול לשמור אותה. השתמש בזה עבור תגובות שמכילות נתונים ספציפיים למשתמש.

stale-while-revalidate

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

Cache-Control: max-age=3600, stale-while-revalidate=86400

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

ETags ו-Conditional Requests

כשקובץ cache פג תוקף, הדפדפן לא חייב להוריד אותו מחדש בעיוורון. הוא יכול לשלוח conditional request שבודק אם הקובץ השתנה.

ETag הוא מזהה ייחודי שהשרת מקצה לגרסה ספציפית של קובץ — בדרך כלל hash של תוכן הקובץ. הדפדפן שומר אותו ושולח אותו חזרה בבקשה הבאה:

If-None-Match: "a3f92b84c1d"

אם הקובץ לא השתנה, השרת מגיב עם 304 Not Modified וללא גוף תגובה. התגובה הזאת יכולה להיות 200 בייט במקום 200 קילובייט. עבור דף עם 30 assets, זה מצטבר מהר מאוד.

Last-Modified עובד באותה צורה אבל משתמש ב-timestamp במקום hash. ETags בדרך כלל יותר אמינים כי timestamps יכולים להשתנות בין הפעלות מחדש של השרת או בסביבות מקובצות.

אסטרטגיית Caching מעשית לפי סוג Asset

הטעות שרוב המפתחים עושים היא להחיל מדיניות caching אחת על הכל. ל-assets שונים יש תדירויות שינוי שונות מאוד.

דפי HTML

Cache-Control: no-cache

HTML משתנה לעתים קרובות ותמיד צריך להיות עדכני. השתמש ב-no-cache כדי שדפדפנים יאמתו בכל ביקור — אבל עדיין נהנים מתגובת 304 כשלא השתנה כלום.

Static assets עם גרסאות (JS ו-CSS עם hash בשם הקובץ)

Cache-Control: public, max-age=31536000, immutable

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

תמונות ללא content-addressed URLs

Cache-Control: public, max-age=604800, stale-while-revalidate=86400

שבוע אחד של טריות עם חלון אימות מחדש ברקע. התאם בהתאם לתדירות שבה התמונות שלך משתנות בפועל.

API responses

עבור נתונים ציבוריים שמשתנים לאט:

Cache-Control: public, max-age=60, stale-while-revalidate=300

עבור תגובות ספציפיות למשתמש, תמיד השתמש ב-private:

Cache-Control: private, no-cache

איך להגדיר את ה-Headers האלה בשרת שלך

ב-Apache, השתמש בקובץ .htaccess שלך:

<FilesMatch "\.(js|css|woff2)$"> Header set Cache-Control "public, max-age=31536000, immutable" </FilesMatch>

ב-Nginx, השתמש ב-location blocks:

location ~* \.(js|css|woff2)$ { add_header Cache-Control "public, max-age=31536000, immutable"; expires 1y; }

ה-directive expires של Nginx מגדיר את ה-header הישן Expires לצד Cache-Control. דפדפנים נותנים עדיפות ל-Cache-Control כששניהם קיימים, אבל הכללת שניהם משפרת תאימות עם לקוחות ישנים יותר.

איך למדוד את ההשפעה

לפני ואחרי ביצוע שינויים, אמת עם כלים אמיתיים:

  • Chrome DevTools Network tab — הסתכל על עמודת Size. תגובות מה-cache מציגות "(disk cache)" או "(memory cache)" עם זמן העברה קרוב לאפס.
  • WebPageTest — הרץ בדיקת repeat view. הפער בין זמן טעינה בביקור ראשון לביקור חוזר מראה בדיוק כמה אפקטיבית מדיניות ה-cache שלך.
  • Lighthouse — הביקורת "Serve static assets with an efficient cache policy" מדגישה כל דבר שנשמר ב-cache פחות מ-30 יום שאינו HTML.
  • curl — בדוק headers ישירות מהטרמינל: curl -I https://yoursite.com/main.js

cache מכוונן היטב יכול להפחית את משקל הדף בביקור חוזר ב-80% ויותר. עבור דף של 2MB, זה אומר שמבקרים חוזרים מורידים בערך 400KB — הבדל משמעותי בחיבורי מובייל.

בצד השרת, caching גם מפחית את הבקשות לשרת המקורי. כשיש reverse proxy מול האפליקציה שלך, cache hit אומר שה-PHP, Node, או Python שלך לא רצים בכלל. זה מפחית ישירות זמן CPU, לחץ על הזיכרון ושאילתות למסד הנתונים. אם אתה לא בטוח אם בקשות באמת פוגעות ב-cache שלך או חוצות לשרת המקורי, שווה לקבל נראות לאותו pipeline — לראות מה ב-cache, מה חסום, ומה מגיע לאפליקציה שלך, מאשר שהשכבות שלך עובדות כמצופה ולא רק כך אתה מניח.

טעויות נפוצות שכדאי להימנע מהן

  • Caching אגרסיבי של HTML. אם למבקרים יש את דף הבית שלך ב-cache ל-24 שעות ואתה דוחף תיקון קריטי, הם לא יראו אותו עד שה-cache יפוג.
  • שימוש ב-no-store בכל מקום "ליתר ביטחון." זה מאלץ הורדה מלאה בכל ביקור. אתה עושה עבודה נוספת ללא שום תועלת.
  • לא לגרסן static assets. בלי hash או מחרוזת גרסה בשם הקובץ, אי אפשר להשתמש בבטחה בערכי max-age ארוכים. מבקרים חוזרים ממשיכים לראות JS ו-CSS ישנים.
  • שכחת ה-Vary header. אם השרת שלך מחזיר תגובות שונות על בסיס Accept-Encoding או Accept-Language, הוסף Vary header כדי שה-caches ישמרו גרסאות נפרדות לכל וריאנט — אחרת אתה מסתכן בהגשת הגרסה הלא נכונה.

שיפורים מהירים שאפשר ליישם היום

  • הרץ curl -I https://yoursite.com/style.css ובדוק איזה ערך Cache-Control אתה שולח בפועל.
  • הוסף immutable לכל asset ששם הקובץ שלו כולל content hash.
  • עבור דפי HTML מ-no-store ל-no-cache כדי שדפדפנים עדיין יוכלו להשתמש בתגובות 304.
  • הוסף stale-while-revalidate לכללים של תמונות ופונטים כדי לבטל את קפיצת הזמן שקורית ברגע שקובץ ב-cache פג תוקף.

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