Estrategia de fuentes web:
los cuatro pasos que siempre aplico

Autoalojamiento, WOFF2, subconjunto y preload, medidos en esta web.

Todo el texto de esta web utiliza la fuente tipográfica JetBrains Mono. Así que, si la fuente va lenta, va lenta la página entera. Los tres font-weight que uso suman 825 KB en los TTF tal y como los publica JetBrains. Lo que se carga de verdad son 65 KB, desde mi propio servidor y pedidos antes de que el navegador sepa siquiera que los va a necesitar. Así es cómo logro hacerlo consistentemente:

Fuentes Rendimiento web Core Web Vitals WPO

Autoaloja la fuente

Lo primero que hago siempre es quitar la importación de fuente de un servicio de terceros y servirla desde mi propio dominio. Un enlace a Google Fonts obliga al navegador a hablar con dos servidores nuevos: uno para la hoja de estilos, que bloquea el renderizado, y otro para el archivo de fuente al que apunta esa hoja. Cada servidor nuevo cuesta una consulta DNS y un handshake TLS antes de que se mueva un solo byte. Al autoalojarla, todo eso se queda en una única petición por una conexión que la página ya tenía abierta.

Un preconnect puede adelantar esos handshakes, pero no se salta la hoja de estilos de en medio. Y el viejo argumento de que el visitante ya tiene la copia de Google en caché dejó de valer hacia 2020, cuando los navegadores empezaron a guardar una caché aparte para cada web.

Además, si el archivo es tuyo, las cabeceras de caché también. Un archivo de fuente nunca cambia sin cambiar también de nombre, así que el navegador puede guardarlo un año entero sin volver a preguntar:

