1. ספרייה
  2. HTTP והרשת
  3. קודי סטטוס HTTP

עודכן לפני חודש

כשהדפדפן שלך מבקש מהשרת משאב, לרוב כבר יש לו אותו משאב שמור במטמון. השאלה אינה "תן לי את הקובץ הזה" — אלא "האם הקובץ הזה השתנה מאז שקיבלתי אותו לאחרונה?"

קוד המצב 304 Not Modified הוא תשובת השרת: לא, הוא לא השתנה. השתמש במה שיש לך.

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

תבנית הבקשה המותנית

ה-304 הגיוני רק כחלק משיחה. כך זה עובד:

ביקור ראשון: קבלת המשאב

GET /style.css HTTP/1.1
Host: www.example.com

HTTP/1.1 200 OK
Content-Type: text/css
Last-Modified: Wed, 21 Oct 2024 07:00:00 GMT
ETag: "686897696a7c876b7e"
Cache-Control: max-age=0, must-revalidate

body { color: blue; }

הדפדפן שומר את ה-CSS במטמון ושומר שני נתונים: מתי הוא שונה לאחרונה, ואת ה-ETag שלו (טביעת אצבע של התוכן).

ביקור חוזר: שאלה אם השתנה

כאשר המטמון דורש אימות, הדפדפן שולח בקשה מותנית:

GET /style.css HTTP/1.1
Host: www.example.com
If-Modified-Since: Wed, 21 Oct 2024 07:00:00 GMT
If-None-Match: "686897696a7c876b7e"

הדפדפן אומר: "יש לי גרסה מתאריך זה עם טביעת אצבע זו. האם היא עדיין תקינה?"

השרת מחליט

אם המשאב לא השתנה:

HTTP/1.1 304 Not Modified
ETag: "686897696a7c876b7e"
Cache-Control: max-age=3600

אין גוף. אין תוכן. רק אישור. הדפדפן משתמש בעותק השמור במטמון שלו.

אם המשאב השתנה:

HTTP/1.1 200 OK
Content-Type: text/css
Last-Modified: Wed, 21 Oct 2024 08:30:00 GMT
ETag: "7789a8b7c93d987e8"
Cache-Control: max-age=3600

body { color: red; }

תוכן חדש, טביעת אצבע חדשה, המטמון מתעדכן.

שתי דרכים לאימות: זמן לעומת טביעת אצבע

HTTP מספק שני מנגנוני אימות. לכל אחד יש פשרות.

Last-Modified (חותמת זמן)

הלקוח שולח:

If-Modified-Since: Wed, 21 Oct 2024 07:00:00 GMT

השרת בודק: האם הקובץ שונה אחרי התאריך הזה?

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

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

ETag (טביעת אצבע)

הלקוח שולח:

If-None-Match: "686897696a7c876b7e"

השרת בודק: האם טביעת האצבע הזו תואמת לתוכן הנוכחי?

יתרון: מדויק. מזהה כל שינוי תוכן מיידית. אינו תלוי בשעוני מערכת.

דרישה: חישוב או מעקב אחר טביעת האצבע.

השתמש בשניהם

לקוחות יכולים לשלוח את שתי הכותרות. אם אחת מהן מציינת שהמשאב לא השתנה, השרת יכול להחזיר 304. כשגם ETag וגם Last-Modified נוכחים, ETag קודם עקב דיוקו הרב יותר.

שיטה מומלצת: יישם את שניהם. ETag לדיוק, Last-Modified לתאימות עם לקוחות ישנים יותר.

יצירת ETags

ניתן לחשב ETags בכמה דרכים:

גיבוב תוכן (המדויק ביותר):

const crypto = require('crypto');
const hash = crypto.createHash('md5').update(content).digest('hex');
const etag = `"${hash}"`;

מטאדאטה של קובץ (מהיר יותר, פחות מדויק):

const stats = fs.statSync(filePath);
const etag = `"${stats.size}-${stats.mtime.getTime()}"`;

מספר גרסה (עבור משאבים בעלי גרסאות):

const etag = `"${resourceId}-${version}"`;

ETags חזקים לעומת חלשים

ETag חזק פירושו זהה בייט-לבייט:

ETag: "686897696a7c876b7e"

ETag חלש פירושו שקול מבחינה סמנטית (עם הקידומת W/):

ETag: W/"686897696a7c876b7e"

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

תגובת ה-304: כותרות בלבד

