Caching & HTTP Validators (ETag / Last-Modified / 304)
למה זה חשוב?
להקטין רוחב‑פס וזמני תגובה: אם התוכן לא השתנה, נחזיר 304 Not Modified במקום גוף מלא.
כך דפדפנים ולקוחות יכולים להשתמש במטמון מקומי בצורה בטוחה ויעילה.
עקרונות בסיס
ETag: מזהה גרסה של המשאב. יכול להיות חזק (
"abc123") או חלש (W/"abc123").Last-Modified: זמן העדכון האחרון בפורמט HTTP (RFC 1123).
If-None-Match / If-Modified-Since: כותרות מהלקוח שמאפשרות לוודא אם יש שינוי.
אם אין שינוי – מחזירים
304וכוללים שוב את הכותרותETagו-Last-Modified.
מתכון: Flask + werkzeug
הדוגמה הבאה מציגה זרימת מימוש בטוחה, כולל סדר בדיקות נכון וקיזוז מיקרו‑שניות להשוואה יציבה.
from werkzeug.http import http_date, parse_date
from flask import request, Response
def build_validators(hash_value: str, updated_at) -> tuple[str, str]:
"""יוצר ערכי ETag (חלש) ו-Last-Modified מפורמטים לתשובה.
updated_at חייב להיות timezone-aware (UTC) כדי למנוע השוואות שגויות.
"""
etag = f'W/"{hash_value}"'
last_modified = http_date(updated_at)
return etag, last_modified
def maybe_not_modified(hash_value: str, updated_at, body: bytes, mimetype: str = "text/html; charset=utf-8"):
etag, last_modified = build_validators(hash_value, updated_at)
# בדיקת ETag (עדיפות ראשונה)
if request.headers.get('If-None-Match') == etag:
resp = Response(status=304)
resp.headers['ETag'] = etag
resp.headers['Last-Modified'] = last_modified
return resp
# בדיקת Last-Modified (עדיפות שנייה)
ims = request.headers.get('If-Modified-Since')
parsed_ims = parse_date(ims) if ims else None
if parsed_ims and updated_at.replace(microsecond=0) <= parsed_ims:
resp = Response(status=304)
resp.headers['ETag'] = etag
resp.headers['Last-Modified'] = last_modified
return resp
# 200 OK – יש לצרף את הכותרות כדי לאפשר אימות בבקשות הבאות
resp = Response(body, mimetype=mimetype)
resp.headers['ETag'] = etag
resp.headers['Last-Modified'] = last_modified
return resp
בדיקות ידניות (Copy‑Paste)
# בקשה רגילה – קבל ETag ו-Last-Modified
curl -i https://<host>/file/<id>
# שליחת If-None-Match עם ה-ETag שהתקבל — מצופה 304
curl -i -H 'If-None-Match: W/"abc123..."' https://<host>/file/<id>
# שליחת If-Modified-Since עם זמן קודם/שווה — מצופה 304
curl -i -H 'If-Modified-Since: Tue, 15 Nov 1994 12:45:26 GMT' https://<host>/file/<id>
Checklist למימוש נכון
להחליט על אסטרטגיית ETag: חזק (hash של התוכן) או חלש (גרסה/עדכון).
לשמור
updated_atכ-UTC aware; להשוות בלי מיקרו‑שניות.תמיד לצרף
ETagו-Last-Modifiedגם בתשובת200וגם ב-304.להקדים בדיקת
If-None-MatchלפניIf-Modified-Since.ליישר קו עם שכבת קאש פנימית (MRU/LRU) כדי למנוע מירוצים.
Gotchas נפוצים
ETag לא יציב: חישוב hash שכולל שדות משתנים (כמו timestamps) יגרום לפספוסי קאש.
Timezone naive/aware: השוואה בין ערכים ללא אזור זמן לערכים עם אזור זמן תיכשל.
מיקרו‑שניות: לקוחות מסוימים חותכים לרזולוציית שנייה; בצעו
replace(microsecond=0)להשוואה.שינוי ללא עדכון ``updated_at``: ודאו שהשדה מייצג את מצב התוכן בפועל.
שילוב עם Cache-Control
ניתן להוסיף Cache-Control בצד השרת בהתאם למדיניות (למשל: משאבים סטטיים עם פינגרפרינט בשם הקובץ):
Cache-Control: public, max-age=31536000, immutable