העברה ל-User-Agent Client Hints

אסטרטגיות להעברת האתר מהסתמכות על מחרוזת סוכן המשתמש אל איסוף נתונים מותנה באותות לסוכן המשתמש (UA-CH).

תאריך פרסום: 19 במאי 2021

המחרוזת של סוכן המשתמש היא משטח טביעת אצבע פסיבי משמעותי בדפדפנים, וגם קשה לעבד אותה. עם זאת, יש כל מיני סיבות מוצדקות לאיסוף ולעיבוד של נתוני סוכן המשתמש, ולכן צריך למצוא פתרון טוב יותר. איסוף נתונים מותנה באותות לסוכן המשתמש (UA-CH) מספק דרך מפורשת להצהיר על הצורך בנתוני סוכן המשתמש, ושיטות להחזרת הנתונים בפורמט קל לשימוש.

כאן נסביר איך לבדוק את הגישה לנתוני סוכן המשתמש ואיך להעביר את השימוש במחרוזת סוכן המשתמש לאיסוף נתונים מותנה באותות לסוכן המשתמש (UA-CH).

Browser Support

  • Chrome: 90.
  • Edge: 90.
  • Firefox: not supported.
  • Safari: not supported.

Source

בדיקת האיסוף של נתוני סוכן המשתמש והשימוש בהם

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

אם אתם לא יודעים אם נעשה שימוש בנתוני סוכן המשתמש או איפה, כדאי לחפש בקוד של ממשק המשתמש שימוש ב-navigator.userAgent ובקוד של הקצה העורפי שימוש בכותרת ה-HTTP‏ User-Agent. כדאי גם לבדוק אם בקוד של ממשק המשתמש יש שימוש בתכונות שכבר הוצאו משימוש, כמו navigator.platform ו-navigator.appVersion.

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

  • השם או הגרסה של הדפדפן
  • השם או הגרסה של מערכת ההפעלה
  • יצרן או דגם המכשיר
  • סוג המעבד, הארכיטקטורה או מספר הביטים (לדוגמה, 64 ביט)

יכול להיות גם שאתם משתמשים בספרייה או בשירות של צד שלישי כדי לעבד את סוכן המשתמש. במקרה כזה, כדאי לבדוק אם הם מעדכנים את התמיכה באיסוף נתונים מותנה באותות לסוכן המשתמש (UA-CH).

האם אתם משתמשים רק בנתונים בסיסיים של סוכן המשתמש?

הקבוצה שמוגדרת כברירת מחדל של איסוף נתונים מותנה באותות לסוכן המשתמש (UA-CH) כוללת:

  • ‫Sec-CH-UA: שם הדפדפן והגרסה הראשית או המשמעותית
  • ‫Sec-CH-UA-Mobile: ערך בוליאני שמציין מכשיר נייד
  • ‫Sec-CH-UA-Platform: שם מערכת ההפעלה

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

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

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

שיטות

יש כמה אסטרטגיות שאתם יכולים להשתמש בהן כדי להעביר את השימוש במחרוזת סוכן המשתמש לאיסוף נתונים מותנה באותות לסוכן המשתמש (UA-CH).

‫JavaScript API בצד הלקוח לפי דרישה

אם אתם משתמשים ב-navigator.userAgent, כדאי לעבור לשימוש ב-navigator.userAgentData לפני שחוזרים לניתוח של מחרוזת סוכן המשתמש.

if (navigator.userAgentData) {
  // use new hints
} else {
  // fall back to user-agent string parsing
}

אם אתם בודקים את הנתונים של מכשירים ניידים או מחשבים, השתמשו בערך הבוליאני mobile:

const isMobile = navigator.userAgentData.mobile;

‫userAgentData.brands הוא מערך של אובייקטים עם המאפיינים brand ו-version, שבו הדפדפן יכול לציין את התאימות שלו למותגים האלה. אפשר לגשת אליו ישירות כמערך, או להשתמש בקריאה ל-some() כדי לבדוק אם רשומה ספציפית קיימת:

function isCompatible(item) {
  // In real life you most likely have more complex rules here
  return ['Chromium', 'Google Chrome', 'NewBrowser'].includes(item.brand);
}
if (navigator.userAgentData.brands.some(isCompatible)) {
  // browser reports as compatible
}

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

navigator.userAgentData.getHighEntropyValues(['model'])
  .then(ua => {
    // requested hints available as attributes
    const model = ua.model
  });

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

כותרת סטטית בצד השרת

אם אתם משתמשים ב-request header‏ User-Agent בשרת והצרכים שלכם לגבי הנתונים האלה עקביים יחסית בכל האתר, אתם יכולים לציין את איסוף אותות בצד הלקוח כקבוצה סטטית בתגובות שלכם. זו גישה פשוטה יחסית, כי בדרך כלל צריך להגדיר אותה רק במיקום אחד. לדוגמה, יכול להיות שהיא נמצאת בהגדרות של שרת האינטרנט אם כבר הוספתם שם כותרות, בהגדרות האירוח או בהגדרות ברמה העליונה של המסגרת או הפלטפורמה שבהן אתם משתמשים באתר.

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

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

לדוגמה, ברירות המחדל הנוכחיות של Chrome יוצגו כך:

‫⬇️ כותרות תגובה

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA

אם אתם רוצים לקבל גם את דגם המכשיר בתגובות, אתם צריכים לשלוח:

‫⬇️ כותרות תגובה

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA

כשמעבדים את זה בצד השרת, קודם צריך לבדוק אם הכותרת הרצויה של Sec-CH-UA נשלחה, ואז לחזור לניתוח הכותרת של User-Agent אם היא לא זמינה.

העברת רמזים לבקשות CORS

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

לדוגמה, נניח ש-https://blog.site מארח משאבים ב-https://cdn.site שיכולים להחזיר משאבים שעברו אופטימיזציה למכשיר ספציפי. https://blog.site יכול לבקש את הרמז Sec-CH-UA-Model, אבל צריך להקצות אותו באופן מפורש ל-https://cdn.site באמצעות הכותרת Permissions-Policy. רשימת איסוף אותות בצד הלקוח שנשלטים על ידי המדיניות זמינה בטיוטה של תשתית איסוף אותות בצד הלקוח.

‫⬇️ תשובה מ-blog.site שמעבירה את הרמז

Accept-CH: Sec-CH-UA-Model
Permissions-Policy: ch-ua-model=(self "https://cdn.site")

⬆️ בקשה למשאבי משנה ב-cdn.site כוללת את הרמז שהועבר

Sec-CH-UA-Model: "Pixel 5"

אפשר לציין כמה רמזים לכמה מקורות, ולא רק מהטווח ch-ua:

‫⬇️ תגובה מ-blog.site שמעבירה כמה רמזים לכמה מקורות

Accept-CH: Sec-CH-UA-Model, DPR
Permissions-Policy: ch-ua-model=(self "https://cdn.site"),
                    ch-dpr=(self "https://cdn.site" "https://img.site")

העברת רמזים ל-iframe

רכיבי iframe ממקורות שונים פועלים באופן דומה למשאבים ממקורות שונים, אבל אתם מציינים את ההצעות שאתם רוצים להעביר במאפיין allow.

‫⬇️ תשובה מאת blog.site

Accept-CH: Sec-CH-UA-Model

‫↪️ קוד HTML של blog.site

<iframe src="https://widget.site" allow="ch-ua-model"></iframe>

‫⬆️ בקשה אל widget.site

Sec-CH-UA-Model: "Pixel 5"

המאפיין allow ב-iframe יבטל כל כותרת Accept-CH ש-widget.site עשוי לשלוח בעצמו, לכן חשוב לוודא שציינתם את כל מה שהאתר ב-iframe צריך.

הינטים דינמיים בצד השרת

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

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

לדוגמה, יכול להיות שבחלק מסוים באתר אתם רוצים להציג סמלים ופקדים שמתאימים למערכת ההפעלה של המשתמש. לשם כך, יכול להיות שתרצו להוסיף גם את Sec-CH-UA-Platform-Version כדי להציג משאבי משנה מתאימים.

‫⬇️ כותרות תגובה של /blog

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA

‫⬇️ כותרות תגובה של /app

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA

הימנעות מהידורים בצד השרת שנדרשים בבקשה הראשונה

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

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

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

‫⬆️ בקשה ראשונית

[With default headers]

‫⬇️ כותרות תגובה

Accept-CH: Sec-CH-UA-Model
Critical-CH: Sec-CH-UA-Model

‫🔃 הדפדפן מנסה שוב לשלוח את הבקשה הראשונית עם הכותרת הנוספת

[With default headers + …]
Sec-CH-UA-Model: Pixel 5

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

במצבים שבהם אתם באמת צריכים רמזים נוספים בטעינת דף הראשונה, ההצעה Client Hints Reliability מציגה מסלול לציין איסוף אותות בצד הלקוח בהגדרות ברמת החיבור. כדי לאפשר את ההעברה המוקדמת הזו של רמזים בחיבורי HTTP/2 ו-HTTP/3, נעשה שימוש בתוסף Application-Layer Protocol Settings (ALPS)‎ ל-TLS 1.3. הפרויקט עדיין בשלב מוקדם מאוד, אבל אם אתם מנהלים באופן פעיל את הגדרות ה-TLS והחיבור שלכם, זה הזמן האידיאלי לתרום.

תמיכה מדור קודם

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

UA-CH retrofill היא ספרייה קטנה שמאפשרת להחליף את navigator.userAgent במחרוזת חדשה שנוצרה מהערכים המבוקשים של navigator.userAgentData.

לדוגמה, הקוד הזה יוצר מחרוזת של סוכן משתמש שכוללת גם את הרמז "model":

import { overrideUserAgentUsingClientHints } from './uach-retrofill.js';
overrideUserAgentUsingClientHints(['model'])
  .then(() => { console.log(navigator.userAgent); });

המחרוזת שתתקבל תציג את הדגם Pixel 5, אבל עדיין תציג את הגרסה המצומצמת 92.0.0.0 כי לא התבקש הרמז uaFullVersion:

Mozilla/5.0 (Linux; Android 10.0; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.0.0 Mobile Safari/537.36

עזרה נוספת

אם האסטרטגיות האלה לא מתאימות לתרחיש לדוגמה שלכם, אתם יכולים להתחיל דיון במאגר privacy-sandbox-dev-support כדי שנוכל לבדוק את הבעיה ביחד.