Como desarrollador web enfocado en la optimización de las páginas web que he creado, siempre he intentado evitar la sobrecarga de recursos en exceso. Por lo general, la carga de fuentes es, junto a las imágenes, uno de los recursos que suele tener gran peso en el diseño. El inconveniente es que también lo tiene en la performance de la web en cuestión.

Desde hace semanas (o meses) quería escribir sobre este tema, aunque pensaba que en ¡2019! poco podía aportar ya a la causa con todo lo que se había escrito sobre este asunto. Hasta que me encontré con este tweet en timeline:

Self hosting your web fonts is the best choice for performance (maybe not for your wallet though)

5:35 - 13 ago. 2019 @zachleat

Pues bien, tras realizar algunos experimentos con la web que estas leyendo, me encuentro no solo que no es cierto, sino que el usar Google Font, como origen del recurso, mejoramos nuestras métricas entre 100 y 200 milisegundos según el contexto: móvil o escritorio. Claro está, esto siempre según Pagespeed Insights de Google.

Veamos los resultados para móvil de la página de inicio:

Page speed usando fuente en servidorResultados con la fuente alojada en mi servidor

Page speed fuente servida desde Google FontsResultado con la fuente alojada en Google Fonts

OJO: Estos datos son obtenidos de la parte de laboratorio y no de la experiencia de usuarios reales. En todo caso suele ser una buena aproximación. Tampoco hay que perder de vista que cada vez que lanzamos el test los resultados pueden variar.

Otros de los datos que suele arrojarnos la herramienta Pagespeed Insights de Google comúnmente, respecto al análisis y uso de fuentes en la web, es la visibilidad del texto en nuestra estrategia de carga de fuentes. En la mayoría de ocasiones nos advierte con un mensaje como el de la imagen siguiente:

Advertencia sobre fuentes de Pagespeed Insights

Cómo cargar una familia de fuentes

Definir una fuente en nuestro CSS

Primero veamos como definir una fuente en nuestro CSS y con que propiedades o descriptores contamos y sus valores.

@font-face {
    font-family: 'FontName';
    font-display: auto | block | swap | fallback;
    font-style: normal | italic | oblique | initial | inherit;
    font-weight: normal | bold | bolder | lighter | number | initial | inherit;
    src: local('Font Name'), url(path-to-font-file) format('eot | ttf | woff | woff2');
    unicode-range: U+0000-00FF;
}

Información sobre descriptores obligatorios y opcionales

Del código anterior debemos saber que las propiedades obligatorias son:

  1. font-family: Definimos el nombre que usaremos en nuestra hoja de estilos.
  2. src: Indicamos la ruta (path) en la que se encuentra nuestra fuente y su formato. En este atributo podemos indicar mucha información respecto a la fuente.  Veremos más adelante esta información.

El resto de propiedades son opcionales. Veamoslas:

  1. font-style: Definimos el estilo de una familia de fuentes. El valor por defecto será normal. Puedes ampliar esta explicación en font-style de MDN web docs.
  2. font-weight: Definimos el peso o grueso de una familia de fuentes. El valor por defecto será normal. Puedes ampliar esta explicación en font-weight de MDN web docs.
  3. unicode-range: Definimos el rango de caracteres que la familia de fuentes usará en la web. Si en el documento no usara ningún carácter del rango, la fuente no será descargada. En caso de usar un solo carácter, la fuente será descargada para su uso. Puedes ampliar la explicación en unicode-range de MDN web docs.
  4. font-display: Definimos el comportamiento de la fuente durante la carga. El valor por defecto es auto. Puedes ampliar esta explicación en font-display de MDN web docs. Más abajo ampliaré información sobre la importancia de este atributo.

Atributo src

En esta propiedad vamos a indicar bastante información. Debes saber que este descriptor no tiene porqué ser único. Puedes definir más de uno en la declaración de @font-face. Puedes ampliar información sobre este atributo en src de font-face en MDN web docs.