תגובת 304 אסורה להכיל גוף. היא מכילה רק כותרות:

HTTP/1.1 304 Not Modified
Date: Wed, 21 Oct 2024 07:28:00 GMT
ETag: "686897696a7c876b7e"
Cache-Control: max-age=3600

כלול: Date, ETag, Cache-Control, Expires (אם בשימוש)

אל תכלול: Content-Type, Content-Length (או הגדר ל-0), כל תוכן של גוף

הכללת ETag בתגובת ה-304 חשובה — היא מודיעה ללקוח מה לשלוח בכותרות If-None-Match עתידיות.

יישום בצד השרת

app.get('/style.css', function(request, response) {
    const resource = loadResource('style.css');
    const currentETag = calculateETag(resource);
    const currentModified = resource.lastModified;

    const clientETag = request.headers['if-none-match'];
    const clientModifiedSince = request.headers['if-modified-since'];

    // בדוק אם המשאב לא השתנה
    if (clientETag === currentETag ||
        (clientModifiedSince && new Date(clientModifiedSince) >= currentModified)) {
        response.status(304);
        response.setHeader('ETag', currentETag);
        response.setHeader('Cache-Control', 'max-age=3600');
        response.end(); // ללא גוף
    } else {
        response.status(200);
        response.setHeader('Content-Type', 'text/css');
        response.setHeader('ETag', currentETag);
        response.setHeader('Last-Modified', currentModified.toUTCString());
        response.setHeader('Cache-Control', 'max-age=3600');
        response.send(resource.content);
    }
});

איזון עומסים: בעיית עקביות ETag

בסביבות עם איזון עומסים, כל שרת חייב לייצר ETags זהים עבור אותו תוכן. אם שרת A מחשב "abc123" ושרת B מחשב "xyz789" עבור אותו קובץ, האימות נכשל באופן אקראי — הלקוח שולח ETag שאינו תואם ומקבל הורדה מלאה במקום 304.

פתרונות:

  • השתמש בגיבובים מבוססי תוכן (אותו תוכן = אותו גיבוב, ללא תלות בשרת)
  • שתף את חישוב ETag בין השרתים
  • השתמש ב-CDN שמטפל ב-ETags באופן עקבי

צפייה ב-304 בפעולה

  1. פתח את DevTools (F12)
  2. עבור ללשונית Network
  3. טען דף
  4. טען אותו מחדש
  5. חפש קודי מצב 304
  6. שים לב לעמודת Size שמציגה העברות קטנות או "from cache"

הדפדפן מטפל בכל זה באופן אוטומטי. כשאתה רואה 304, אתה צופה ב-HTTP עושה את עבודתו — מאשר שלא השתנה דבר, חוסך הורדה, מאיץ את הגלישה.

שאלות נפוצות על 304 Not Modified

מה ההבדל בין 304 Not Modified לבין קבלת משאב מהמטמון?

קבלת משאב "מהמטמון" פירושה שהדפדפן לא פנה לשרת כלל — העותק השמור היה עדיין טרי לפי Cache-Control. 304 פירושו שהדפדפן כן פנה לשרת כדי לאמת את העותק השמור, והשרת אישר שהוא עדיין עדכני. 304 דורש הלוך-חזור לשרת; פגיעה ישירה במטמון אינה דורשת זאת.

האם תגובת 304 יכולה להכיל גוף?

לא. מפרט HTTP אוסר זאת במפורש. תגובת 304 חייבת להכיל כותרות בלבד. הכללת גוף מבזבזת רוחב פס ומפרת את הפרוטוקול — כל הנקודה של 304 היא להימנע מהעברת המשאב.

מדוע שרת יחזיר 200 במקום 304 כאשר המטמון שלי נראה תקין?

כמה אפשרויות: יצירת ETag עשויה להיות לא עקבית בין שרתים בהגדרה עם איזון עומסים, ייתכן שהשרת כלל לא מיישם בקשות מותנות, ייתכן שחותמת הזמן של If-Modified-Since סובלת מבעיית דיוק של שנייה אחת, או שהשעון של השרת אינו מסונכרן. בדוק שכותרות הבקשה המותנית שלך נשלחות כראוי ושהשרת תומך באימות ETag/Last-Modified.

האם כדאי להשתמש ב-ETag או ב-Last-Modified?

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

האם דף זה היה מועיל?

😔
🤨
😃