Por qué tus imágenes no se ven bien al compartir en redes (si usas AVIF en WordPress)

Compartía un enlace y la imagen salía rota. El culpable: AVIF

Me pasó lo típico: compartes un enlace de tu propio blog en redes y la imagen de vista previa no sale, o sale rota, o directamente no aparece nada. Y claro, empiezas a sospechar de todo menos de lo obvio.

En mi caso, la causa raíz era Modern Image Formats (el plugin webp-uploads, del equipo de Performance de WordPress, que suele venir de la mano de Performance Lab). Este plugin convierte tus subidas a formatos modernos automáticamente, y en mi sitio la salida es AVIF. El problema es que wp_get_attachment_url() ya te devuelve directamente la URL en .avif, y Rank Math usa esa misma URL para el og:image. Resultado: muchas redes sociales no saben qué hacer con un AVIF a la hora de generar la vista previa, y la imagen simplemente no sale.

Un ejemplo real de mi propio sitio para que se entienda mejor:

  • URL del adjunto que sale en el Open Graph: …/cursor-window-restauration-dev-adammartin.avif
  • El original en disco, guardado en los metadatos como original_image: cursor-window-restauration-dev-adammartin.png

O sea, el PNG bueno está ahí, en el propio servidor, pero nadie le está diciendo a las meta de compartir que lo use a él en vez del AVIF.

El primer snippet que me dieron no funcionaba (y por dos motivos distintos)

Como suele pasar, antes de llegar a la causa real probé con un snippet que me pasaron, algo tipo esto:

<?php

add_filter('wpseo_opengraph_image', 'forzar_jpg_en_opengraph'); // Si usas Yoast
add_filter('rank_math/frontend/og_image', 'forzar_jpg_en_opengraph'); // Si usas Rank Math

function forzar_jpg_en_opengraph($url) {
    return preg_replace('/\.([a-zA-Z0-9]+)\.(webp|avif)$/i', '.$1', $url);
}

Y no hacía nada. Cero. Al investigar por qué, me encontré con dos fallos:

  1. El filtro de Rank Math estaba mal: rank_math/frontend/og_image no existe en el código del plugin. El hook real lleva el prefijo por red social: rank_math/opengraph/{network}/image, es decir, rank_math/opengraph/facebook/image y rank_math/opengraph/twitter/image. Si te enganchas a un hook que no existe, tu función nunca se llega a ejecutar, por mucho que la lógica de dentro esté perfecta.
  2. La regex era incompleta: preg_replace('/\.([a-zA-Z0-9]+)\.(webp|avif)$/i', ...) solo cubre el patrón foto.png.avif (con doble extensión). Pero Modern Image Formats, cuando el fichero principal que sirve es el AVIF, genera el patrón foto.avif a secas, sin la extensión intermedia. Esa regex nunca iba a convertir mi caso real, porque el patrón que buscaba no era el que se estaba generando.

Ojo con esto si os pasa algo parecido: que un snippet «tenga sentido» a simple vista no significa que funcione con vuestro caso concreto. Hay que comprobar el hook exacto del plugin que uséis y el patrón real que generan vuestros ficheros, no el que uno se imagina que debería ser.

Cómo lo resolví: un plugin propio de un solo fichero

No hacía falta desactivar Modern Image Formats ni renunciar a servir AVIF en el front, con lo bien que va para el rendimiento. El único sitio donde el AVIF me estaba jugando una mala pasada era en las meta de compartir. Así que la solución fue un plugin pequeño que solo toca eso.

Se engancha a los hooks correctos de Rank Math (que es lo que uso yo) y también a los de Yoast, por si algún día migro:

// Rank Math.
add_filter( 'rank_math/opengraph/facebook/image', 'am_aic_forzar_jpg_en_opengraph' );
add_filter( 'rank_math/opengraph/twitter/image', 'am_aic_forzar_jpg_en_opengraph' );

// Yoast SEO.
add_filter( 'wpseo_opengraph_image', 'am_aic_forzar_jpg_en_opengraph' );
add_filter( 'wpseo_twitter_image', 'am_aic_forzar_jpg_en_opengraph' );

Y la lógica de conversión va probando varias vías, de la más fiable a la más rebuscada, hasta encontrar una URL clásica que exista de verdad en disco:

  1. Si la URL no termina en .webp o .avif, se devuelve tal cual, no hay nada que tocar.
  2. Se intenta resolver el adjunto con attachment_url_to_postid() y, si tiene metadatos con original_image, se reconstruye la URL apuntando a ese fichero original. Este es justo mi caso real, el del PNG que os comentaba antes.
  3. Si eso falla, se prueba la regex del snippet original, para el patrón de doble extensión (foto.jpg.webp).
  4. Si tampoco, se prueba el patrón que genera Performance Lab con guion (foto-jpeg.webpfoto.jpeg).
  5. Y como último recurso, se prueban directamente en disco las extensiones clásicas (.jpg, .jpeg, .png, .gif) sustituyendo la moderna, por si ninguno de los patrones anteriores encaja pero el fichero existe igualmente.

Así, si falla el método más «elegante», siempre queda el método bruto de mirar qué hay realmente en la carpeta de uploads antes de rendirse y devolver el AVIF sin más.

Activar el plugin y qué comprobar después

Comprobé en una entrada reciente que el og:image apuntaba ya a un .png o .jpg, no al .avif.

Una cosa importante que se me olvidó la primera vez: Facebook y LinkedIn cachean las vistas previas. Si ya habías compartido el enlace antes con la imagen rota, tienes que forzar el refresco en sus depuradores de compartido (el Sharing Debugger de Facebook o el Post Inspector de LinkedIn), porque si no, seguirás viendo la imagen vieja aunque el plugin ya esté funcionando perfectamente por detrás.

Resumiendo

  • Si usas Modern Image Formats (o cualquier plugin que sirva AVIF/WebP) y tus imágenes de compartir fallan, el sospechoso número uno es que el og:image esté apuntando directamente al fichero moderno.
  • Antes de copiar un snippet de internet a ciegas, comprueba el hook exacto de tu plugin SEO y el patrón real de nombres de fichero que genera tu conversor de imágenes. Los dos me fallaban en el snippet que probé primero.
  • No hace falta renunciar a servir AVIF en el resto del sitio, solo hay que interceptar las meta de compartir.
  • Y si cambiáis algo de esto después de haber compartido ya el enlace alguna vez, no os olvidéis de refrescar la caché en los depuradores de Facebook y LinkedIn, que si no, la imagen rota os va a perseguir un rato más.