1. ספרייה
  2. HTTP והרשת
  3. כותרות HTTP

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

כשאתם מבקשים משהו משרת, התגובה מגיעה בשני חלקים. הגוף הוא מה שביקשתם — ה-HTML, התמונה, נתוני ה-JSON. הכותרות הן השרת מספר לכם מה הוא נתן לכם ומה לעשות איתו.

חשבו על זה כמו קבלת חבילה. תוכן החבילה הוא הגוף. הכותרות הן הוראות הטיפול המודבקות בחוץ: "שביר." "לאחסן במקרר לאחר הפתיחה." "זו מתנה — אל תציגו את המחיר."

השאלות שכל תגובה עונה עליהן

כל תגובת HTTP עונה במשתמע על סדרת שאלות:

מה זה? כותרת ה-Content-Type מספרת לדפדפן אם הוא מסתכל על HTML, JSON, תמונה או PDF. בלעדיה, הדפדפן מקבל זרם של בתים ואין לו מושג איך לפרש אותם.

כמה גדול זה? Content-Length מאפשר לדפדפנים להציג התקדמות הורדה ולדעת מתי קיבלו את הכל.

האם זה דחוס? Content-Encoding אומר לדפדפנים לפתוח את הדחיסה של התגובה לפני השימוש בה.

אפשר לשמור אותו במטמון? Cache-Control וכותרות קשורות קובעות האם דפדפנים ו-CDN יכולים לאחסן עותקים, ולכמה זמן.

אילו כללי אבטחה חלים? כותרות כמו Content-Security-Policy ו-Strict-Transport-Security מספרות לדפדפנים מה לחסום ומה לאכוף.

האם לאחסן עוגיות? כותרות Set-Cookie יוצרות מצב מתמיד בין בקשות.

האם ללכת למקום אחר? כותרות Location (עם קודי סטטוס הפניה) אומרות לדפדפנים לבקש כתובת URL אחרת.

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

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

Content-Type

כותרת התגובה הבסיסית ביותר. היא אומרת לדפדפן איך לפרש את הבתים שקיבל:

Content-Type: text/html; charset=utf-8

בלי Content-Type, הדפדפן מקבל זרם של בתים ואין לו מושג מה הוא מסתכל עליו. האם זה HTML לעיבוד? JSON לניתוח? תמונה להצגה? הבתים עצמם לא אומרים.

ערכים נפוצים:

  • text/html — לעיבוד כדף אינטרנט
  • application/json — לניתוח כנתונים מובנים
  • image/png — להצגה כתמונה
  • application/pdf — להצגה או הורדה כ-PDF
  • text/css — להחלה כגיליון סגנון
  • application/javascript — להרצה כקוד

החלק charset=utf-8 מציין קידוד תווים. שגיאה כאן תגרום לטקסט להיראות כתווים מקולקלים.

Content-Length

גודל גוף התגובה בבתים:

Content-Length: 348

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

Content-Encoding

מציינת דחיסה שהוחלה על התגובה:

Content-Encoding: gzip

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

  • gzip — נתמך באופן נרחב, דחיסה טובה
  • br — Brotli, יחסי דחיסה טובים יותר לטקסט

דחיסה יכולה להפחית את גודל ההעברה ב-70-90% לתוכן טקסטואלי כמו HTML, CSS ו-JavaScript. הבתים המועברים ברשת הם דחוסים; הדפדפן מנפח אותם חזרה לצורתם המקורית.

Content-Disposition

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

Content-Disposition: attachment; filename="report.pdf"

עם inline (ברירת המחדל לרוב התוכן), דפדפנים מציגים את התגובה ישירות. עם attachment, הם מבקשים מהמשתמש לשמור קובץ. פרמטר ה-filename מציע שם לקובץ.

כך שרת יכול לשלוח בתים זהים אבל לגרום לכתובת URL אחת להציג PDF בדפדפן בעוד אחרת מפעילה חלון הורדה.

כותרות מטמון: אפשר לשמור עותק?

Cache-Control

הכותרת הראשית השולטת כיצד ניתן לשמור תגובות במטמון:

Cache-Control: public, max-age=3600, must-revalidate

ההנחיות החשובות:

public — כל מטמון יכול לאחסן זאת: CDN, שרתי proxy, דפדפנים. מתאים לנכסים סטטיים כמו תמונות וסקריפטים.

private — רק הדפדפן של המשתמש יכול לשמור זאת במטמון, לא מטמונים משותפים. מתאים לתוכן מותאם אישית.

