Comparte un enlace de tu web en WhatsApp, Twitter o LinkedIn, ¿qué ves? Si no tienes etiquetas Open Graph configuradas probablemente un título genérico, ninguna descripción y ninguna imagen. O peor, una imagen que no tiene nada que ver con el contenido.
Las redes sociales, apps de mensajería y plataformas como Slack o Discord leen las etiquetas Open Graph (OG) del HTML de tu página para montar esa vista previa. Si no las encuentran, improvisan con lo primero que pillan, y el resultado suele ser bastante feo.
La solución típica es instalar un plugin SEO (Yoast, Rank Math, SEOPress, etc.) que entre otras cosas genera esas etiquetas, pero si ya no necesitas un plugin SEO completo porque tienes cubierto lo esencial con herramientas más ligeras, ¿para qué cargar con todo ese peso?
Aquí te dejo un plugin que genera las tags Open Graph (OG) y Twitter Card de forma automática, sin configuración, sin ajustes, sin base de datos extra. Lo sueltas en tu carpeta de mu-plugins y funciona solo.
Qué genera este plugin
Para cada página de tu web genera automáticamente las siguientes meta etiquetas en el <head>:
Open Graph (Facebook, LinkedIn, WhatsApp, Telegram, Slack, Discord…):
og:title– título de la página.og:description– descripción a partir del extracto o el contenido.og:image– imagen destacada, imagen de galería (WooCommerce), primera imagen del contenido, o imagen de la descripción del término en archivos de taxonomía.og:url– URL canónica.og:type–articlepara entradas,profilepara archivos de autor,websitepara todo lo demás.og:site_name– nombre del sitio.og:image:widthyog:image:height– dimensiones de la imagen cuando están disponibles.
Twitter Card (ahora X, pero las etiquetas siguen siendo las mismas):
twitter:card–summary_large_imagesi hay imagen,summarysi no la hay.twitter:title,twitter:description,twitter:image– mismos valores que las OG.
De dónde saca los datos
El mu-plugin no tiene ajustes ni campos personalizados, tira de lo que WordPress ya tiene para cada tipo de página.
Entradas y páginas
- Título: el título del post.
- Descripción: el extracto si lo has rellenado, y si no, las primeras 25 palabras del contenido (limpio de HTML y shortcodes).
- Imagen: imagen destacada. Si no hay, toma la primera imagen de la galería de WooCommerce (solo en productos). Si tampoco la tiene o no es un producto, pilla la primera imagen
<img>del contenido. Si no hay ninguna, no poneog:image.
Página de inicio
- Título: nombre del sitio (desde Ajustes > Generales).
- Descripción: descripción corta del sitio (desde Ajustes > Generales).
- Imagen: ninguna. Evita que salga un banner de publicidad o algo que no tenga que ver.
Archivos de categoría, etiqueta y taxonomía
- Título: nombre de la categoría, etiqueta o término.
- Descripción: descripción del término (la que rellenas en la edición de la categoría).
- Imagen: primero busca una imagen dentro del HTML de la descripción del término (si usas un editor enriquecido en categorías y has metido una imagen ahí, la pilla). Si no encuentra ninguna, usa la imagen destacada del primer post del archivo que tenga una.
Archivos de autor
- Título: nombre para mostrar del autor.
- Descripción: biografía del perfil de usuario.
- Imagen: Gravatar del autor si tiene uno personalizado (no el muñeco por defecto). Si no tiene Gravatar, la imagen destacada del primer post del autor. El resultado de la comprobación del Gravatar se cachea 24 horas para no hacer peticiones repetidas.
Archivos de tipo de contenido personalizado
- Título: título del archivo del CPT.
- Imagen: imagen destacada del primer post del archivo.
Productos de WooCommerce
Los productos funcionan como cualquier entrada, con un extra, y es que si un producto no tiene imagen destacada pero sí tiene imágenes en la galería del producto, el mu-plugin usa la primera imagen de esa galería como te comenté antes.
Si no tienes WooCommerce instalado, esta comprobación ni se ejecuta.
Página de resultados de búsqueda
Estas páginas normalmente ni se suelen tener indexadas, y no deberían, así que como mucho vamos con lo mínimo.
- Título: «Search results for» seguido del término buscado.
- Imagen: ninguna.
- Descripción: ninguna.
Compatibilidad con plugins SEO
Si tienes instalado alguno de estos plugins, el mu-plugin no añade nada, para no duplicar etiquetas:
- Yoast SEO.
- Rank Math.
- SEOPress.
- All in One SEO (AIOSEO).
- The SEO Framework.
- Slim SEO.
- Jetpack (cuando tiene activadas las etiquetas OG).
Cómo instalarlo
- Descarga el archivo
ayudawp-og-tags.php(lo tienes más abajo). - Súbelo a la carpeta
wp-content/mu-plugins/de tu instalación de WordPress. Si la carpetamu-pluginsno existe, créala. - No hay paso 3, los mu-plugins se activan solos, no necesitas activar nada desde el escritorio de WordPress.
Si algún día quieres desactivarlo, borra el archivo de la carpeta y ya está.
El código
<?php
/**
* Generar Open Graph Tags Automáticamente
*
* Genera meta tags de Open Graph y Twitter Card automáticamente
* a partir del título del post, el extracto y la imagen destacada. No se necesitan configuraciones.
*
* Poner este archivo en wp-content/mu-plugins/
*
* @package AyudaWP
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_action( 'wp_head', 'ayudawp_og_tags', 1 );
/**
* Producir meta tags Open Graph y Twitter
*/
function ayudawp_og_tags() {
// Skip if a known SEO plugin is active.
if (
defined( 'WPSEO_VERSION' ) || // Yoast SEO.
defined( 'RANK_MATH_VERSION' ) || // Rank Math.
defined( 'SEOPRESS_VERSION' ) || // SEOPress.
defined( 'AIOSEO_VERSION' ) || // All in One SEO.
defined( 'THE_SEO_FRAMEWORK_VERSION' ) // The SEO Framework.
) {
return;
}
// Slim SEO utiliza una clase en lugar de una constante de versión
if ( class_exists( 'SlimSEO\\Plugin', false ) ) {
return;
}
if ( has_action( 'wp_head', 'jetpack_og_tags' ) ) {
return;
}
$title = '';
$description = '';
$url = '';
$type = 'website';
$image = '';
$image_w = '';
$image_h = '';
$site_name = get_bloginfo( 'name' );
if ( is_singular() ) {
$post = get_queried_object();
if ( ! $post instanceof WP_Post ) {
return;
}
$title = get_the_title( $post );
$url = get_permalink( $post );
$type = ( 'post' === $post->post_type ) ? 'article' : 'website';
// Description: usa el extracto si está disponible, de lo contrario usa el contenido recortado
if ( has_excerpt( $post ) ) {
$description = get_the_excerpt( $post );
} else {
$content = strip_shortcodes( $post->post_content );
$description = wp_trim_words( wp_strip_all_tags( $content ), 25, '...' );
}
// Primero la imagen destacada, luego la galería de WooCommerce, luego la primera imagen en el contenido
$image_data = ayudawp_og_get_post_image( $post->ID );
if ( ! $image_data && function_exists( 'wc_get_product' ) && 'product' === $post->post_type ) {
$product = wc_get_product( $post->ID );
if ( $product ) {
$gallery_ids = $product->get_gallery_image_ids();
if ( ! empty( $gallery_ids ) ) {
$img_data = wp_get_attachment_image_src( $gallery_ids[0], 'large' );
if ( $img_data ) {
$image_data = array(
'url' => $img_data[0],
'width' => $img_data[1],
'height' => $img_data[2],
);
}
}
}
}
if ( ! $image_data ) {
$image_data = ayudawp_og_get_image_from_html( $post->post_content );
}
if ( $image_data ) {
$image = $image_data['url'];
$image_w = $image_data['width'];
$image_h = $image_data['height'];
}
} elseif ( is_front_page() || is_home() ) {
$title = get_bloginfo( 'name' );
$description = get_bloginfo( 'description' );
$url = home_url( '/' );
// Si no hay imagen en la página de inicio — podría extraer anuncios o contenido no relacionado
} elseif ( is_category() || is_tag() || is_tax() ) {
$term = get_queried_object();
if ( $term instanceof WP_Term ) {
$title = single_term_title( '', false );
$url = get_term_link( $term );
// Obtiene la descripción sin procesar (puede contener HTML con imágenes).
$raw_description = term_description( $term );
$description = wp_strip_all_tags( $raw_description );
// Intenta obtener la imagen desde el HTML de la descripción, luego desde el primer post.
$image_data = ayudawp_og_get_image_from_html( $raw_description );
if ( ! $image_data ) {
$image_data = ayudawp_og_get_archive_image();
}
if ( $image_data ) {
$image = $image_data['url'];
$image_w = $image_data['width'];
$image_h = $image_data['height'];
}
}
} elseif ( is_author() ) {
$author = get_queried_object();
if ( $author instanceof WP_User ) {
$title = $author->display_name;
$description = get_the_author_meta( 'description', $author->ID );
$url = get_author_posts_url( $author->ID );
$type = 'profile';
// Primero la imagen de Gravatar, luego la imagen del primer post.
$image_data = ayudawp_og_get_gravatar_image( $author->ID );
if ( ! $image_data ) {
$image_data = ayudawp_og_get_archive_image();
}
if ( $image_data ) {
$image = $image_data['url'];
$image_w = $image_data['width'];
$image_h = $image_data['height'];
}
}
} elseif ( is_post_type_archive() ) {
$post_type_obj = get_queried_object();
$title = post_type_archive_title( '', false );
$url = get_post_type_archive_link( $post_type_obj->name );
$image_data = ayudawp_og_get_archive_image();
if ( $image_data ) {
$image = $image_data['url'];
$image_w = $image_data['width'];
$image_h = $image_data['height'];
}
} elseif ( is_search() ) {
$title = sprintf(
/* translators: %s: search query */
__( 'Search results for "%s"', 'ayudawp' ),
get_search_query()
);
$url = get_search_link();
} else {
return;
}
if ( empty( $title ) ) {
return;
}
// Limpieza de la descripción: línea única, máximo 200 caracteres.
$description = wp_strip_all_tags( $description );
$description = preg_replace( '/\s+/', ' ', $description );
$description = mb_substr( trim( $description ), 0, 200 );
$twitter_card = $image ? 'summary_large_image' : 'summary';
// --- Salida ---
echo "\n<!-- AyudaWP Open Graph Tags -->\n";
printf( '<meta property="og:site_name" content="%s" />' . "\n", esc_attr( $site_name ) );
printf( '<meta property="og:title" content="%s" />' . "\n", esc_attr( $title ) );
printf( '<meta property="og:type" content="%s" />' . "\n", esc_attr( $type ) );
printf( '<meta property="og:url" content="%s" />' . "\n", esc_url( $url ) );
if ( $description ) {
printf( '<meta property="og:description" content="%s" />' . "\n", esc_attr( $description ) );
}
if ( $image ) {
printf( '<meta property="og:image" content="%s" />' . "\n", esc_url( $image ) );
if ( $image_w && $image_h ) {
printf( '<meta property="og:image:width" content="%d" />' . "\n", absint( $image_w ) );
printf( '<meta property="og:image:height" content="%d" />' . "\n", absint( $image_h ) );
}
}
printf( '<meta name="twitter:card" content="%s" />' . "\n", esc_attr( $twitter_card ) );
printf( '<meta name="twitter:title" content="%s" />' . "\n", esc_attr( $title ) );
if ( $description ) {
printf( '<meta name="twitter:description" content="%s" />' . "\n", esc_attr( $description ) );
}
if ( $image ) {
printf( '<meta name="twitter:image" content="%s" />' . "\n", esc_url( $image ) );
}
echo "<!-- /AyudaWP Open Graph Tags -->\n\n";
}
/**
* Obtener datos de la imagen destacada para un post.
*
* @param int $post_id ID del post.
* @return array|false Array con la url, ancho, altura o false.
*/
function ayudawp_og_get_post_image( $post_id ) {
if ( ! has_post_thumbnail( $post_id ) ) {
return false;
}
$img_id = get_post_thumbnail_id( $post_id );
$img_data = wp_get_attachment_image_src( $img_id, 'large' );
if ( ! $img_data ) {
return false;
}
return array(
'url' => $img_data[0],
'width' => $img_data[1],
'height' => $img_data[2],
);
}
/**
* Obtener la imagen destacada del primer post en el archivo actual.
*
* Recorre los posts en la consulta hasta encontrar uno con imagen destacada.
* No se usa en la página de inicio/primera página para evitar extraer imágenes no relacionadas.
*
* @return array|false Array con url, ancho, altura o false.
*/
function ayudawp_og_get_archive_image() {
global $wp_query;
if ( empty( $wp_query->posts ) ) {
return false;
}
foreach ( $wp_query->posts as $archive_post ) {
$image_data = ayudawp_og_get_post_image( $archive_post->ID );
if ( $image_data ) {
return $image_data;
}
}
return false;
}
/**
* Extraer la primera URL de imagen de una cadena HTML.
*
* Útil para obtener imágenes de descripciones de términos que contienen
* etiquetas img agregadas mediante el editor TinyMCE.
*
* @param string $html Contenido HTML para buscar.
* @return array|false Array con url, ancho, altura o false.
*/
function ayudawp_og_get_image_from_html( $html ) {
if ( empty( $html ) ) {
return false;
}
// Busca la primera etiqueta <img> con un atributo src.
if ( ! preg_match( '/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i', $html, $matches ) ) {
return false;
}
$img_tag = $matches[0];
$src = $matches[1];
if ( empty( $src ) ) {
return false;
}
// Trata de obtener el ancho y alto desde los atributos de la etiqueta.
$width = 0;
$height = 0;
if ( preg_match( '/width=["\'](\d+)["\']/', $img_tag, $w ) ) {
$width = (int) $w[1];
}
if ( preg_match( '/height=["\'](\d+)["\']/', $img_tag, $h ) ) {
$height = (int) $h[1];
}
// Si no hay dimensiones en la etiqueta, intenta resolver desde los metadatos del adjunto.
if ( ( ! $width || ! $height ) && function_exists( 'attachment_url_to_postid' ) ) {
$attachment_id = attachment_url_to_postid( $src );
if ( $attachment_id ) {
$img_data = wp_get_attachment_image_src( $attachment_id, 'large' );
if ( $img_data ) {
return array(
'url' => $img_data[0],
'width' => $img_data[1],
'height' => $img_data[2],
);
}
}
}
return array(
'url' => $src,
'width' => $width,
'height' => $height,
);
}
/**
* Obtener una imagen real de Gravatar para un autor.
*
* Verifica que el usuario tenga un Gravatar personalizado (no el placeholder por defecto)
* solicitándolo con d=404. El resultado se almacena en caché por 24 horas por usuario.
*
* @param int $user_id ID del usuario.
* @return array|false Array con la url, ancho, altura o false.
*/
function ayudawp_og_get_gravatar_image( $user_id ) {
$email = get_the_author_meta( 'user_email', $user_id );
if ( empty( $email ) ) {
return false;
}
$hash = md5( strtolower( trim( $email ) ) );
$transient_key = 'ayudawp_og_gravatar_' . $hash;
// Revisar el resultado en caché (false = sin gravatar, array = datos del gravatar).
$cached = get_transient( $transient_key );
if ( 'none' === $cached ) {
return false;
}
if ( is_array( $cached ) ) {
return $cached;
}
// Petición HEAD a Gravatar con d=404 para comprobar si existe un avatar real.
$check_url = 'https://www.gravatar.com/avatar/' . $hash . '?d=404';
$response = wp_remote_head( $check_url, array( 'timeout' => 3 ) );
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
set_transient( $transient_key, 'none', DAY_IN_SECONDS );
return false;
}
// Si existe Gravatar, devolver una versión de 512px para OG.
$avatar_url = get_avatar_url( $user_id, array( 'size' => 512 ) );
$data = array(
'url' => $avatar_url,
'width' => 512,
'height' => 512,
);
set_transient( $transient_key, $data, DAY_IN_SECONDS );
return $data;
}
Cómo comprobar que funciona
La forma más rápida de verificar que las etiquetas OG se están generando es abrir el código fuente de cualquier página de tu web (botón derecho > Ver código fuente de la página) y buscar esto:
<!-- AyudaWP Open Graph Tags -->
Todo lo que hay entre ese comentario y el de cierre <!-- /AyudaWP Open Graph Tags --> son las etiquetas que genera el mu-plugin.
Si lo encuentras es que funciona, si no aparece revisa que el archivo está en wp-content/mu-plugins/ y que no tengas un plugin SEO activo que lo esté anulando.
Lista de comprobaciones de que funciona
Una vez confirmado que las etiquetas están ahí toca revisar que los valores son correctos en cada tipo de página. Aquí tienes la lista completa de comprobaciones que puedes, y deberías, hacer.
Página de inicio
og:title= nombre del sitio.og:description= descripción corta del sitio.og:url= URL raíz con barra final.og:type=website.og:image= no debe aparecer.twitter:card=summary.
Entrada con imagen destacada y extracto
og:title= título de la entrada.og:description= texto del extracto, sin HTML, máximo 200 caracteres.og:image= URL de la imagen destacada en tamaño large.og:image:widthyog:image:height= dimensiones reales.og:url= permalink de la entrada.og:type=article.twitter:card=summary_large_image.twitter:image= misma URL queog:image.
Entrada sin extracto
og:description= primeras ~25 palabras del contenido, sin HTML ni shortcodes, terminado en «…».
Entrada sin imagen destacada pero con imagen en el contenido
og:image= URL de la primera<img>del contenido.- Si la imagen pertenece a la biblioteca de medios,
og:image:widthyog:image:heightaparecen con las dimensiones correctas.
Entrada sin ninguna imagen
og:image= no debe aparecer.twitter:card=summary.
Página estática
- Todo igual que una entrada, salvo que
og:type=websiteen vez dearticle.
Producto de WooCommerce
og:description= descripción corta del producto (el extracto de WooCommerce).og:image= imagen destacada del producto. Si no tiene, primera imagen de la galería del producto. Si tampoco, primera imagen del contenido.og:type=website.
Archivo de categoría o etiqueta con imagen en la descripción del término
og:title= nombre de la categoría o etiqueta.og:description= texto de la descripción del término, sin HTML.og:image= la imagen de la descripción HTML del término, no la del primer post.og:type=website.
Archivo de categoría o etiqueta sin imagen en la descripción
og:image= imagen destacada del primer post del listado que tenga una.
Archivo de autor
og:title= nombre para mostrar del autor.og:description= biografía del perfil.og:type=profile.og:image= Gravatar del autor si tiene uno personalizado. Si no, imagen destacada del primer post del autor.
Archivo de tipo de contenido personalizado
og:title= título del archivo del CPT.og:image= primer post del archivo que tenga imagen destacada.
Página de resultados de búsqueda
og:title= «Search results for» + el término buscado.og:image= no debe aparecer.og:description= no debe aparecer.
Comprobaciones generales
Estas comprobaciones hay que hacerlas en todos los casos:
- No hay etiquetas OG duplicadas en el
<head>. - Todas las etiquetas están entre los comentarios
<!-- AyudaWP Open Graph Tags -->y<!-- /AyudaWP Open Graph Tags -->. - Las URLs de las imágenes son absolutas (empiezan por
https://), no relativas. - La descripción no tiene saltos de línea, HTML ni caracteres raros.
- Las comillas y caracteres especiales en los títulos están escapados correctamente dentro del atributo
content.
Validación externa
Una vez que hayas comprobado el código fuente, valida las URLs con herramientas externas que simulan cómo las ven las plataformas:
- Facebook Sharing Debugger – te muestra exactamente lo que Facebook lee de tu página. También permite limpiar la caché si has hecho cambios.
- Twitter Card Validator – lo mismo para Twitter/X.
- LinkedIn Post Inspector – para comprobar cómo se ve al compartir en LinkedIn.
La prueba definitiva es la mejor, la manual, simplemente copia una URL de tu web y pégala en un chat de WhatsApp o Telegram. Si ves el título, la descripción y la imagen correcta es que todo funciona, vienes de nuevo aquí, me das las gracias.
¿De verdad es así de fácil?
Pues sí, hemos vivido engañados, así que ya puedes desinstalar ese enorme plugin de SEO (hazme caso, yo hace tiempo que no los uso) y usar esta triada perfecta:
- Native SEO Sitemap Customizer: Porque para qué quieres un plugin que cree otro mapa del sitio cuando WordPress ya lo hace por defecto y mejor, y compatible siempre. Con este plugin ultraligero consigues lo único que no tiene el sitemap nativo, poder personalizarlo a golpe de clic.
- Native SEO NoIndexer: Lo mejor que ofrecen los plugins de SEO y algo a lo que aún hacen caso los motores de búsqueda y los bots de IA es añadir la etiqueta
noindexa contenidos concretos, y este pequeño pero gran plugin te permite hacerlo a todos los niveles, de la manera más fácil posible. Funciona con todo tipo de contenidos, taxonomías, feeds, productos, por lotes, con exclusiones, inclusiones, desde el editor, los listados, lo tiene todo. - Native SEO Meta Tags: Este plugin hace todo lo que acabamos de ver y mucho más. Genera las tags Open Graph partir de lo que ya configuras en WordPress y tus contenidos, usando todo funciones nativas, es ligero, funciona, no tiene pegas.
¿Quitar un plugin para instalar tres? ¿no era malo instalar muchos plugins? En realidad no, lo malo es activar demasiado código para utilizar una décima parte, que es lo único que necesitas.
La mayoría de los plugins SEO habituales compiten por cantidad, no por calidad, cuando en realidad son pocas cosas las que hacen falta para hacer buen SEO, y mejor como hemos visto, con solo lo justo. Pero tú eliges, yo solo te doy opciones, y además todas gratis, así que no te cuesta nada probar.
¿Te gustó este artículo? ¡Ni te imaginas lo que te estás perdiendo en YouTube!






