瀏覽器遙測代理與 Core Web Vitals
Telemetry JavaScript SDK 使用秘密 API 金鑰,屬於可信伺服器程式碼。不要將該金鑰捆綁到瀏覽器應用程式中。要收集 Core Web Vitals 或有界前端可靠性事件,請將一個小的白名單有效負載傳送到您自己的同源端點,並讓該端點將其轉發到 Telemetry。
這種設計將憑證保留在伺服器端,併為應用程式提供了一個地方來強制同意、來源檢查、速率限制、欄位型別、有效負載大小和隱私規則。
建築
browser measurement
-> POST /api/browser-telemetry
-> origin, size, rate, and schema checks
-> server-side telemetry-sh client
-> browser_performance_measured table
代理不是一般的日誌端點。僅接受已知事件名稱和已知欄位。切勿接受客戶端提供的資料表名稱或 Telemetry API 金鑰。
定義瀏覽器合約
對於 Core Web Vitals,每個指標一個事件很容易驗證:
type BrowserMetric = {
event_name: "browser_performance_measured";
metric_name: "CLS" | "INP" | "LCP";
metric_value: number;
metric_id: string;
route_template: string;
release: string;
navigation_type: string;
};
使用 /projects/:id 等路由範本,而不是 location.href。請勿傳送查詢字串、DOM 文字、表單值、cookie、授權資料、包含識別符號的引用站點或任意錯誤訊息。隨機指標 ID 可以幫助消除重複重傳,但它不應該成為跨站點使用者識別符號。
在瀏覽器中收集 Web Vitals
web-vitals 包報告當前的 Core Web Vitals。如果您的政策和司法管轄區需要同意,則在同意後傳送:
import { onCLS, onINP, onLCP, type Metric } from "web-vitals";
function reportMetric(metric: Metric) {
const body = JSON.stringify({
event_name: "browser_performance_measured",
metric_name: metric.name,
metric_value: metric.value,
metric_id: metric.id,
route_template: routeTemplateFor(location.pathname),
release: window.__APP_RELEASE__,
navigation_type: metric.navigationType,
});
if (navigator.sendBeacon) {
navigator.sendBeacon(
"/api/browser-telemetry",
new Blob([body], { type: "application/json" }),
);
return;
}
void fetch("/api/browser-telemetry", {
method: "POST",
headers: { "content-type": "application/json" },
body,
keepalive: true,
credentials: "same-origin",
});
}
onCLS(reportMetric);
onINP(reportMetric);
onLCP(reportMetric);
sendBeacon 適用於頁面終止期間的小型盡力而為有效負載。無法保證交付,瀏覽器擴充套件可能會阻止請求,並且使用者可能會在傳輸之前關閉頁面。將瀏覽器測量結果視為取樣的體驗資料,而不是精確的計費或稽核分類帳。
在伺服器上驗證並轉發
端點應拒絕未知來源、事件名稱、指標名稱、非有限值、超大主體和意外欄位。下面的例子顯示了邊界;使請求和回應原語適應伺服器框架:
import { Telemetry } from "telemetry-sh";
const telemetry = new Telemetry(process.env.TELEMETRY_API_KEY);
const allowedMetrics = new Set(["CLS", "INP", "LCP"]);
export async function POST(request: Request) {
if (!isAllowedSameOrigin(request)) {
return new Response("forbidden", { status: 403 });
}
const contentLength = Number(request.headers.get("content-length") || 0);
if (contentLength > 4096 || !(await rateLimit(request))) {
return new Response("rejected", { status: 429 });
}
const input = await request.json();
if (
input.event_name !== "browser_performance_measured" ||
!allowedMetrics.has(input.metric_name) ||
!Number.isFinite(input.metric_value) ||
!isAllowedRouteTemplate(input.route_template)
) {
return new Response("invalid event", { status: 400 });
}
await telemetry.log("browser_performance_measured", {
metric_name: input.metric_name,
metric_value: input.metric_value,
metric_id: boundedString(input.metric_id, 80),
route_template: input.route_template,
release: boundedString(input.release, 80),
navigation_type: boundedCategory(input.navigation_type),
});
return new Response(null, { status: 202 });
}
在生產中,每個伺服器處理程序初始化一次客戶端,使用有限的上游超時,並決定遙測失敗是否返回 202、204 或可重試的回應。避免瀏覽器重試迴圈。網路和會話邊界的小速率限制可以減少濫用,但不要在事件中儲存原始 IP 地址。
安全和隱私清單
- 僅將
TELEMETRY_API_KEY保留在伺服器執行時中。 - 將端點限制為同源請求以及您期望的方法和內容型別。
- 在解析 JSON 之前應用較小的正文限制。
- 允許列表事件名稱、欄位、類別、路由範本和數字範圍。
- 呼叫上行前限速API。
- 除非跨源收集是有意且經過審查的產品,否則請勿使用寬鬆的 CORS。
- 在收集之前應用您的同意和選擇退出規則。
- 定義任何識別符號的保留和刪除行為。
- 監控被拒絕和速率限制的請求,而無需複製被拒絕的有效負載。
當端點接受憑據或改變使用者連結狀態時,CSRF 防禦仍然很重要。對於匿名、同源測量端點,源驗證、嚴格內容型別和非使用者特定的有效負載可能是適當的邊界;使用應用程式的安全模型確認該選擇。
驗證資料
首先部署到非生產環境。確認:
- 瀏覽器捆綁包不包含 Telemetry 金鑰。
- 未知的事件名稱、額外欄位、原始 URL 和過大的正文將被拒絕。
- 有效的 CLS、INP 和 LCP 事件以數值形式到達。
- 收集請求被阻止或失敗不會影響導覽。
- 發布和路由值足夠穩定,可以進行分組。
- 稀疏路線不用於嘈雜的警示。
使用 按路線和發布配方劃分的核心 Web Vitals 在測量旁邊保留樣本計數。繼續使用 使用 SQL 進行前端可靠性監控、JavaScript SDK 導軌 和 脫敏敏感資料。