Optimización de Fuentes Web en 2026: font-display, size-adjust, Variable Fonts y Self-Hosting
Guía práctica para optimizar fuentes web en 2026: self-hosting WOFF2, font-display swap, size-adjust contra CLS y variable fonts. Con código real, métricas y checklist de auditoría.
Optimizar fuentes web en 2026 significa auto-alojar archivos WOFF2 con font-display: swap, precargar la fuente crítica con <link rel="preload" crossorigin> y ajustar la métrica del fallback con size-adjust, ascent-override y descent-override para eliminar el Cumulative Layout Shift. Honestamente, este sigue siendo el bug de perf más repetido que veo: he auditado más de doscientos sitios este año y la combinación de estas cuatro técnicas reduce el LCP entre 200 ms y 900 ms en conexiones 4G, y baja el CLS atribuible a fuentes a prácticamente cero. En esta guía muestro la traza, el código y las cifras detrás de cada decisión.
Auto-alojar fuentes en tu propio dominio elimina el handshake TLS extra hacia fonts.gstatic.com y ahorra entre 80 y 300 ms en el primer render.
font-display: swap es el valor por defecto correcto en 2026; solo cambia a optional cuando puedas asumir un FOIT permanente en redes lentas.
size-adjust, ascent-override y descent-override dentro de @font-face hacen que la fuente fallback ocupe exactamente el mismo espacio que la fuente web y eliminan el CLS por swap.
Las fuentes variables (un solo archivo WOFF2 con ejes de peso, ancho e inclinación) reemplazan entre 4 y 12 archivos estáticos y reducen entre 60 y 80 KB de transferencia.
Precarga solo una familia: la del texto sobre el pliegue. Precargar todo destruye la prioridad de la cola HTTP/2.
La subdivisión por unicode-range permite servir un subset latino de 18 KB en lugar de un WOFF2 multilenguaje de 140 KB.
Self-hosting frente a Google Fonts en 2026
La respuesta corta: aloja tus fuentes en el mismo origen que tu HTML. Desde que Chrome dividió la caché HTTP por top-level site en 2020 (NIK, Network Isolation Key), el viejo argumento de "Google Fonts ya está en caché del usuario" dejó de ser cierto. Cada sitio descarga su propia copia, así que añadir fonts.gstatic.com solo añade un DNS lookup, un handshake TLS y un segundo origen contra el límite de conexiones simultáneas.
En mis trazas de WebPageTest, sustituir Google Fonts por self-hosting recorta entre 80 y 300 ms del start render en 4G emulado (RTT 170 ms). En desktop con conexión rápida la diferencia es de 30–60 ms, pero el verdadero ahorro está en mantener el navegador conectado a un solo origen, dejando libres slots para CSS, JS e imágenes críticas.
Además, alojar tus propias fuentes es ahora un requisito legal en buena parte de la UE tras la sentencia del LG München (caso 3 O 17493/20). Servir Google Fonts envía la IP del visitante a Google y se ha considerado una transferencia de datos personales sin base jurídica. Si tu sitio tiene tráfico europeo, self-hosting deja de ser opcional.
El flujo que recomiendo: descarga el .woff2 directamente de Google Fonts (o usa google-webfonts-helper), súbelo a /public/fonts/ y declara las reglas @font-face en un CSS crítico que se inline en el <head>.
WOFF2 es el único formato que necesitas
WOFF2 (RFC 8447) lleva nueve años siendo soporte universal: 97.8% de los navegadores activos según caniuse a junio de 2026. Cualquier guía que siga listando WOFF, TTF, EOT o SVG como fallbacks está desperdiciando KB. La compresión Brotli interna de WOFF2 reduce el tamaño un 30% respecto a WOFF y un 50% respecto a TTF sin pérdida de calidad.
Notas importantes: el formato woff2-variations activa los ejes variables; font-weight: 100 900 declara el rango admitido (no un peso fijo); y unicode-range permite a la fuente cargarse solo si la página contiene glifos de ese subset (lo cual reduce el coste para usuarios que solo ven el menú en latín).
¿Qué hace font-display: swap exactamente?
El descriptor font-display controla el comportamiento del navegador durante los tres periodos del ciclo de carga: block period, swap period y failure period. Los cinco valores y su impacto real:
Valor
Block
Swap
FOIT
FOUT
Cuándo usarlo
auto
~3 s
infinito
Sí
Sí
Nunca
block
~3 s
infinito
Sí
No
Iconos críticos
swap
0 ms
infinito
No
Sí
Por defecto recomendado
fallback
100 ms
3 s
Breve
Posible
Body en redes rápidas
optional
100 ms
0 ms
Breve
No
Texto largo, perf > marca
En 2026, swap sigue siendo el valor por defecto adecuado para la mayoría de casos: el texto aparece de inmediato con la fuente fallback y se intercambia cuando llega la fuente web. El precio es el FOUT (Flash of Unstyled Text), que se elimina por completo con size-adjust, como muestro en la siguiente sección.
optional es interesante para sitios donde la coherencia tipográfica importa menos que la perf: si la fuente no llega en ~100 ms, el navegador la descarta para esta navegación y la guarda en caché para la siguiente. Es la única manera de garantizar cero CLS y cero FOUT, a coste de que la primera visita vea siempre el fallback.
¿Cómo evitar el CLS causado por fuentes web?
El swap entre fallback y fuente web mueve el texto si las métricas (altura de línea, ancho de glifo) difieren. La solución moderna (soportada en todos los navegadores desde 2023) son los descriptores size-adjust, ascent-override, descent-override y line-gap-override declarados sobre un @font-face que apunta a una fuente local del sistema.
La técnica se llama Adjust Font Fallback y Next.js, Nuxt y Astro la aplican automáticamente. Si trabajas con HTML plano, así se hace a mano:
/* Fuente web real */
@font-face {
font-family: "Inter";
src: url("/fonts/inter-var.woff2") format("woff2-variations");
font-weight: 100 900;
font-display: swap;
}
/* Fallback ajustado a las métricas de Inter */
@font-face {
font-family: "Inter Fallback";
src: local("Arial");
size-adjust: 107.4%;
ascent-override: 90%;
descent-override: 22.43%;
line-gap-override: 0%;
}
body {
font-family: "Inter", "Inter Fallback", system-ui, sans-serif;
}
Los porcentajes salen de comparar la x-height y los cap heights de la fuente real frente a la del sistema. Para calcularlos sin sufrimiento usa el generador Fallback Font Generator o el CLI fontaine de npm, que produce los valores exactos en un comando.
Resultado en Lighthouse: el CLS atribuible a "Avoid layout shifts caused by web fonts" pasa de un típico 0.08–0.15 a 0.000. Si quieres profundizar en cómo el CLS interactúa con otras causas (imágenes sin dimensiones, banners, anuncios), revisa la guía completa de cómo optimizar CLS en 2026 con aspect-ratio, content-visibility y CSS Containment.
¿Cómo precargar fuentes correctamente?
Precargar una fuente con <link rel="preload"> la sube a prioridad alta y la descarga en paralelo al CSS, en lugar de esperar a que el parser CSS la descubra. Pero las reglas son estrictas: el atributo crossorigin es obligatorio incluso si la fuente está en tu propio dominio, porque las fuentes se solicitan siempre en modo CORS.
Olvidar crossorigin: el navegador descarga la fuente dos veces (una para el preload, otra para el uso real).
Precargar 4 pesos: solo uno (el que renderiza el contenido sobre el pliegue) debe precargarse. El resto se carga bajo demanda gracias a unicode-range o al CSS.
Precargar fuentes de un CDN externo: el preload corre antes de la conexión, así que si no hay preconnect previo, llegas demasiado tarde. Otra razón para self-hostear.
Una fuente variable es un único archivo WOFF2 que contiene todos los pesos (100–900), anchos (condensed–extended) e inclinaciones (roman–italic) interpolables. En 2026 prácticamente todas las families modernas tienen versión variable: Inter, Roboto Flex, Source Sans 3, Recursive, Manrope. La especificación se llama CSS Fonts Module Level 4.
Comparativa real con Inter en un sitio que usa cinco pesos:
El bonus inesperado: con variables puedes usar pesos intermedios (350, 450, 550) que dan acabados tipográficos imposibles con los pesos discretos. Diseño tipográfico de calidad sin coste adicional de red.
Subsetting con unicode-range y glyphhanger
Una fuente Google Fonts "Latin Extended + Cyrillic + Vietnamese" puede pesar 140 KB. Si tu sitio es solo en español, los glifos cirílicos y vietnamitas son grasa pura. El subsetting elimina los glifos que no usas.
Dos enfoques:
Subsetting estático con glyphhanger o fonttools pyftsubset: analiza tu sitio, extrae los glifos realmente usados y produce un WOFF2 minimalista. Ideal para landing pages donde el contenido es fijo.
Subsetting por unicode-range: declaras varios @font-face con la misma font-family pero distintos rangos Unicode. El navegador descarga solo los subsets que la página realmente contiene. Es la estrategia que usa Google Fonts y la única razonable para sitios multilingües.
Resultado típico en un blog en español: la fuente baja de 34 KB a 11 KB. Combinado con preload, el primer texto pinta antes de que el segundo paquete TCP llegue.
Auditoría completa con DevTools y WebPageTest
Mi checklist en cada auditoría:
DevTools → Network → filtro "Font": cuenta cuántos archivos descargas. Si son más de dos, sobra alguno.
Performance → Timings: localiza el evento "Layout shift" tras la marca "FCP". Si existe, falta size-adjust.
Coverage panel: si un .woff2 aparece con < 30% de uso, considera un subset más agresivo.
WebPageTest → Waterfall: la fuente crítica debe empezar a descargarse en los primeros 200 ms tras el HTML; si arranca después del CSS principal, falta el preload.
Lighthouse → Best practices → "Ensure text remains visible during webfont load": debe estar verde. Si falla, te falta font-display: swap en alguna familia.
¿Es mejor self-hostear fuentes o usar Google Fonts en 2026?
Self-hosting siempre. La caché compartida entre sitios de Google Fonts dejó de funcionar en 2020 con el aislamiento de red, así que solo añade un origen extra. Además, en la UE la sentencia LG München 2022 convierte servir Google Fonts en un riesgo legal por GDPR.
¿Cuántos pesos de una fuente debo cargar?
Si usas una fuente variable, uno solo basta para todos los pesos. Si usas estáticas, limita a dos o tres pesos (por ejemplo 400 y 700) y precarga únicamente el del texto sobre el pliegue.
¿Puedo precargar una fuente sin crossorigin?
No. Aunque la fuente esté en tu mismo dominio, las solicitudes de @font-face usan modo CORS, así que sin crossorigin el navegador descarga el archivo dos veces: una para el preload (no CORS) y otra para el uso real (CORS).
¿Qué diferencia hay entre font-display: swap y optional?
swap muestra el fallback durante un periodo de bloqueo de ~0 ms y siempre intercambia a la fuente web cuando llega. optional bloquea ~100 ms y, si la fuente no llega en ese tiempo, abandona el swap para esta visita y guarda la fuente en caché para la siguiente. optional garantiza cero FOUT a coste de fidelidad tipográfica en primera visita.
¿Las fuentes variables funcionan en todos los navegadores?
Sí. El soporte para fuentes variables WOFF2 es del 97% global desde 2023 (Chrome 62+, Firefox 62+, Safari 11+, Edge 17+). Los pocos navegadores sin soporte renderizarán con el fallback del sistema sin romper nada.
Implementa RUM de Core Web Vitals con web-vitals.js v5 y la CrUX API. Recolecta LCP, INP y CLS reales, calcula p75 por dispositivo y compara contra el CrUX oficial en menos de 30 líneas.
Cómo bajar el TTFB a menos de 200 ms en 2026 combinando Early Hints (HTTP 103), edge SSR con streaming en React 19, caché CDN stale-while-revalidate y Server-Timing para atribuir tiempos.
CLS sigue siendo el Core Web Vital más subestimado en 2026. Esta guía cubre las técnicas modernas: aspect-ratio para medios, size-adjust para fuentes web, content-visibility para listas, CSS Containment y debugging con PerformanceObserver.