max-age=3600 — התגובה טרייה למשך 3600 שניות (שעה אחת). בחלון זה, מטמונים יכולים להגיש אותה ללא בדיקה מול השרת.

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

no-store — לא לשמור במטמון כלל. התגובה לעולם לא תיכתב לדיסק. מתאים לנתונים רגישים.

must-revalidate — ברגע שפג תוקף, מטמונים חייבים לאמת מחדש. הם אינם יכולים להגיש תוכן מיושן גם אם השרת אינו נגיש.

ETag

טביעת אצבע המזהה באופן ייחודי גרסה ספציפית של המשאב:

ETag: "a1b2c3d4e5f6"

כך ETags הופכים את המטמון ליעיל: הדפדפן שומר את התגובה יחד עם ה-ETag שלה. מאוחר יותר, כשרשומת המטמון מיושנת, הדפדפן מבקש את המשאב שוב אבל כולל את ה-ETag בכותרת If-None-Match. אם התוכן לא השתנה, השרת מגיב עם 304 Not Modified — ללא גוף, רק אישור שהגרסה המאוחסנת עדיין תקפה.

זה הופך הורדה גדולה פוטנציאלית לבדיקת אימות קטנה.

Last-Modified

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

Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT

דומה ל-ETag אבל מבוסס על חותמות זמן ולא על טביעות אצבע של תוכן. הדפדפן שולח זאת חזרה בכותרות If-Modified-Since לאימות. פחות מדויק מ-ETags (רזולוציה של שנייה אחת בלבד) אבל פשוט יותר ליישום.

כותרות עוגיות: לזכור

מורה לדפדפן לאחסן עוגייה:

Set-Cookie: sessionId=abc123; Secure; HttpOnly; SameSite=Strict; Max-Age=3600

ערך העוגייה (sessionId=abc123) הוא הנתון עצמו. התכונות שולטות בהתנהגות:

Secure — שלח עוגייה זו רק דרך HTTPS. מונע יירוט בחיבורים לא מאובטחים.

HttpOnly — JavaScript אינו יכול לגשת לעוגייה זו. מגן על אסימוני סשן מהתקפות XSS שמנסות לגנוב עוגיות דרך document.cookie.

SameSite — שולט מתי עוגיות נשלחות עם בקשות בין-אתריות:

  • Strict — לעולם לא לשלוח בין-אתרית (ההגנה החזקה ביותר)
  • Lax — לשלוח בניווטים ברמה העליונה אבל לא בבקשות מוטבעות
  • None — תמיד לשלוח (דורש Secure; משמש לתרחישים בין-אתריים לגיטימיים)

Max-Age — כמה שניות עד שתפוג העוגייה. ללא ערך זה או Expires, זוהי "עוגיית סשן" שנעלמת כשהדפדפן נסגר.

תגובה אחת יכולה לכלול מספר כותרות Set-Cookie כדי להגדיר מספר עוגיות.

כותרות אבטחה: אכוף כללים אלה

Strict-Transport-Security (HSTS)

אומר לדפדפנים להשתמש תמיד ב-HTTPS:

Strict-Transport-Security: max-age=31536000; includeSubDomains

ברגע שדפדפן מקבל כותרת זו, הוא ממיר אוטומטית כל בקשת HTTP ל-HTTPS למשך הזמן שצוין. גם אם משתמש מקליד http:// או לוחץ על קישור HTTP, הדפדפן משדרג ל-HTTPS לפני שליחת דבר.

זה מונע מתוקפים ליירט את בקשת ה-HTTP הראשונית לפני שהפניה ל-HTTPS יכלה להתרחש.

Content-Security-Policy (CSP)

מגדיר אילו משאבים מורשה לדף לטעון:

Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com

CSP יוצר רשימה לבנה. כל משאב שלא הותר במפורש נחסם. זוהי הגנה חזקה מפני התקפות XSS — גם אם תוקף מזריק תג סקריפט זדוני, הוא לא יורץ אם מקורו אינו ברשימה הלבנה.

ההנחיות מציינות כללי מקור עבור סוגי משאבים שונים: script-src ל-JavaScript, img-src לתמונות, style-src ל-CSS, וכן הלאה. default-src קובע את ברירת המחדל לכל דבר שלא צוין במפורש.

X-Content-Type-Options

X-Content-Type-Options: nosniff

מונע מדפדפנים "להריח" את סוג התוכן. בלעדיו, דפדפנים עשויים להתעלם מכותרת Content-Type ולנחש לפי התוכן עצמו. אם תוכן שהועלה על ידי משתמש מתפרש בטעות כ-JavaScript הניתן להרצה, זו פרצת אבטחה. כותרת זו מאלצת דפדפנים לסמוך על Content-Type.