En el ejemplo anterior tenemos una lista de valores separados por comas. Estos formarán los valores para el descriptor. Veamoslos en detalle:

local

Indicamos al cliente (navegador) que busque si existe la fuente en el sistema. Podemos indicar varios local separados por comas, para indicar diferentes nombres de la misma fuente. Así, es bastante usual encontrase algo como lo siguiente:

src: local('Lato Regular'), local('Lato-Regular'), ...​

url y format

Indicamos la ruta donde está alojada la fuente y su formato. El fichero de la fuente podemos tenerlo alojado en nuestro servidor. También podemos usar un servicio de terceros y usar la URL al fichero de la fuente. Por ejemplo:

<!-- en tu servidor -->
src: local('Lato Regular'), local('Lato-Regular'), url(../fonts/lato.woff2) format('woff2');
<!-- desde google fonts -->
src: local('Lato Regular'), local('Lato-Regular'), url(https://fonts.gstatic.com/s/lato/v13/1YwB1sO8YE1Lyjf12WNiUA.woff2) format('woff2');​

Será muy habitual encontrar en esta propiedad una lista separada por comas indicando diferentes ficheros y formatos. Esto es debido a que cada navegador usaba un formato diferente de la fuente. Por suerte, ultimamente parece que se ha estandarizado el formato woff. Aún así es bastante normal encontrar este tipo de declaraciones:

src: local('Lato Regular'),
         url('lato-regular.eot?#iefix') format('embedded-opentype'),
         url('lato-regular.woff') format('woff'),
         url('lato-regular.ttf') format('truetype'),
         url('lato-regular.woff') format('woff'),
         url('lato-regular.woff2') format('woff2');

Sabiendo todo esto, veamos como definir una familia de fuentes.

Definir una familia de fuentes

Teniendo la información anterior veamos un ejemplo de como definir la familia de una fuente. Entendamos la familia de una fuente los diferentes ficheros que darán los diferentes estilos a la fuente. Veamos como realizar la carga de la fuente Lato en diferentes versiones, es decir: regular, itálica, negrita.

<!-- Definir familia de fuentes Lato -->
<!-- Regular -->
@font-face {
    font-family: 'Lato';
    font-display: fallback;
    font-style: normal;
    font-weight: 400;
    src: local('Lato Regular'), local('Lato-Regular'), url(../fonts/lato-regular.woff2) format('woff2');
}
<!-- Bold (negrita) -->
@font-face {
    font-family: 'Lato';
    font-display: fallback;
    font-style: normal;
    font-weight: bold;
    src: local('Lato Bold'), local('Lato-Bold'), url(../fonts/lato-bold.woff2) format('woff2');
}
<!-- Italic (cursiva) -->
@font-face {
    font-family: 'Lato';
    font-display: fallback;
    font-style: italic;
    font-weight: 400;
    src: local('Lato Italic'), local('Lato-Italic'), url(../fonts/lato-italic.woff2) format('woff2');
}

Podemos comprobar que el font-family es igual para los tres. Si analizamos el código anterior, lo que cambia respecto a una u otra son los siguientes atributos: font-style, font-weight y src.

Con esto y definiendo en nuestra etiqueta body la fuente que queremos usar de la siguiente manera:

body {
    font-family: 'Lato', Helvetica, Verdana, sans-serif;
}

los diferentes elementos HTML, usarán la fuente Lato. ¿Qué quiero decir con esto? Los elementos que por defecto se muestran en negrita se mostrarán con la fuente Lato Bold y los elementos que por defecto se muestran en itálica se mostrarán con la fuente Lato Italic. ¿Qué elementos son? Por ejemplo las etiquetas de encabezados hx, b y strong y las etiqueta em como negrita e itálica respectivamente. ¿Por qué comento esto? Me encuentro, y yo mismo he usado por desconocimiento, la siguiente forma de definir una familia de fuentes:

<!-- Definir familia de fuentes Lato -->
<!-- Regular -->
@font-face {
    font-family: 'Lato';
    font-display: fallback;
    font-style: normal;
    font-weight: 400;
    src: local('Lato Regular'), local('Lato-Regular'), url(../fonts/lato-regular.woff2) format('woff2');
}
<!-- Bold (negrita) -->
@font-face {
    font-family: 'Lato Bold';
    font-display: fallback;
    font-style: normal;
    font-weight: 400;
    src: local('Lato Bold'), local('Lato-Bold'), url(../fonts/lato-bold.woff2) format('woff2');
}
<!-- Italic (cursiva) -->
@font-face {
    font-family: 'Lato Italic';
    font-display: fallback;
    font-style: normal;
    font-weight: 400;
    src: local('Lato Italic'), local('Lato-Italic'), url(../fonts/lato-italic.woff2) format('woff2');
}

Esto me obligaba a redifinir los elementos genéricos HTML de esta forma:

body {
    font-family: 'Lato', Helvetica, Verdana, sans-serif;
}

h1, h2, h3, h4, h5, h6, strong {
    font-family: 'Lato Bold';
    font-weight: normal;
}

em {
    font-family: 'Lato Italic';
    font-style: normal;
}

.outstanding {
    font-family: 'Lato Bold';
}

Los inconveniente de definir una familia de fuentes de esta manera son:

  1. Al resetear estilos para que la fuente se muestre como deseamos, si por cualquier motivo la fuente no fuera cargada por el navegador, perderemos la visualización de los distintos elementos en sus diferentes estilos: ni las negritas ni las cursivas se verían como tal.
  2. Al usar el descriptor font-family más a menudo, añadimos complejidad al mantenimiento de la hoja de estilos. La simple decisión de cambiar de familia será más compleja de ejecutar.

Cómo cargar más rápido desde Google Fonts

Vimos un ejemplo anterior en el  que podemos cargar fuentes desde un recurso externo. Esto quiere decir que podríamos llamar a una URL externa a nuestro dominio. Siguiendo con el ejemplo anterior de la carga de la familia Lato en sus tres variantes: regular, itálica y negrita, veamos como aprovechar Google Fonts para la carga de una familia.

También haré uso de la priorización de la carga de recursos con dns-prefetch y prefetch. Recomiendo leer antes este artículo: Prioridades de recursos: cómo hacer que el navegador te ayude antes de continuar.

¿Ya lo has leído? Continuemos entonces.

Obtener la familia de Google Fonts

Google Fonts nos ofrece de forma sencilla este enlace para incorporarlo al head de nuestro HTML:

<head>
<link href="https://fonts.googleapis.com/css?family=Lato:400,400i,700&display=swap" rel="stylesheet"> 
</head>

Si curioseamos el enlace que nos indica: https://fonts.googleapis.com/css?family=Lato:400,400i,700&display=swap veremos esto:

/* latin-ext */
@font-face {
  font-family: 'Lato';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: local('Lato Italic'), local('Lato-Italic'), url(https://fonts.gstatic.com/s/lato/v16/S6u8w4BMUTPHjxsAUi-qJCY.woff2) format('woff2');
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Lato';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: local('Lato Italic'), local('Lato-Italic'), url(https://fonts.gstatic.com/s/lato/v16/S6u8w4BMUTPHjxsAXC-q.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Lato';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: local('Lato Regular'), local('Lato-Regular'), url(https://fonts.gstatic.com/s/lato/v16/S6uyw4BMUTPHjxAwXjeu.woff2) format('woff2');
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Lato';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: local('Lato Regular'), local('Lato-Regular'), url(https://fonts.gstatic.com/s/lato/v16/S6uyw4BMUTPHjx4wXg.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Lato';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: local('Lato Bold'), local('Lato-Bold'), url(https://fonts.gstatic.com/s/lato/v16/S6u9w4BMUTPHh6UVSwaPGR_p.woff2) format('woff2');
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Lato';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: local('Lato Bold'), local('Lato-Bold'), url(https://fonts.gstatic.com/s/lato/v16/S6u9w4BMUTPHh6UVSwiPGQ.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Nos ofrece dos versiones para cada estilo. En la mayoría de los casos, con tener una versión será suficiente. Así que de lo anterior me voy a quedar con la versión latin-ext de cada estilo:

/* latin-ext */
@font-face {
  font-family: 'Lato';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: local('Lato Italic'), local('Lato-Italic'), url(https://fonts.gstatic.com/s/lato/v16/S6u8w4BMUTPHjxsAUi-qJCY.woff2) format('woff2');
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin-ext */
@font-face {
  font-family: 'Lato';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: local('Lato Regular'), local('Lato-Regular'), url(https://fonts.gstatic.com/s/lato/v16/S6uyw4BMUTPHjxAwXjeu.woff2) format('woff2');
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin-ext */
@font-face {
  font-family: 'Lato';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: local('Lato Bold'), local('Lato-Bold'), url(https://fonts.gstatic.com/s/lato/v16/S6u9w4BMUTPHh6UVSwaPGR_p.woff2) format('woff2');
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

En lugar de incluir el link que nos proporciona Google Fonts en nuestro head, voy a incorporar a mi CSS la definición de la familia Lato tal y como véis encima de estas líneas.

Me gustaría añadir la ventaja de incluir la fuente desde un recurso externo. El usarlo, además de tener menos descargas desde nuestro servidor (ahorro de dinero) nos ofrece que en caso de que el usuario haya visitado una página anterior que use el mismo recurso externo, este no será descargado de nuevo en su ordenador, si no que usará el ya descargado con anterioridad. Además, en el caso concreto de Google Fonts, será como usar un CDN con los recursos usados.

Evidentemente tiene sus inconvenientes. Si el servicio no está disponible (algo poco probable, pero posible), la web se mostrará con una fuente secundaria o por defecto.

Acelerar la resolución de dominio con dns-prefetch

Si has leído el artículo que recomendé más arriba, seguramente ya sabrás que hace. En todo caso esto consiste en indicar al cliente (navegador) que vamos a conectar con un dominio diferente. Al estar preparado, este trabajará más rápido.

Veamos como indicarlo con un ejemplo:

<head>
  <link rel="dns-prefetch" href="//fonts.googleapis.com" />
</head>

Esta declaración que hacemos en el head de nuestro HTML deberíamos hacerla antes de llamar a cualquier recurso del dominio que hemos indicado. En nuestro ejemplo realizamos la llamada a los recursos en nuestro CSS. Esta declaración debe ir antes de llamar a la hoja de estilos.

Quizá sea obvio, pero se pueden implementar tantos dns-prefetch como llamadas a recursos externos tengamos.

Precarga de las fuentes

Poco que añadir a lo comentado en el artículo recomendado. En él, explican en Caso de Uso: Fuentes como realizar una precarga de estas. Veamos como hacerlo en nuestro caso particular:

<head>
  <!-- dns-prefetch -->
  <link rel="dns-prefetch" href="//fonts.googleapis.com" />

  <!-- preload -->
  <link rel="preload" href="//fonts.gstatic.com/s/lato/v16/S6u8w4BMUTPHjxsAUi-qJCY.woff2" as="font" crossorigin="crossorigin" type="font/woff2">
  <link rel="preload" href="//fonts.gstatic.com/s/lato/v16/S6uyw4BMUTPHjxAwXjeu.woff2" as="font" crossorigin="crossorigin" type="font/woff2">
  <link rel="preload" href="//fonts.gstatic.com/s/lato/v16/S6u9w4BMUTPHh6UVSwaPGR_p.woff2" as="font" crossorigin="crossorigin" type="font/woff2">

  <!-- load css -->
  <link rel="stylesheet" type="text/css" href="style.css" media="screen" />
</head>

Se debe tener en cuenta la importancia del atributo crossorigin en este caso. Tal como indican en el artículo y que cito aquí:

Ten en cuenta que el uso de crossorigin aquí es importante. Sin este atributo, el navegador ignora la fuente precargada y se realiza una nueva operación fetch. Esto es porque se espera que el navegador obtenga las fuentes anónimamente y la solicitud de precarga se hace anónima solo mediante el atributo crossorigin

También es posible, en lugar de usar preload, usar prefetch. En este caso indicamos, o más bien, dejamos decidir al navegador la importancia en la resolución del recurso. Diferente a preload, el cual indica de forma explícita la importancia de carga.

<head>
  <!-- dns-prefetch -->
  <link rel="dns-prefetch" href="//fonts.googleapis.com" />

  <!-- preload -->
  <link rel="prefetch" href="//fonts.gstatic.com/s/lato/v16/S6u8w4BMUTPHjxsAUi-qJCY.woff2" as="font" crossorigin="crossorigin" type="font/woff2">
  <link rel="prefetch" href="//fonts.gstatic.com/s/lato/v16/S6uyw4BMUTPHjxAwXjeu.woff2" as="font" crossorigin="crossorigin" type="font/woff2">
  <link rel="prefetch" href="//fonts.gstatic.com/s/lato/v16/S6u9w4BMUTPHh6UVSwaPGR_p.woff2" as="font" crossorigin="crossorigin" type="font/woff2">

  <!-- load css -->
  <link rel="stylesheet" type="text/css" href="style.css" media="screen" />
</head>

Actualmente, y con los Core Web Vitalas en la mano, deberías estar usando alguna de estas estrategías para la priorización de carga. Al menos, en algunos experimentos realizados, PageSpeed Insights suele recomendarte usar preload por defecto, en caso de no estar usándolo. Hasta el día de hoy no tengo experimentos documentados con prefetch.

Control del comportamiento con font-display

Si recordamos la imagen de más arriba, en la que Pagespeed Insights recomienda que revisemos la estrategia de carga de fuentes, nos ofrece un link para documentarnos al respecto: Controlling Font Performance with font-display. En él explica en qué consiste esta propiedad. Si prefieres leerlo en español puedes ver font-display de MDN web doc.

Veamos, la teoría que nos ofrecen es que en el proceso de intentar cargar una fuente, este se divide en tres fases:

  1. Momento de bloqueo: En el cual, si la fuente a usar no está cargada, se usa una alternativa.
  2. Cambio de fuente: En el cual, si la fuente a usar no está cargada, se usa una alternativa.
  3. Fallo de carga: En el cual, si la fuente a usar falla, se usa una alternativa.

Con font-display podemos influir en el comportamiento indicando el valor que deseamos. Los valores son:

  1. auto: Depende del navegador y es el valor por defecto.
  2. block: Establece un tiempo de bloqueo de la fuente corto y un periodo de intercambio infinito.
  3. swap: No establece tiempo de bloqueo para la fuente y un tiempo infinito de intercambio.
  4. fallback: Establece un tiempo de bloqueo muy pequeño y un período de intercambio corto.
  5. optional: Establece un tiempo de bloqueo muy corto y sin tiempo de intercambio.

¿Qué significa todo esto y cuál usar? Depende de si para tu web es imprescindible cargar la fuente o es un molaría pero no es imprescindible.

En el primero de los casos, en el que es imprescindible, deberías usar el valor block, que es el que usan la mayoría de navegadores con su valor a auto, o swap. En el caso de swap cargará la fuente tan rápido la fuente esté cargada. Estos casos parecen ser recomendados si usas la fuente para la marca de la página o elementos imprescindibles.

En el segundo de los casos, molaría pero no es imprescindible, deberías usar el valor fallback u optional. Los tiempo de bloque son de unos cien milisegundos, mientras mostrará la alternativa de la que disponga el navegador.

Google Fonts actualmente nos ofrece, por defecto, las fuentes con la declaración font-display: swap. Además, debemos tener en cuenta, si nos importa mucho la performance y lo que diga Pagespeed Insights al respecto, que la advertencia de la imagen inicial se produce cuando, al cargar la web, la fuente no se visualiza durante un determinado periodo de tiempo o es invisible. Podemos aprender más sobre estos efectos en el artículo: FOUT, FOIT, FOFT.

Cuándo cargar las fuentes para no bloquear el dibujado de la página

Como hemos ido viendo a lo largo del artículo, casi todo consiste en estrategias de carga de fuentes. Al igual que otros recursos se pueden cargar de forma asíncrona, con las fuentes o quizá mejor con un fichero donde solo tengamos las fuentes podríamos hacer lo mismo mediante el uso de JavaScript. Aunque quizá, y si para nuestra web no es tan imprescindible la carga de fuentes en un primer momento podríamos retrasar la carga al igual que historicamente se ha hecho con recursos JavaScript. Añadiendo el fichero CSS correspondiente, el que contiene la definición de las fuentes, al final de la página justo antes del cierre del body.

Veamos como:

<html>
<head>
  <!-- critical css -->
  <link rel="stylesheet" type="text/css" href="style.css" media="screen" />
<head>
<body>
  <!-- header, main and footer -->

  <!-- load no critical css -->
  <link rel="stylesheet" type="text/css" href="style-no-critical.css" media="screen" property="stylesheet"/>

  <!-- load js -->
  <script type="text/javascript" src="app.js" async></script>
</body>
</html>

He añadido la propiedad property con el valor stylesheet para que los validadores de HTML entiendan que se refiere a una hoja de estilos.

Esta estrategia, además de para familias de fuentes, la he usado para imágenes de fondo que debían ser de gran calidad (con su correspondiente tamaño) evitando bloquear la página al comienzo. En este caso es recomendable incluir el link al CSS antes que la carga de cualquier fichero o script JavaScript.

Cachear las fuentes desde el servidor

Por último, no debemos olvidar cachear las fuentes desde nuestro servidor. En el caso de Apache 2.x desde el fichero htacces que nos permita configurar (dependerá de tu proveedor de hosting) deberíamos incluir las siguientes líneas (entre otras):

<ifmodule mod_deflate.c>
  #Checks if your server supports Addtype
  <ifmodule mod_mime.c>
    Addtype font/opentype .otf
    Addtype font/eot .eot
    Addtype font/truetype .ttf
    Addtype font/woff .woff
  </ifmodule>
  AddOutputFilterByType DEFLATE font/opentype font/truetype font/eot
</ifmodule>

# Expires Headers - 2678400s = 31 days
<ifmodule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access plus 1 year"

  # Webfonts
  ExpiresByType font/truetype "access plus 1 year"
  ExpiresByType font/opentype "access plus 1 year"
  ExpiresByType font/woff "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"
  ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
</ifmodule>

Un tiempo largo es el recomendado para ofrecer una buena experiencia según Pagespeed Insights para los recursos que no suelen cambiar como es el caso de las fuentes.

La mejor estrategia para las familias de fuentes...

Obviamente es evitarlas o mantener el número más bajo posible de estas. Si por especificaciones del proyecto debes incluir demasiadas fuentes, esto podrá ayudar, pero con un éxito limitado.

Te dejo un dato. Por los experimentos que he realizado, por cada fichero para la familia de fuentes, el tiempo de carga suele incrementarse unos 100 milisegundos de media. En apareciencia es muy poco tiempo, pero una familia de fuentes suele contar con 4 o 5 ficheros. Al sumar estamos incrementando el tiempo en 400 o 500 milisegundos.

Quizá, algo que merezca la pena probar (o volver a utilizar) es el uso de fuentes del sistema. Estas han mejorado mucho. Todo depende del contexto del proyecto para el que estes trabajando.

Recursos