/assets/fonts/*
  Cache-Control: public, max-age=31536000, immutable

Lo hago por la velocidad, pero no es el único beneficio: una dependencia externa menos y ningún servidor ajeno enterándose de quién entra. Y eso último no es teórico: en 2022, un tribunal alemán condenó al dueño de una web a indemnizar a un visitante por usar Google Fonts, porque enviaba su IP a Google sin su consentimiento.

Google Fonts

  1. HTML
  2. DNS
  3. TLS
  4. CSS
  5. DNS
  6. TLS
  7. WOFF2

fonts.googleapis.com -> fonts.gstatic.com

Autoalojada

  1. HTML
  2. WOFF2

miguelpv.dev — conexión ya abierta

Lo que tiene que esperar el navegador hasta que llega el primer byte de la fuente. Cada caja es un paso que no se puede saltar, no una duración medida: la ruta de terceros añade dos consultas DNS, dos handshakes y una hoja de estilos antes de que la fuente empiece siquiera a descargarse.

Sírvela en woff2

La elección del formato es más simple de lo que parece: woff2 es el formato de fuente web más comprimido que hay, y todos los navegadores para los que desarrollo lo entienden desde hace años. Los mismos glifos con un tercio de los bytes: JetBrains Mono Regular pesa 274 KB en TTF y 92 KB en WOFF2.

Así que sirvo WOFF2 y nada más: un @font-face por font-weight, cada uno apuntando a un único archivo. Sin formatos de reserva ni una lista de src que el navegador tenga que ir probando.

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  src: url('/assets/fonts/jetbrains-mono-v24-latin-regular.woff2')
       format('woff2');
}
El peso regular, tal y como lo declara esta web. Medium y semibold son la misma regla con otro peso y otro archivo.

Haz un subconjunto y quita los font-weight que no uses

En proporción, aquí está el mayor recorte: el WOFF2 ya dejaba los archivos en un tercio, y el subconjunto vuelve a dividirlos entre más de cuatro. Un archivo de fuente completo trae cirílico, griego, vietnamita y cientos de símbolos que esta web no usa nunca, así que sólo sirvo el subconjunto latin. Cubre también la versión en español, porque la á, la é, la ñ, el ¿ y el ¡ están todos en él, así que un mismo archivo sirve para los dos idiomas. El WOFF2 completo pesa 92 KB; el latin, 21 KB.

Después recorto los font-weight a los que el diseño usa de verdad. Aquí son tres (regular, medium y semibold) y ninguna cursiva: cada peso o estilo de más es otro archivo que descargar.

  • TTF, completa 825 KB
  • WOFF2, completa 280 KB
  • WOFF2, latino 65 KB
Los tres font-weights que usa esta web (400, 500 y 600), sumados. El TTF y el WOFF2 completos son de la versión oficial 2.304 de JetBrains Mono; el subconjunto latin son los archivos que sirve esta web de verdad. Casi 13 veces más ligero, sin perder nada visible.

Precarga las que importan

El navegador no pide una fuente cuando lee el @font-face. Espera a haber analizado la página, aplicado los estilos y encontrado algún texto que use esa fuente. Eso es tarde, incluso con el CSS en línea, como aquí. Un preload en el <head> lanza la descarga en el acto, en paralelo con todo lo demás:

<link rel="preload" href="/assets/fonts/jetbrains-mono-v24-latin-regular.woff2"
      as="font" type="font/woff2" crossorigin />

Aquí hay dos detalles importantes. crossorigin no es opcional, ni siquiera en tu propio dominio: los navegadores siempre piden las fuentes en modo CORS, así que un preload sin ese atributo no coincide con la petición real y la fuente se descarga dos veces. Y precargo justo las fuentes que usa la primera pantalla (aquí, las tres), ni una más. Si lo precargas todo, tus fuentes acaban peleándose con tus propios recursos críticos por el ancho de banda.

Sin preload

  1. HTML
  2. parseo
  3. WOFF2
  4. texto

Con preload

  1. HTML
  2. parseo
  3. texto
  1. WOFF2
Sin preload, la petición de la fuente espera a que termine el parseo. Con ella, la petición sale mientras el <head> todavía se está leyendo, así que la fuente ya está ahí cuando el texto la necesita. Ilustrativo, no a escala.

Cuándo lo haría de otra forma

Estos cuatro pasos son mi punto de partida, no una ley: son lo que necesitaba esta web. Hay cosas que me harían cambiar de idea:

  • Más fuentes: cuantas más familias y pesos necesite un diseño, más importa recortar caracteres y pesos, y más difícil es precargarlo todo sin atascar la ruta crítica. A partir de cierto punto, la solución de verdad es usar menos fuentes, no cargarlas con más maña.
  • Un idioma fuera del alfabeto latino: si algún día añado uno, serviría sus glifos como un subconjunto aparte con unicode-range, para que quien lee en inglés o en español no tenga que descargarlos nunca.
  • Navegadores antiguos de verdad: sólo sirvo WOFF2 porque todos los navegadores para los que desarrollo lo entienden. Si tuviera que llegar a navegadores mucho más viejos, añadiría un woff de reserva a la lista src, y esos bytes de más sólo se los descargarían ellos.
  • Una fuente que no consigo hacer rápida: si una tipografía corporativa tuviera que aparecer sí o sí y no lograra hacerla pequeña, recortarla ni precargarla, lo honesto sería cambiar de fuente, no servir una lenta.

Y la mejora no es exclusiva de mi web. En una web que revisé hace poco con un amigo, arreglar sólo las fuentes (autoalojarlas, WOFF2, subconjuntos, menos familias y precargar las adecuadas) redujo su peso unas 10 veces. La página pasó de un 47 en PageSpeed, con el FCP y el LCP en torno a los 10 segundos, a un 92–96, con ambas métricas entre 2,0 y 2,7 s. Mismo contenido, mismo diseño. Las fuentes simplemente dejaron de ser el cuello de botella.

Esa es toda la estrategia: haz que la fuente pese poco y sírvela pronto. Abre la pestaña Red de las DevTools, busca tus archivos de fuente y mira cuánto pesan y cuándo empiezan a descargarse. Esas dos cifras te dicen por qué paso empezar.

Lecturas relacionadas