X-Frame-Options

שולטת האם הדף יכול להיות מוטבע במסגרת:

X-Frame-Options: DENY
  • DENY — לא ניתן להטביע על ידי אף אחד
  • SAMEORIGIN — ניתן להטביע רק על ידי דפים מאותו מקור

זה מונע התקפות clickjacking שבהן אתר זדוני מטביע את הדף שלכם ב-iframe בלתי נראה ומרמה משתמשים ללחוץ עליו.

כותרות CORS: גישה בין מקורות

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

Access-Control-Allow-Origin

הכותרת הבסיסית של CORS:

Access-Control-Allow-Origin: https://www.example.com

זה אומר: "JavaScript מ-https://www.example.com מורשה לקרוא תגובה זו." ללא כותרת זו, הדפדפן מביא את התגובה אבל מסרב לאפשר ל-JavaScript לגשת אליה.

התו הכללי * מאפשר לכל מקור, אבל לא ניתן להשתמש בו עם אישורים (עוגיות).

Access-Control-Allow-Methods

אילו שיטות HTTP מותרות לבקשות בין-מקוריות:

Access-Control-Allow-Methods: GET, POST, PUT, DELETE

זה מופיע בתגובות לבקשות preflight, ומציין לדפדפנים אילו שיטות הבקשה בפועל יכולה להשתמש.

Access-Control-Allow-Headers

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

Access-Control-Allow-Headers: Content-Type, Authorization

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

Access-Control-Max-Age

כמה זמן דפדפנים יכולים לאחסן תוצאות preflight במטמון:

Access-Control-Max-Age: 86400

בקשות preflight מוסיפות השהייה. אחסון התוצאות במטמון ל-86400 שניות (יום אחד) אומר שדפדפנים לא צריכים לשלוח preflight לכל בקשה בין-מקורית.

כותרות הפניה

Location

משמשת עם קודי סטטוס 3xx כדי לציין לאן לנווט:

Location: https://www.example.com/new-page

כשלתגובה יש קוד סטטוס הפניה (301, 302, 307, 308), הדפדפן מבקש אוטומטית את ה-URL שבכותרת Location. קוד הסטטוס של ההפניה קובע האם הדפדפן יכול לאחסן את ההפניה במטמון ואם לשנות את שיטת ה-HTTP.

כותרות העברה

Transfer-Encoding

כיצד גוף ההודעה מועבר:

Transfer-Encoding: chunked

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

עם קידוד chunked, אין כותרת Content-Length — הגושים עצמם מגדירים את הגבולות.

מידע על השרת

Date

מתי נוצרה התגובה:

Date: Wed, 21 Oct 2024 07:28:00 GMT

שימושי לניפוי שגיאות ולחישוב רעננות המטמון.

Age

כמה זמן התגובה יושבת במטמון:

Age: 3600

CDN ושרתי proxy מוסיפים כותרת זו. אם Cache-Control אומר max-age=7200 ו-Age אומר 3600, לתגובה נשאר עוד שעה של רעננות.

Server

מזהה את תוכנת השרת:

Server: nginx/1.18.0

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

שאלות נפוצות על כותרות תגובה

מדוע אני רואה תווים מקולקלים במקום תוכן קריא?

בדרך כלל מדובר באי-התאמה ב-Content-Type. השרת שלח charset שגוי (לא UTF-8 כשהיה צריך) או שלא שלח charset בכלל. דפדפנים מנחשים, והניחוש לעיתים קרובות נכשל. התיקון הוא בצד השרת: יש לוודא ש-Content-Type כולל את ה-charset הנכון.

מה ההבדל בין no-cache ל-no-store?

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

מדוע הבקשה הבין-מקורית שלי עובדת ב-Postman אבל נכשלת בדפדפן?

Postman לא אוכף CORS — הוא אינו דפדפן. דפדפנים חוסמים תגובות בין-מקוריות שחסרה בהן כותרת Access-Control-Allow-Origin מתאימה. הבקשה מצליחה ברמת הרשת; הדפדפן פשוט מסרב לאפשר ל-JavaScript לראות את התגובה.

האם אפשר להגדיר מספר עוגיות בתגובה אחת?

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

מה קורה אם Cache-Control ו-Expires לא מסכימים?

Cache-Control מנצח. זוהי הכותרת החדשה והמדויקת יותר. Expires קיים לצורך תאימות לאחור עם מטמוני HTTP/1.0 אבל בפועל מתעלמים ממנה כש-Cache-Control קיים.

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

😔
🤨
😃