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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

אם אתם משתמשים כרגע ב-navigator.userAgent, צריך לעבור להעדיף navigator.userAgentData לפני שחוזרים לניתוח מחרוזת ה-user-agent.

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 בשרת והצרכים שלכם לגבי הנתונים האלה עקביים יחסית בכל האתר, אפשר לציין את הרמזים הרצויים של הלקוח כקבוצה סטטית בתשובות. זו גישה פשוטה יחסית, כי בדרך כלל צריך להגדיר אותה רק במקום אחד. לדוגמה, יכול להיות שהוא יהיה בתצורה של שרת האינטרנט שלכם אם כבר הוספתם לו כותרות, את הגדרות האירוח או את התצורה ברמה העליונה של ה-framework או הפלטפורמה שבהם אתם משתמשים באתר.

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

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

לדוגמה, ברירות המחדל הנוכחיות עבור 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 אם היא לא זמינה.

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

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

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

🎶️ תשובה שהתקבלה מ-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

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

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

אסטרטגיה: תמיכה מדור קודם

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

מילוי מחדש של UA-CH הוא ספרייה קטנה שמאפשרת להחליף את 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 כדי שנוכל לבדוק את הבעיה ביחד.

תמונה מאת ריקרדו רוצ'ה ב-Unbounce