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

אסטרטגיות להעברת האתר מהסתמכות על מחרוזת הסוכן המשתמש לשימוש ברמזים החדשים על הלקוח (Client Hints) לגבי הסוכן המשתמש.

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

במאמר הזה נסביר איך מבצעים ביקורת על הגישה לנתוני סוכן המשתמש ומעבירים את השימוש במחרוזת של סוכן המשתמש לרמזים על הלקוח (Client Hints) לגבי סוכן המשתמש.

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

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

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

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

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

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

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

קבוצת ברירת המחדל של רמזים על הלקוח (Client Hints) לגבי הסוכן המשתמש כוללת:

  • Sec-CH-UA: שם הדפדפן וגרסה ראשית/משמעותית
  • Sec-CH-UA-Mobile: ערך בוליאני שמציין מכשיר נייד
  • Sec-CH-UA-Platform: שם מערכת ההפעלה
    • הערה: השינוי הזה עודכן במפרט, והוא יבוצע בקרוב ב-Chrome ובדפדפנים אחרים המבוססים על Chromium.

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

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

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

אסטרטגיה: ממשק API של JavaScript בצד הלקוח על פי דרישה

אם אתם משתמשים כרגע ב-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, כך שאפשר לבקש ערכים של סוכן משתמש בכל שלב.

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

אם אתם משתמשים בכותרת הבקשה 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 אם היא לא זמינה.

אסטרטגיה: הענקת הרשאות לקבלת רמזים לבקשות מ-origin שונים

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

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

⬇️ התשובה מ-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")

שיטה: הענקת הרשאות ל-hints למסגרות 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

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

במצבים שבהם אתם באמת זקוקים לאיתותים נוספים בעומס הדף הראשון, הצעה לשיפור האמינות של האותות מהלקוח מציגה דרך לציין את האותות בהגדרות ברמת החיבור. לשם כך, נעשה שימוש בתוסף Application-Layer Protocol Settings‏(ALPS) ל-TLS 1.3 כדי לאפשר העברה מוקדמת של רמזים בחיבורי HTTP/2 ו-HTTP/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

תמיכה נוספת

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

תמונה של Ricardo Rocha ב-Unsplash