Proxy de telemetría del navegador y Core Web Vitals
El Telemetry JavaScript SDK utiliza una clave secreta API y pertenece al código de servidor confiable. No incluya esa clave en una aplicación de navegador. Para recopilar Core Web Vitals o eventos de confiabilidad de frontend limitados, envíe una pequeña carga útil incluida en la lista permitida a su propio punto final del mismo origen y deje que ese punto final la reenvíe a Telemetry.
Este diseño mantiene las credenciales en el lado del servidor y le brinda a la aplicación un lugar para hacer cumplir el consentimiento, las verificaciones de origen, los límites de velocidad, los tipos de campos, el tamaño de la carga útil y las reglas de privacidad.
Arquitectura
browser measurement
-> POST /api/browser-telemetry
-> origin, size, rate, and schema checks
-> server-side telemetry-sh client
-> browser_performance_measured table
El proxy no es un punto final de registro general. Acepte sólo nombres de eventos conocidos y campos conocidos. Nunca acepte un nombre de tabla proporcionado por el cliente o una clave Telemetry API.
Definir el contrato del navegador
Para Core Web Vitals, un evento por métrica es fácil de validar:
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;
};
Utilice una plantilla de ruta como /projects/:id, no location.href. No envíe cadenas de consulta, texto DOM, valores de formulario, cookies, datos de autorización, referencias que contengan identificadores ni mensajes de error arbitrarios. Una ID de métrica aleatoria puede ayudar a deduplicar la retransmisión, pero no debería convertirse en un identificador de usuario entre sitios.
Recopile Web Vitals en el navegador
El paquete web-vitals informa los Core Web Vitals actuales. Enviar después del consentimiento cuando su política y jurisdicción requieran el consentimiento:
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 es apropiado para cargas útiles pequeñas de mejor esfuerzo durante la terminación de la página. La entrega no está garantizada, las extensiones del navegador pueden bloquear la solicitud y un usuario puede cerrar la página antes de la transmisión. Trate las mediciones del navegador como datos de experiencia de muestra, no como un libro de auditoría o facturación exacto.
Validar y reenviar en el servidor
El punto final debe rechazar orígenes desconocidos, nombres de eventos, nombres de métricas, valores no finitos, cuerpos de gran tamaño y campos inesperados. El siguiente ejemplo muestra el límite; Adapte las primitivas de solicitud y respuesta al marco del servidor:
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 });
}
En producción, inicialice el cliente una vez por proceso del servidor, utilice un tiempo de espera ascendente limitado y decida si los errores de telemetría devuelven 202, 204 o una respuesta reintentable. Evite los bucles de reintento del navegador. Un pequeño límite de velocidad por red y límite de sesión puede reducir el abuso, pero no almacene una dirección IP sin procesar en ese caso.
Lista de verificación de seguridad y privacidad
- Mantenga
TELEMETRY_API_KEYúnicamente en el tiempo de ejecución del servidor. - Restrinja el punto final a solicitudes del mismo origen y los métodos y tipos de contenido que espera.
- Aplique un límite de cuerpo pequeño antes de analizar JSON.
- Lista de permitidos nombres de eventos, campos, categorías, plantillas de ruta y rangos numéricos.
- Límite de velocidad antes de llamar al API ascendente.
- No utilice CORS permisivo a menos que la recopilación de orígenes cruzados sea un producto revisado intencional.
- Aplique sus reglas de consentimiento y exclusión voluntaria antes de la recolección.
- Defina el comportamiento de retención y eliminación de cualquier identificador.
- Supervise las solicitudes rechazadas y de velocidad limitada sin copiar las cargas útiles rechazadas.
Las defensas CSRF siguen siendo importantes cuando el punto final acepta credenciales o cambia el estado vinculado al usuario. Para un punto final de medición anónimo del mismo origen, la validación del origen, los tipos de contenido estrictos y una carga útil no específica del usuario pueden ser el límite apropiado; confirme esa elección con el modelo de seguridad de la aplicación.
Verificar los datos
Implemente primero en un entorno que no sea de producción. Confirma que:
- El paquete del navegador no contiene ninguna clave Telemetry.
- Se rechazan los nombres de eventos desconocidos, los campos adicionales, las URL sin formato y los cuerpos de gran tamaño.
- Los eventos CLS, INP y LCP válidos llegan con valores numéricos.
- Una solicitud de cobro bloqueada o fallida no afecta la navegación.
- Los valores de liberación y ruta son lo suficientemente estables como para agruparlos.
- Las rutas dispersas no se utilizan para alertas ruidosas.
Utilice el Core Web Vitals por ruta y receta de lanzamiento para mantener el recuento de muestras junto a las mediciones. Continúe con Monitoreo de confiabilidad frontend con SQL, Guía JavaScript SDK y Eliminación de datos sensibles confidenciales.