<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" version="2.0">

<channel>
	<title>Ayuda WordPress</title>
	<atom:link href="https://ayudawp.com/feed/" rel="self" type="application/rss+xml"/>
	<link>https://ayudawp.com</link>
	<description>Recursos, temas, plugins, tutoriales en español</description>
	<lastBuildDate>Mon, 08 Jun 2026 14:51:14 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://ayudawp.com/wp-content/uploads/2026/05/cropped-ayuda-wordpress-32x32.png</url>
	<title>Ayuda WordPress</title>
	<link>https://ayudawp.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<xhtml:meta content="noindex" name="robots" xmlns:xhtml="http://www.w3.org/1999/xhtml"/><item>
		<title>La biblioteca de medios de WordPress es un caos ¿no se puede organizar por carpetas o algo?</title>
		<link>https://ayudawp.com/organizar-biblioteca-medios/</link>
					<comments>https://ayudawp.com/organizar-biblioteca-medios/#respond</comments>
		
		<dc:creator><![CDATA[Fernando Tellado]]></dc:creator>
		<pubDate>Tue, 09 Jun 2026 06:28:00 +0000</pubDate>
				<category><![CDATA[Tutoriales - Trucos]]></category>
		<category><![CDATA[WordPress.com]]></category>
		<category><![CDATA[WordPress.org]]></category>
		<category><![CDATA[Imágenes]]></category>
		<category><![CDATA[Principiante]]></category>
		<guid isPermaLink="false">https://ayudawp.com/?p=159460</guid>

					<description><![CDATA[¿Qué, le ponemos ya solución al desastre organizativo que es la biblioteca de medios de WordPress?]]></description>
										<content:encoded><![CDATA[<p>Si llevas un tiempo con WordPress y la biblioteca de medios pasa de unos cientos de archivos, ya sabes lo que toca ¿verdad?</p>
<p>Buscas una imagen que subiste hace tres meses, no recuerdas el nombre del archivo, te plantas delante de un mosaico interminable y empiezas a hacer scroll y clics hasta encontrarla … o desistir.</p>
<p>Esto pasa porque <strong>la gestión de medios de WordPress en general, y la biblioteca en particular, a estas alturas, sigue sin tener un modo de organización decente</strong>, ni clasificación por carpetas de forma nativa, ni siquiera un buscador instantáneo decente , y parece ser que esto sí que alguien ha decidido que sea territorio plugins, o no se, buscarte la vida.</p>
<p>Total, que <strong>vamos a ver qué opciones tenemos de organizar ese desastre que es la (no) organización de medios en WordPress</strong>, que haberlas haylas.</p>
<p>Empezaremos repasando los plugins más interesantes, y luego te cuento algún apaño más.</p>
<h2>Ojito con esto: carpetas virtuales y carpetas físicas</h2>
<p>Antes de elegir plugin tienes que entender una cosa que no te cuentan bien, porque hay dos formas de organizar la biblioteca con carpetas, y casi no se suele decir, y es importante, al menos yo creo que lo es.</p>
<h3>Carpetas virtuales</h3>
<p>El plugin no toca tus archivos, lo que hace es crear una taxonomía o estructura interna en la base de datos que asocia cada imagen con una «carpeta».</p>
<p>En el servidor todo sigue en <code>/wp-content/uploads/2026/04/</code> como siempre, ordenado por año y mes. Si miras por FTP, ahí no hay carpetas que valgan, solo cambia lo que ves en el escritorio de WordPress.</p>
<h3>Carpetas físicas</h3>
<p>El plugin mueve los archivos de verdad, de modo que si creas una carpeta «catálogo» y metes una imagen, el archivo se traslada a <code>/wp-content/uploads/catalogo/imagen.jpg</code>.</p>
<p>La URL del archivo cambia, y por tanto el plugin tiene que reescribir todas las referencias en la base de datos para que nada se rompa.</p>
<hr>
<p>Cada enfoque tiene ventajas y problemas:</p>
<ul>
<li>Las virtuales son más seguras, rápidas y no rompen nada al activarlas, pero te atan al plugin de por vida (luego vemos por qué).</li>
<li>Las físicas son más limpias, mejores para SEO y FTP, pero el proceso de migración es delicado y puede dejar enlaces rotos si algo va mal.</li>
</ul>
<h3>¿Qué pasa con las carpetas virtuales cuando desactivas el plugin?</h3>
<p>Aquí está la trampa principal y donde se nota la diferencia entre plugins. Si todas tus carpetas son virtuales y un día decides desactivar o cambiar de plugin, esto es lo que pasa:</p>
<ul>
<li>Los archivos siguen en el servidor, en sus carpetas <code>/uploads/año/mes/</code> de toda la vida, sin cambios. Eso es lo bueno.</li>
<li>La estructura de carpetas que habías montado durante meses o años desaparece de la vista en cuanto desactivas. La biblioteca vuelve a ser ese mosaico infinito de antes.</li>
<li>Si te pasas a otro plugin de carpetas virtuales, la mayoría tienen herramienta de importación, así que se puede migrar. Pero solo si el otro plugin sigue mantenido y tiene importador para el tuyo.</li>
<li>Si el plugin muere o el desarrollador desaparece, dependes de que otro plugin meta soporte para importar desde él, y eso no siempre pasa.</li>
</ul>
<p>O sea, con carpetas virtuales te casas con el plugin. Tu organización de medios deja de ser tuya, pasa a vivir en la base de datos del plugin que tengas instalado. No es un problema todos los días, pero el día que toca cambiar, si cambias, lo es.</p>
<p>Con carpetas físicas pasa lo contrario, pues si desinstalas el plugin los archivos siguen organizados en el servidor como los habías dejado.</p>
<p>La biblioteca de WordPress los seguirá viendo, porque la URL de cada imagen está guardada en su tabla de adjuntos. Pierdes la interfaz cómoda de carpetas en el escritorio, pero no pierdes la organización.</p>
<p>¿Lo tienes claro? pues empezamos, y si ya has tomado alguna decisión te saltas la parte que no te interesa.</p>
<h2>Plugins para organizar la biblioteca de medios</h2>
<p>Dicho lo anterior, para que te sea más fácil decidirte, te ordeno los plugins bajo este criterio, así sabes por dónde te andas.</p>
<h3>Plugins que usan el sistema de carpetas virtuales</h3>
<p>Son la mayoría, y funcionan a base de taxonomía personalizada para adjuntos.</p>
<h4>FileBird</h4>
<p>El más popular con diferencia, con <a href="https://wordpress.org/plugins/filebird/" target="_blank" rel="nofollow noopener">más de 200.000 instalaciones activas en WordPress.org</a>. Tiene versión gratuita generosa (carpetas ilimitadas, drag and drop, búsqueda) y Pro desde 25 USD con licencia de por vida para un sitio.</p>
<p>Importa la estructura de prácticamente todos los demás, es compatible con Elementor, Divi, Gutenberg, WPBakery, WooCommerce, WPML y Polylang.</p>
<p>Tiene un punto débil, y es que en los últimos años se le han reportado y parcheado varias vulnerabilidades de seguridad, así que mantenlo actualizado siempre.</p>
<h4>HappyFiles (Pro)</h4>
<p>Este es de lo más limpio y rápido del mercado, pero <a href="https://happyfiles.io/" target="_blank" rel="nofollow noopener">retiró la versión gratuita en 2022</a> y actualmente solo se vende Pro a 59 USD de pago único para sitios ilimitados, con devolución de 60 días.</p>
<p>La gran ventaja sobre los demás es que organiza también entradas, páginas, productos de WooCommerce y cualquier tipo de contenido personalizado, no solo medios, así que si vas a tener carpetas para todo el escritorio, este es el que tiene más sentido.</p>
<h4>Folders</h4>
<p>También está <a href="https://wordpress.org/plugins/folders/" target="_blank" rel="nofollow noopener">gratis en WordPress.org</a>  y tiene versión Pro de pago. Organiza medios, páginas y entradas, importa desde FileBird, Real Media Library, WP Media Folder, HappyFiles y otros.</p>
<p>Tiene una interfaz sencilla, buen punto de entrada para quien empieza.</p>
<h4>Real Media Library (Lite)</h4>
<p><a href="https://wordpress.org/plugins/real-media-library-lite/" target="_blank" rel="nofollow noopener">Tiene versión Lite gratis</a> con carpetas principales ilimitadas, y Pro desde 17 USD que añade subcarpetas, accesos directos en varias carpetas a la vez y más.</p>
<p>Es de los plugins mejor mantenidos del grupo y la curiosidad es que es un modelo mixto, porque su versión básica es virtual, pero si necesitas carpetas reales tiene una extensión que sí las mueve (lo vemos en la siguiente familia).</p>
<h4>CatFolders</h4>
<p>El recién llegado al sector, con una interfaz moderna, rendimiento decente, buen ajuste con Gutenberg. Va ganando cuota poco a poco.</p>
<p>Si te gusta probar lo nuevo o no te han convencido los otros plugins échale un ojo.</p>
<h3>Plugins que usan el sistema de carpetas físicas</h3>
<p>Si prefieres tener todo organizado como en tu ordenador, en directorios de verdad, estos son los que merece la pena probar.</p>
<h4>WP Media Folder</h4>
<p>Funciona por defecto con carpetas virtuales, pero en sus ajustes hay una opción que se llama justamente «Physical Folders» que las convierte en reales.</p>
<p>El propio fabricante avisa en su documentación de que activarla es delicado, porque desactiva algunas funciones del propio plugin y porque la migración toca muchas tablas.</p>
<p>Es el plugin más caro de la lista (desde 69 USD anuales con add-ons), pero también el único que integra sincronización con Google Drive, Dropbox, OneDrive y S3 de serie.</p>
<h4>Real Media Library + Real Physical Media</h4>
<p>Instalas Real Media Library de antes, para tener el árbol de carpetas, y encima la extensión <a href="https://devowl.io/wordpress-real-physical-media/" target="_blank" rel="nofollow noopener">Real Physical Media</a> reflejada esa estructura en el servidor.</p>
<p>Cuando mueves un archivo se mueve de verdad, físicamente, con redirecciones 301 automáticas para que nada quede roto. Tiene cola en segundo plano para no tumbar el servidor con muchos archivos.</p>
<p>Es la opción más profesional para quien quiere de verdad carpetas físicas con SEO bien resuelto.</p>
<h4>Media Library Folders</h4>
<p>Este plugin de Max Foundry esta disponible gratis en WordPress.org y con versión Pro. Crea carpetas reales en el servidor desde el primer momento.</p>
<p>No hay modo virtual ni nada que activar, las carpetas que ves son carpetas de verdad, y lo bueno es la simplicidad conceptual, pues lo que ves es lo que hay.</p>
<p>Lo malo es que para sitios grandes con muchos enlaces internos a imágenes, la reorganización inicial puede ser dura.</p>
<h2>Comparativa rápida</h2>
<table border="1" cellspacing="0" cellpadding="8">
<thead>
<tr>
<th>Plugin</th>
<th>Tipo de carpeta</th>
<th>¿Gratis?</th>
<th>Precio Pro</th>
<th>Para quién</th>
</tr>
</thead>
<tbody>
<tr>
<td>FileBird</td>
<td>Virtual</td>
<td>Sí</td>
<td>Desde 25 USD lifetime</td>
<td>Mayoría de webs, lo más usado</td>
</tr>
<tr>
<td>HappyFiles</td>
<td>Virtual</td>
<td>No</td>
<td>59 USD lifetime, sitios ilimitados</td>
<td>Agencias y quien quiere carpetas en todo el escritorio</td>
</tr>
<tr>
<td>Folders (Premio)</td>
<td>Virtual</td>
<td>Sí</td>
<td>Varios planes</td>
<td>Quien empieza y busca algo sencillo</td>
</tr>
<tr>
<td>Real Media Library</td>
<td>Virtual</td>
<td>Sí</td>
<td>Desde 17 USD</td>
<td>Bien mantenido, base para combinar con el add-on físico</td>
</tr>
<tr>
<td>CatFolders</td>
<td>Virtual</td>
<td>Sí</td>
<td>Varios planes</td>
<td>Quien quiere probar algo nuevo y ligero</td>
</tr>
<tr>
<td>WP Media Folder</td>
<td>Virtual o física</td>
<td>No</td>
<td>Desde 69 USD/año</td>
<td>Sitios con almacenamiento en la nube (Drive, S3, etc.)</td>
</tr>
<tr>
<td>Real Media Library / Real Physical Media</td>
<td>Física</td>
<td>Sí / No</td>
<td>RMA desde 17 USD + RPM aparte</td>
<td>SEO serio de imágenes, URLs descriptivas</td>
</tr>
<tr>
<td>Media Library Folders (Max Foundry)</td>
<td>Física</td>
<td>Sí</td>
<td>Versión Pro disponible</td>
<td>Quien quiere carpetas físicas sin complicarse</td>
</tr>
</tbody>
</table>
<h2>¿Y no se puede hacer algo sin plugins? ¡Taxonomías al rescate!</h2>
<p><strong>WordPress permite asignar taxonomías a los adjuntos</strong>, por si no lo sabías. Es en realidad lo mismo que usan por dentro casi todos los plugins virtuales.</p>
<p>Así que si te animas <strong>te olvidas de los plugins</strong>, y con unas líneas de código puedes registrar una taxonomía «<strong>carpeta</strong>» y empezar a <strong>clasificar tus imágenes desde la propia ficha de cada archivo</strong>.</p>
<p>Ya te aviso antes de empezar que esto no te va a dar interfaz de arrastrar y soltar, ni árbol de carpetas en la biblioteca. Lo que te ofrece son <strong>categorías que puedes asignar a cada imagen una a una, y filtros para luego encontrarlas y mostrarlas</strong>.</p>
<p>Si tienes la biblioteca a base de cientos de archivos y quieres organización rápida con clics, esto no es lo que necesitas, pero si lo tuyo es bibliotecas pequeñas o eres desarrollador y prefieres no depender de un plugin más, te <strong>sirve más que de sobra</strong>.</p>
<pre>&lt;?php
/**
* Plugin Name: Carpetas para organizar la biblioteca de medios
* Plugin URI: https://servicios.ayudawp.com
* Description: Organización ligera y todo con funciones nativas para crear y organizar medios usando carpetas mediante una taxonomía personanlizada.
* Version: 1.0
* Author: Fernando Tellado
* Author URI: https://tellado.es
* License: GPL-2.0-or-later
* Text Domain: ayudawp
*/

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* Registro de la taxonomía media_folder para los adjuntos.
*/
function ayudawp_register_media_folders_taxonomy() {

$labels = array(
'name' =&gt; 'Carpetas de medios',
'singular_name' =&gt; 'Carpeta de medios',
'search_items' =&gt; 'Buscar carpetas',
'all_items' =&gt; 'Todas las carpetas',
'parent_item' =&gt; 'Carpeta principal',
'parent_item_colon' =&gt; 'Carpeta principal:',
'edit_item' =&gt; 'Editar carpeta',
'update_item' =&gt; 'Actualizar carpeta',
'add_new_item' =&gt; 'Añadir nueva carpeta',
'new_item_name' =&gt; 'Nombre de la nueva carpeta',
'menu_name' =&gt; 'Carpetas',
);

$args = array(
'hierarchical' =&gt; true,
'labels' =&gt; $labels,
'public' =&gt; true,
'publicly_queryable' =&gt; false,
'show_ui' =&gt; true,
'show_in_menu' =&gt; true,
'show_admin_column' =&gt; true,
'show_in_rest' =&gt; true,
'query_var' =&gt; true,
'rewrite' =&gt; false,
);

register_taxonomy( 'media_folder', array( 'attachment' ), $args );
}
add_action( 'init', 'ayudawp_register_media_folders_taxonomy' );

/**
* Reemplazar la entrada de texto por defecto para media_folder en la pantalla de edición de adjuntos
* y la ventana emergente de medios con un menú desplegable.
*
* Usamos los slugs de los términos como valores de opción porque el núcleo de WordPress procesa
* las taxonomías de adjuntos como cadenas separadas por comas después de que se ejecute este filtro.
* Si pasamos IDs numéricos de términos, el núcleo intentará encontrar un slug que coincida con ese
* número y, si falla eso, crear un nuevo término con ese nombre.
*/
function ayudawp_replace_folder_field_in_modal( $form_fields, $post ) {

$taxonomy = 'media_folder';

if ( ! isset( $form_fields[ $taxonomy ] ) ) {
return $form_fields;
}

$terms = get_terms(
array(
'taxonomy' =&gt; $taxonomy,
'hide_empty' =&gt; false,
'orderby' =&gt; 'name',
'order' =&gt; 'ASC',
)
);

if ( is_wp_error( $terms ) || empty( $terms ) ) {
$form_fields[ $taxonomy ]['input'] = 'html';
$form_fields[ $taxonomy ]['html'] = '&lt;em&gt;Primero crea carpetas en Medios &amp;gt; Carpetas.&lt;/em&gt;';
return $form_fields;
}

$assigned = wp_get_object_terms( $post-&gt;ID, $taxonomy, array( 'fields' =&gt; 'slugs' ) );
$assigned = is_wp_error( $assigned ) ? array() : $assigned;

$name = 'attachments[' . absint( $post-&gt;ID ) . '][' . $taxonomy . ']';
$id = 'attachments-' . absint( $post-&gt;ID ) . '-' . $taxonomy;

$html = '&lt;select name="' . esc_attr( $name ) . '" id="' . esc_attr( $id ) . '" style="min-width:200px;"&gt;';
$html .= '&lt;option value=""&gt;(none)&lt;/option&gt;';

foreach ( $terms as $term ) {
$selected = in_array( $term-&gt;slug, $assigned, true ) ? ' selected' : '';
$html .= '&lt;option value="' . esc_attr( $term-&gt;slug ) . '"' . $selected . '&gt;' . esc_html( $term-&gt;name ) . '&lt;/option&gt;';
}

$html .= '&lt;/select&gt;';

$form_fields[ $taxonomy ]['input'] = 'html';
$form_fields[ $taxonomy ]['html'] = $html;

return $form_fields;
}
add_filter( 'attachment_fields_to_edit', 'ayudawp_replace_folder_field_in_modal', 20, 2 );

/**
* Añade un menú desplegable de filtro de carpetas a la vista de lista de la biblioteca de medios.
*/
function ayudawp_add_folder_filter_to_media_list( $post_type ) {

if ( 'attachment' !== $post_type ) {
return;
}

$taxonomy = 'media_folder';
$terms = get_terms(
array(
'taxonomy' =&gt; $taxonomy,
'hide_empty' =&gt; false,
)
);

if ( is_wp_error( $terms ) || empty( $terms ) ) {
return;
}

$current = isset( $_GET[ $taxonomy ] ) ? sanitize_key( wp_unslash( $_GET[ $taxonomy ] ) ) : '';

echo '&lt;select name="' . esc_attr( $taxonomy ) . '"&gt;';
echo '&lt;option value=""&gt;Todas las carpetas&lt;/option&gt;';

foreach ( $terms as $term ) {
echo '&lt;option value="' . esc_attr( $term-&gt;slug ) . '" ' . selected( $current, $term-&gt;slug, false ) . '&gt;' . esc_html( $term-&gt;name ) . '&lt;/option&gt;';
}

echo '&lt;/select&gt;';
}
add_action( 'restrict_manage_posts', 'ayudawp_add_folder_filter_to_media_list' );

/**
* Añade un menú desplegable de filtro de carpetas a la vista de cuadrícula de la biblioteca de medios y a la ventana emergente,
* usando wp_add_inline_script adjunto al manejador media-views.
*/
function ayudawp_add_folder_filter_to_grid_view() {

$terms = get_terms(
array(
'taxonomy' =&gt; 'media_folder',
'hide_empty' =&gt; false,
'orderby' =&gt; 'name',
'order' =&gt; 'ASC',
)
);

if ( is_wp_error( $terms ) || empty( $terms ) ) {
return;
}

$folders = array();
foreach ( $terms as $term ) {
$folders[] = array(
'slug' =&gt; $term-&gt;slug,
'name' =&gt; $term-&gt;name,
);
}

$js = '(function() {
if ( typeof wp === "undefined" || ! wp.media || ! wp.media.view || ! wp.media.view.AttachmentFilters ) {
return;
}
var folders = ' . wp_json_encode( $folders ) . ';
var FolderFilter = wp.media.view.AttachmentFilters.extend({
id: "media-attachment-folder-filter",
createFilters: function() {
var filters = {};
filters.all = {
text: "Todas las carpetas",
props: { media_folder: "" },
priority: 10
};
folders.forEach(function(folder, index) {
filters["folder_" + folder.slug] = {
text: folder.name,
props: { media_folder: folder.slug },
priority: 20 + index
};
});
this.filters = filters;
}
});
var originalCreateToolbar = wp.media.view.AttachmentsBrowser.prototype.createToolbar;
wp.media.view.AttachmentsBrowser.prototype.createToolbar = function() {
originalCreateToolbar.call( this );
this.toolbar.set( "mediaFolderFilter", new FolderFilter({
controller: this.controller,
model: this.collection.props,
priority: -75
}).render() );
};
})();';

wp_add_inline_script( 'media-views', $js );
}
add_action( 'admin_enqueue_scripts', 'ayudawp_add_folder_filter_to_grid_view' );

/**
* Filtra la consulta de adjuntos AJAX para que la vista de cuadrícula y la ventana emergente de medios
* respeten la carpeta seleccionada.
*/
function ayudawp_filter_attachments_query( $args ) {

if ( empty( $_REQUEST['query']['media_folder'] ) ) {
return $args;
}

$slug = sanitize_key( wp_unslash( $_REQUEST['query']['media_folder'] ) );

if ( '' === $slug ) {
return $args;
}

$args['tax_query'] = array(
array(
'taxonomy' =&gt; 'media_folder',
'field' =&gt; 'slug',
'terms' =&gt; $slug,
),
);

return $args;
}
add_filter( 'ajax_query_attachments_args', 'ayudawp_filter_attachments_query' );</pre>
<p>Pega este código en un plugin propio o en un mu-plugin, no lo metas en el <code>functions.php</code> del tema porque el día que cambies de tema te quedas sin carpetas y sin selector.</p>
<p>Si lo guardas como <code>/wp-content/mu-plugins/ayudawp-media-folders.php</code> se activa solo, que es lo más cómodo para uso personal.</p>
<p>Al activarlo te encuentras esto:</p>
<ul>
<li>En el menú de medios aparece «<strong>Carpetas</strong>» para crear y gestionar las carpetas como si fueran categorías, con jerarquía padre-hijo incluida.</li>
<li>En la ficha de edición de cada adjunto sale la caja meta de carpetas a la derecha, con casillas de selección.</li>
<li>En la ventana emergente de insertar imagen aparece el campo «Carpetas» con un desplegable.</li>
<li>En el listado de medios en vista de lista hay una columna nueva «Carpeta» y un filtro.</li>
<li>En la vista de cuadrícula de la biblioteca y en el modal de medios aparece también un filtro de carpetas en la barra de herramientas superior.</li>
</ul>

<a href="https://ayudawp.com/organizar-biblioteca-medios/taxonomia-carpetas-medios-ayudawp/" rel="nofollow"><img width="1200" height="678" src="https://ayudawp.com/wp-content/uploads/2026/06/taxonomia-carpetas-medios-ayudawp-1200x678.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/taxonomia-carpetas-medios-ayudawp-1200x678.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/taxonomia-carpetas-medios-ayudawp-768x434.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/taxonomia-carpetas-medios-ayudawp-1536x867.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/taxonomia-carpetas-medios-ayudawp.jpg 1920w" sizes="(max-width: 1200px) 100vw, 1200px" decoding="async" fetchpriority="high"></a>
<a href="https://ayudawp.com/organizar-biblioteca-medios/carpetas-medios-ayudawp-vista-lista-biblioteca/" rel="nofollow"><img width="1200" height="630" src="https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-vista-lista-biblioteca-1200x630.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-vista-lista-biblioteca-1200x630.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-vista-lista-biblioteca-768x403.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-vista-lista-biblioteca-1536x806.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-vista-lista-biblioteca.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>
<a href="https://ayudawp.com/organizar-biblioteca-medios/carpetas-medios-ayudawp-vista-cuadricula-biblioteca/" rel="nofollow"><img width="1200" height="609" src="https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-vista-cuadricula-biblioteca-1200x609.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-vista-cuadricula-biblioteca-1200x609.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-vista-cuadricula-biblioteca-768x390.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-vista-cuadricula-biblioteca-1536x780.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-vista-cuadricula-biblioteca.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>
<a href="https://ayudawp.com/organizar-biblioteca-medios/carpetas-medios-ayudawp-ventana-emergente-biblioteca/" rel="nofollow"><img width="1200" height="678" src="https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-ventana-emergente-biblioteca-1200x678.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-ventana-emergente-biblioteca-1200x678.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-ventana-emergente-biblioteca-768x434.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-ventana-emergente-biblioteca-1536x867.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-ventana-emergente-biblioteca.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>
<a href="https://ayudawp.com/organizar-biblioteca-medios/carpetas-medios-ayudawp-editor-imagenes/" rel="nofollow"><img width="1200" height="678" src="https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-editor-imagenes-1200x678.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-editor-imagenes-1200x678.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-editor-imagenes-768x434.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-editor-imagenes-1536x867.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-editor-imagenes.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>
<a href="https://ayudawp.com/organizar-biblioteca-medios/carpetas-medios-ayudawp-insertador-imagenes-editor/" rel="nofollow"><img width="1200" height="679" src="https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-insertador-imagenes-editor-1200x679.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-insertador-imagenes-editor-1200x679.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-insertador-imagenes-editor-768x434.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-insertador-imagenes-editor-1536x869.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/carpetas-medios-ayudawp-insertador-imagenes-editor.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>

<h3>Algunas pequeñas limitaciones</h3>
<ul>
<li>Solo permite una carpeta por archivo en el selector de la ventana emergente. La caja meta de la ficha permite varias por ser de tipo <code>checkbox</code>. Si quieres consistencia ajusta uno de los dos.</li>
<li>No hay arrastrar y soltar, ni árbol al estilo Finder, esto son selectores y desplegables. Para interfaz más cómoda, plugin al canto.</li>
<li>El filtro de la vista de cuadrícula aparece también en la ventana emergente de insertar imagen en el editor de entradas y páginas, pues ambos usan el mismo componente de WordPress. Si te molesta en la ventana emergente hay forma de limitarlo solo a la pantalla principal de la biblioteca pero alarga el código.</li>
<li>Si tienes muchas carpetas (más de veinte o treinta), el desplegable se hace incómodo. Cualquier plugin profesional resuelve esto con un árbol lateral, aquí no lo tienes.</li>
</ul>
<p>Aun con esas limitaciones, hace su trabajo y te deja la organización guardada en tablas estándar de WordPress (<code>wp_terms</code>, <code>wp_term_taxonomy</code>, <code>wp_term_relationships</code>), sin dependencia de un plugin de terceros que pueda desaparecer mañana.</p>
<p>Si un día decides instalar FileBird o cualquier otro, la mayoría tienen importador de taxonomías estándar y pueden recuperar la organización sin que tengas que reasignar las imágenes a mano.</p>
<h2>Y entonces ¿cuál elijo?</h2>
<p>Respuesta corta y al grano:</p>
<ul>
<li>Si quieres lo más sencillo, gratis y popular: <strong>FileBird</strong>. Manténlo actualizado siempre por las vulnerabilidades pasadas.</li>
<li>Si llevas una agencia o varias webs y vas a usar carpetas para entradas, páginas y productos además de medios: <strong>HappyFiles</strong>.</li>
<li>Si te importa el SEO de las imágenes, quieres URLs limpias y descriptivas y no te asusta el proceso de migración: <strong>Real Media Library con el add-on Real Physical Media</strong>.</li>
<li>Si combinas WordPress con Google Drive, Dropbox o almacenamiento en la nube: <strong>WP Media Folder</strong>.</li>
<li>Si quieres carpetas físicas reales sin complicarte con add-ons: <strong>Media Library Folders</strong> de Max Foundry.</li>
<li>Si eres desarrollador o prefieres montar tu propio sistema mínimo sin atarte: <strong>taxonomía personalizada</strong> con el código de arriba.</li>
</ul>
<p>Sea cual sea tu elección, antes de instalar <strong>pregúntate si podrías vivir si esE plugin desaparece</strong> mañana. Si la respuesta es que no, estás eligiendo mal, o estás eligiendo bien pero asumiendo el riesgo conscientemente, que también vale.</p>
<p>Y sobre todo, antes de tocar la biblioteca de medios de una web en producción, haz copia de seguridad, y si vas a migrar a carpetas físicas, prueba primero en un staging.</p>
<p>Estas operaciones tocan miles de archivos y registros, y un fallo a mitad puede dejarte la biblioteca con imágenes huérfanas y enlaces rotos en posts antiguos.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ayudawp.com/organizar-biblioteca-medios/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Por qué ya ni siquiera puedes fiarte de los plugins de WordPress.org</title>
		<link>https://ayudawp.com/ataques-cadena-suministro-plugins/</link>
					<comments>https://ayudawp.com/ataques-cadena-suministro-plugins/#respond</comments>
		
		<dc:creator><![CDATA[Fernando Tellado]]></dc:creator>
		<pubDate>Mon, 08 Jun 2026 06:28:15 +0000</pubDate>
				<category><![CDATA[Seguridad WordPress]]></category>
		<category><![CDATA[Tutoriales - Trucos]]></category>
		<category><![CDATA[WordPress.org]]></category>
		<guid isPermaLink="false">https://ayudawp.com/?p=159487</guid>

					<description><![CDATA[Ni siquiera instalar plugins desde WordPress.org es totalmente seguro, y siempre hay que estar alerta, protegerse, y tomar medidas…]]></description>
										<content:encoded><![CDATA[<p>El 7 de abril de 2026 <strong>treinta y un plugins WordPress desaparecieron del repositorio oficial el mismo día</strong>. Si tenías alguno instalado, <strong>durante ocho meses tu web estuvo enviando spam a Google sin que tú vieras nada</strong> raro en el escritorio.</p>
<p><strong>No fue un fallo de programación ni una vulnerabilidad clásica</strong> de las que se publican en una base de datos y se parchean en dos semanas, fue otra cosa.</p>
<p>Alguien con dinero compró el catálogo entero de plugins de un desarrollador conocido, heredó sus permisos de WordPress.org y subió <strong>una actualización envenenada</strong> que llegó a cientos de miles de webs como si fuera una mejora más.</p>
<p>Aviso por si te suena lejano, <strong>la misma semana cayó otro plugin con 800.000 instalaciones</strong>, y un par más al poco tiempo. Esto va a más, y conviene que sepas cómo defenderte.</p>
<h2>Los 3 casos que la liaron parda</h2>
<p><img decoding="async" class="sombra alignnone wp-image-159502 size-full" src="https://ayudawp.com/wp-content/uploads/2026/06/la-he-liado-parda.jpg" alt="" width="1200" height="700" srcset="https://ayudawp.com/wp-content/uploads/2026/06/la-he-liado-parda.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/la-he-liado-parda-768x448.jpg 768w" sizes="(max-width: 1200px) 100vw, 1200px"></p>
<p>No es un caso aislado, es un patrón. En muy poco tiempo se descubrieron al menos tres ataques con el mismo guion, aunque el tipo de amenaza fue diferente.</p>
<h3>Essential Plugin: el catálogo de plugins comprado en Flippa</h3>
<p>Un comprador con perfil en SEO, criptomonedas y marketing de juego online adquirió en Flippa el catálogo completo de Essential Plugin (que antes se llamaba WP Online Support) por una cifra de seis dígitos a principios de 2025.</p>
<p>Eran <strong>más de treinta plugins con años de desarrollo legítimo y unas 400.000 instalaciones</strong> declaradas entre todos.</p>
<p>El 8 de agosto de 2025, en la versión 2.6.7 de los plugins, <strong>el nuevo dueño subió una actualización con un registro de cambios tan inocente que daba pereza leerlo</strong>, como tantos otros: «Check compatibility with WordPress version 6.8.2».</p>
<p>Detrás de ese texto venían <strong>191 líneas extra de PHP con una puerta trasera de deserialización</strong>, que estuvo durmiente ocho meses.</p>
<p>El 5 de abril de 2026, <strong>el atacante activó la puerta trasera</strong> desde su servidor (<code>analytics.essentialplugin.com</code>) y <strong>las webs infectadas empezaron a servir spam SEO camuflado solo para Googlebot</strong>.</p>
<p>Los visitantes humanos veían la web normal, el propietario de la web veía el escritorio normal, pero Google veía una web llena de <strong>enlaces a chiringuitos de apuestas</strong>.</p>
<p>Patchstack y Anchor Hosting destaparon la liada y el 7 de abril <strong>WordPress.org cerró los 31 plugins de golpe</strong>.</p>
<h3>Smart Slider 3 Pro: el servidor de actualizaciones comprometido</h3>
<p>La misma semana el plugin Smart Slider 3 Pro (800.000 instalaciones) cayó por otra vía. Aquí no hubo compra del plugin, pero alguien consiguió acceso al servidor desde el que se distribuyen las actualizaciones premium y metió <strong>código malicioso en una versión normal</strong>.</p>
<p>El efecto para el usuario final fue el mismo, o sea, instalas una actualización oficial firmada por el autor de siempre y <strong>te llevas malware de regalo</strong>.</p>
<h3>Widget Logic, Countdown Timer Ultimate y los que vendrán</h3>
<p>El mismo patrón se ha visto en Widget Logic y Countdown Timer Ultimate, descubiertos en las semanas siguientes, y más que vendrán mientras no se le ponga algún tipo de freno.</p>
<p><strong>El método es demasiado rentable como para que algún joputa no lo repita</strong>, porque no necesitas saber explotar una vulnerabilidad, ni buscarla, ni pelearte con un cortafuegos, compras la confianza ya forjada por otro y la usas contra los usuarios.</p>
<h2>Cómo lo llaman a esta putada en inglés … y qué es exactamente</h2>
<p><img decoding="async" class="sombra alignnone wp-image-159498 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/06/supply-chain-attack-WordPress-plugins-1200x800.jpg" alt="" width="1200" height="800" srcset="https://ayudawp.com/wp-content/uploads/2026/06/supply-chain-attack-WordPress-plugins-1200x800.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/supply-chain-attack-WordPress-plugins-768x512.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/supply-chain-attack-WordPress-plugins.jpg 1536w" sizes="(max-width: 1200px) 100vw, 1200px"></p>
<p>En la prensa especializada anglosajona lo llaman <strong><em>supply chain attack</em></strong> (ataque a la cadena de suministro), que no se tú, pero a mi no me dice nada, suena como a tomarse un te macha en una terraza de verano o poco más.</p>
<p>Y ya lo entiendo, que realmente no está mal traído, pues el término viene de la idea de que <strong>tu plugin no es solo el código que ves, es también el desarrollador, el repositorio, el servidor de actualizaciones y todos los eslabones intermedios</strong>.</p>
<p>Trata de definir que <strong>si uno de esos eslabones se corrompe, el código que tú instalas en tu web ya no es el que tú creías que era, aunque venga por el canal oficial</strong>.</p>
<p>La diferencia con un ataque tradicional es importante, porque en un ataque clásico los malos buscan un fallo en tu plugin para colarse. Aquí no necesitan buscar nada porque el plugin viene ya envenenado de origen.</p>
<p>La actualización que rompe tu web no es un exploit, es una versión oficial firmada por el autor original, distribuida por WordPress.org, instalada por tu sistema de actualizaciones automáticas como ha hecho mil veces antes. Por eso es tan difícil de parar.</p>
<p>Venga, acepto el nombre, pero a mi me pega más algo como cabronada, plugins envenenados o burra vieja, no se, ya me entiendes, soy más de andar por casa.</p>
<h2>Por qué WordPress es el caldo de cultivo perfecto</h2>
<p><img decoding="async" class="sombra alignnone wp-image-159503 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/06/caballo-de-troya-instalador-plugins-WordPress-1200x675.jpg" alt="" width="1200" height="675" srcset="https://ayudawp.com/wp-content/uploads/2026/06/caballo-de-troya-instalador-plugins-WordPress-1200x675.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/caballo-de-troya-instalador-plugins-WordPress-768x432.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/caballo-de-troya-instalador-plugins-WordPress-1536x864.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/caballo-de-troya-instalador-plugins-WordPress.jpg 1672w" sizes="(max-width: 1200px) 100vw, 1200px"></p>
<p>Esto no pasa solo en WordPress, pero <strong>en WordPress pasa con especial facilidad por varias razones</strong> que conviene tener claras.</p>
<h3>Los plugins se compran y se venden como cualquier otro activo digital</h3>
<p>En marketplaces como Flippa puedes encontrar plugins en venta con sus 50.000, 100.000 o 400.000 instalaciones activas.</p>
<p><strong>El comprador, sea quien sea y tenga las intenciones que tenga, hereda el acceso SVN al repositorio de WordPress.org</strong> y puede subir actualizaciones desde el minuto uno.</p>
<p><strong>No hay un proceso de revisión del nuevo dueño, ni un aviso público</strong> en el escritorio del usuario diciendo «este plugin ha cambiado de manos, revisa antes de actualizar», nada.</p>
<h3>Las actualizaciones automáticas funcionan en tu contra cuando esto pasa</h3>
<p>Si tienes las activadas las actualizaciones automáticas para todos los plugins, que es la recomendación habitual razonable, <strong>recibes la versión envenenada sin enterarte</strong>.</p>
<p>De hecho muchos hosting incluso fuerzan las actualizaciones automáticas por defecto, por buena intención, pero aquí te das de bruces contra una putada que pervierte una buena idea original.</p>
<h3>WordPress.org no firma el código</h3>
<p>Otros ecosistemas como Debian, Microsoft Store o iOS exigen firma criptográfica para que el software que ejecutas pueda comprobarse contra una clave del autor original.</p>
<p>En WordPress eso no existe, <strong>si alguien hereda los permisos del autor puede subir lo que quiera sin que nadie compare nada contra nada</strong>.</p>
<p>No se tú, llámame loco, pero esto debería revisarse en las directrices del directorio de plugins, no es complicado de implementar, no es algo raro, y nos quitaría de mucho cabronazo malintencionado de estos.</p>
<h3>El modelo de negocio del plugin gratuito empuja a vender</h3>
<p>Mantener un plugin con miles de instalaciones es caro en tiempo, en soporte y en mantenimiento, y si no monetizas con versión premium ni con servicios llega un momento en el que abandonas o vendes.</p>
<p>Los compradores con intenciones limpias existen, la mayoría lo hacen por ese digno objetivo de ganar dinero lícitamente, pero también están los otros, que prefieren hacerse ricos a tu costa, y sin tu consentimiento.</p>
<p>¿He dicho ya que me cago en todos sus muertos?, por si acaso.</p>
<h2>Cómo saber si tu web está infectada con plugins envenenados</h2>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-159505 size-full" src="https://ayudawp.com/wp-content/uploads/2026/06/investigar-codigo-inseguro-WordPress.jpg" alt="" width="1200" height="670" srcset="https://ayudawp.com/wp-content/uploads/2026/06/investigar-codigo-inseguro-WordPress.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/investigar-codigo-inseguro-WordPress-768x429.jpg 768w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<p>Si tienes las actualizaciones automáticas activadas y alguno de los plugins comprometidos estaba instalado entre agosto de 2025 y abril de 2026, posiblemente tu sitio sirvió contenido manipulado durante un tiempo.</p>
<p>Estos son los indicadores que tienes que mirar ahora mismo.</p>
<h3>Busca en wp-config.php</h3>
<p>La puerta trasera de Essential Plugin se inyectaba en <code>wp-config.php</code> justo en la misma línea que el <code>require_once ABSPATH . 'wp-settings.php';</code>.</p>
<p>No estaba en una línea aparte, en la misma, para que sea fácil pasarlo por alto si revisas el archivo a ojo.</p>
<p>Abre tu <code>wp-config.php</code> por SFTP o desde el gestor de archivos del hosting y busca esa línea. Si ves cualquier cosa pegada a continuación o el archivo es notablemente más grande de lo normal (la inyección añade unos 6 KB), tienes un problema.</p>
<h3>Busca el archivo wp-comments-posts.php</h3>
<p>El payload activado creaba un archivo llamado <code>wp-comments-posts.php</code> en la raíz del WordPress. Si lo ves ahí no es tuyo, no es de una instalación normal, WordPress no tiene ningún archivo con ese nombre por defecto.</p>
<h3>Revisa el listado de plugins cerrados</h3>
<p>Entra en tu listado de plugins instalados, abre los detalles de cada uno y mira si aparece el aviso de «<code>cerrado</code>», o algo más claro en la página del plugin, pero tampoco para tirar cohetes.</p>

<a href="https://ayudawp.com/ataques-cadena-suministro-plugins/plugin-cerrado-instalador/" rel="nofollow"><img width="1200" height="675" src="https://ayudawp.com/wp-content/uploads/2026/06/plugin-cerrado-instalador-1200x675.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/plugin-cerrado-instalador-1200x675.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/plugin-cerrado-instalador-768x432.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/plugin-cerrado-instalador-1536x864.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/plugin-cerrado-instalador.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>
<a href="https://ayudawp.com/ataques-cadena-suministro-plugins/widget-logic-plugin-cerrado/" rel="nofollow"><img width="1200" height="675" src="https://ayudawp.com/wp-content/uploads/2026/06/Widget-Logic-Plugin-cerrado-1200x675.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/Widget-Logic-Plugin-cerrado-1200x675.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/Widget-Logic-Plugin-cerrado-768x432.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/Widget-Logic-Plugin-cerrado-1536x864.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/Widget-Logic-Plugin-cerrado.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>

<p>Si lo ves lo desinstalas inmediatamente, no lo dejes ahí «por si lo reabren» o «por si arreglan algo». Los archivos siguen ejecutándose mientras estén ahí, aunque el plugin esté desactivado en WordPress.</p>
<h3>Plugins de Essential Plugin</h3>
<p>Si no recuerdas todos los plugins que tienes (o tenías), la lista completa de los 31 plugins de Essential Plugin afectados la mantienen actualizada en <a href="https://anchor.host/someone-bought-30-wordpress-plugins-and-planted-a-backdoor-in-all-of-them/" target="_blank" rel="nofollow noopener">el análisis de Anchor Hosting</a>.</p>
<p>Compara con tu lista de plugins instalados, incluso si están desactivados.</p>
<h3>Mira los logs de Cloudflare o del hosting</h3>
<p>Si tienes Cloudflare o cualquier CDN delante, revisa el tráfico saliente hacia analytics.essentialplugin.com en los últimos meses.</p>
<p>Si tu hosting te deja acceder a los logs de salida (no todos lo hacen), busca peticiones a ese dominio, es un indicador casi infalible.</p>
<h2>Cómo te defiendes para el próximo ataque</h2>
<p>Porque va a haber uno, no te quepa duda. La defensa contra este tipo de ataques no es una sola medida, son varias capas que tienen que trabajar juntas, te las cuento por orden de prioridad.</p>
<h3>Detección de plugins cerrados</h3>
<p>Cuando WordPress.org cierra un plugin (por vulnerabilidad grave, por código malicioso o por incumplir las normas del repositorio), no te avisa de manera clara.</p>
<p>Se marca el aviso en el listado de plugins, pero si no entras a esa pantalla a diario y miras los detalles de los plugins, te puedes pasar semanas con el plugin comprometido instalado sin enterarte. Esto es lo primero que tienes que automatizar.</p>
<p>Dos opciones para esto, estas:</p>
<ul>
<li><a href="https://ayudawp.com/tag/wordfence-2/" target="_blank" rel="nofollow noopener">Wordfence</a> detecta plugins cerrados y abandonados desde 2017. Es la opción más conocida, pero lo hace dentro de su escaneo de malware general, que la gente programa cada semana. Si el plugin se cierra el lunes y tu escaneo es el viernes, tienes cuatro días de margen.</li>
<li>El plugin <a href="https://es.wordpress.org/plugins/vigilante/" target="_blank" rel="nofollow noopener">Vigilante</a> tiene esta función, con un par de detalles que ayudan bastante. Por un lado, lanza una consulta diaria a la API de WordPress.org para cada plugin instalado (activado o no) y avisa el mismo día con un email crítico. Por otro, recuerda qué plugins estaban vivos antes, y si la API empieza a devolver 404 para un plugin que ayer respondía, lo trata como cierre con la misma gravedad que un cierre explícito (algunos cierres por motivos graves esconden la metadata del plugin entera, y si solo miras lo que devuelve la API te lo puedes perder). Además, los plugins cerrados aparecen en el escritorio como recomendación crítica con enlace directo, en la auditoría de seguridad como evento, y se siguen mostrando en cada email de resumen hasta que los desinstalas o los marcas como ignorados de forma explícita.</li>
</ul>

<a href="https://ayudawp.com/ataques-cadena-suministro-plugins/vigilante-recomendacion-plugin-cerrado-detectado/" rel="nofollow"><img width="1200" height="678" src="https://ayudawp.com/wp-content/uploads/2026/06/vigilante-recomendacion-plugin-cerrado-detectado-1200x678.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/vigilante-recomendacion-plugin-cerrado-detectado-1200x678.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/vigilante-recomendacion-plugin-cerrado-detectado-768x434.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/vigilante-recomendacion-plugin-cerrado-detectado-1536x867.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/vigilante-recomendacion-plugin-cerrado-detectado.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>
<a href="https://ayudawp.com/ataques-cadena-suministro-plugins/vigilante-plugin-cerrado-en-check-seguridad/" rel="nofollow"><img width="1200" height="678" src="https://ayudawp.com/wp-content/uploads/2026/06/vigilante-plugin-cerrado-en-check-seguridad-1200x678.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/vigilante-plugin-cerrado-en-check-seguridad-1200x678.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/vigilante-plugin-cerrado-en-check-seguridad-768x434.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/vigilante-plugin-cerrado-en-check-seguridad-1536x867.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/vigilante-plugin-cerrado-en-check-seguridad.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>

<p>Cualquiera de las dos opciones es mejor que nada. Si ya tienes Wordfence lo dejas, si quieres detección más rápida y dedicada o no quieres meter Wordfence por su peso prueba con Vigilante.</p>
<h3>Integridad de archivos</h3>
<p>La inyección de la puerta trasera cambia archivos en el plugin y a veces en el <code>wp-config.php</code>. Un escáner de integridad de archivos que compare lo que hay en tu instalación con las sumas de comprobación oficiales de WordPress.org detecta cambios sospechosos.</p>
<p>Esto lo hacen Kadence Security, Vigilante y otros. Lo activas, lo programas cada día, y recibes un email cuando hay un archivo modificado que no esperabas. El truco está en no ignorar los avisos.</p>
<p><a href="https://ayudawp.com/?attachment_id=159495" rel="nofollow"><img loading="lazy" decoding="async" class="sombra alignnone wp-image-159495 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/06/vigilante-plugin-cerrado-integridad-archivos-1200x345.jpg" alt="" width="1200" height="345" srcset="https://ayudawp.com/wp-content/uploads/2026/06/vigilante-plugin-cerrado-integridad-archivos-1200x345.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/vigilante-plugin-cerrado-integridad-archivos-768x221.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/vigilante-plugin-cerrado-integridad-archivos-1536x442.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/vigilante-plugin-cerrado-integridad-archivos-350x100.jpg 350w, https://ayudawp.com/wp-content/uploads/2026/06/vigilante-plugin-cerrado-integridad-archivos.jpg 1870w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></a></p>
<p>Cuando <strong>WordPress se actualiza</strong>, también te llegan <strong>avisos de archivos modificados</strong> (porque efectivamente se modifican), y <strong>la mayoría de nosotros nos acostumbramos a marcarlos como leídos sin mirar</strong>, y no, no lo hagas.</p>
<p>Si un email te avisa de cambios en plugins que no has actualizado, abre y mira.</p>
<h3>Auditoría y registro de actividad</h3>
<p>Tener un registro que te diga <strong>quién instala, activa, actualiza o desactiva plugins</strong> en tu web es un seguro barato, de hecho gratis.</p>
<p>Si alguien con acceso al admin de WordPress mete un plugin envenenado o activa uno que ya estaba desactivado queda registrado.</p>
<p>Lo incluyen casi todos los plugins de seguridad medianamente serios (como Vigilante), y sino también hay plugins específicos que son solo para esto.</p>
<h3>Actualizaciones automáticas selectivas</h3>
<p>Aquí va a ir contra lo que has leído mil veces, pero hay matices, porque sí, <strong>las actualizaciones automáticas son buena idea para plugins pequeños y/o bien mantenidos</strong>, especialmente si no son críticos para tu negocio.</p>
<p>Pero <strong>para plugins que tocan dinero o datos sensibles</strong> (WooCommerce, pasarelas de pago, formularios que recogen tarjetas, plugins de membresía, plugins de email transaccional) <strong>la actualización automática es una muy mala idea</strong>.</p>
<p>Te bajas la actualización a staging, miras el registro de cambios, esperas 48 horas a ver si alguien grita en los foros, y entonces actualizas a producción.</p>
<p>El caso Essential Plugin se distribuyó precisamente gracias a las actualizaciones automáticas. Quien las tenía desactivadas y revisaba los registros de cambios se salvó, aunque fuese por suerte o casualidad.</p>
<h2>Protegiendo la comarca</h2>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-159563" src="https://ayudawp.com/wp-content/uploads/2026/06/wapuu-a-gandalf-wapuu-that-is-a-coding-wizard-302912b8.webp" alt="" width="1024" height="1024" srcset="https://ayudawp.com/wp-content/uploads/2026/06/wapuu-a-gandalf-wapuu-that-is-a-coding-wizard-302912b8.webp 1024w, https://ayudawp.com/wp-content/uploads/2026/06/wapuu-a-gandalf-wapuu-that-is-a-coding-wizard-302912b8-150x150.webp 150w, https://ayudawp.com/wp-content/uploads/2026/06/wapuu-a-gandalf-wapuu-that-is-a-coding-wizard-302912b8-768x768.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px"></p>
<p>Afortunadamente, el 5 de junio, coincidiendo con WordCamp Europe, <a href="https://wordpress.org/news/2026/06/pts/" target="_blank" rel="nofollow noopener">se anunció</a> que WordPress está poniendo en la nevera las actualizaciones de plugins durante 24 horas, para precisamente evitar cosas como lo que estamos hablando, para dar tiempo al equipo de plugins a revisar todas las actualizaciones, por supuesto ayudados de una IA especializada, a la que han llamado Gandalf, para que tenga de todo.</p>
<p>De momento son eso, 24 horas de espera, pero se irán reduciendo los tiempos a medida que se mejore el sistema.</p>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-159565 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/06/aviso-espera-plugin-actualizado-seguridad-gandalf-proteger-comarca-1200x198.jpg" alt="" width="1200" height="198" srcset="https://ayudawp.com/wp-content/uploads/2026/06/aviso-espera-plugin-actualizado-seguridad-gandalf-proteger-comarca-1200x198.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/aviso-espera-plugin-actualizado-seguridad-gandalf-proteger-comarca-768x127.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/aviso-espera-plugin-actualizado-seguridad-gandalf-proteger-comarca.jpg 1476w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<p>Es una buenísima noticia, sí, pero no exime a nadie de responsabilidades, tampoco a ti.</p>
<h2>Lo que no hace ningún plugin por ti</h2>
<p>Ningún plugin de seguridad te salva de todo, <strong>hay cosas que tienes que hacer tú</strong>, con tu equipo o contratando a alguien que se ocupe por ti, y no hay automatización que las sustituya.</p>
<p>Revisar el registro de cambios antes de actualizar plugins críticos es una de ellas. Un changelog que dice «Check compatibility with WordPress version 6.8.2» y la actualización pesa 6 KB más, debería levantarte la ceja. No siempre se nota tan claro, pero a veces sí, y conviene mirar.</p>
<p>Suscribirte a feeds de bases de datos de vulnerabilidades es otra. <a href="https://patchstack.com/database/" target="_blank" rel="nofollow noopener">Patchstack</a>, <a href="https://wpscan.com/" target="_blank" rel="nofollow noopener">WPScan</a> y <a href="https://www.wordfence.com/threat-intel/" target="_blank" rel="nofollow noopener">Wordfence Intelligence</a> publican avisos a diario. No necesitas leerlos todos, pero suscribirte a sus newsletters semanales te da una visión general del panorama sin esfuerzo.</p>
<p>Seguir el foro de soporte de los plugins que usas en webs importantes también ayuda. Si un plugin con muchas instalaciones se ha vuelto raro, los foros se llenan de quejas a las pocas horas, y tener cinco o seis plugins críticos en favoritos del foro de WordPress.org no cuesta nada.</p>
<p><strong>Desconfiar de cualquier plugin que cambie de dueño</strong> y desinstalarlo si puedes vivir sin él es la medida más radical, pero también la más segura. Si te enteras de que un plugin que usas ha sido vendido te tomas un par de horas para evaluar alternativas y mantenerte preparado por si la cosa va mal. No siempre va mal, pero cuando va ya no hay tiempo.</p>
<p>Y mantener backups, sí, otra vez. Copias de seguridad de verdad, fuera de tu servidor, probados, recientes y restaurables en menos de una hora.</p>
<p>Si te ha pasado lo de Essential Plugin y no quieres pelearte con la limpieza, lo más rápido suele ser restaurar a antes de la infección y aplicar lo aprendido para no caer otra vez.</p>
<h2>Lista de tareas para ya</h2>
<p>Si has llegado hasta aquí, dedica un rato hoy mismo a hacer esto. No te lleva más de una hora, dependiendo del tamaño de tu web.</p>
<ul>
<li><strong>Revisa el listado de plugins instalados</strong> (incluso los desactivados) y comprueba si aparece alguno con el aviso de «cerrado». Si lo hay, desinstálalo.</li>
<li><strong>Cruza tu listado contra el de lo 31 plugins de Essential Plugin</strong> del enlace de Anchor Hosting que te he puesto más arriba. Si te coincide alguno, asume que tu web estuvo comprometida y empieza la limpieza desde wp-config.php.</li>
<li>Abre tu wp-config.php por SFTP y <strong>busca código extraño</strong> junto a la línea de <code>require_once ABSPATH . 'wp-settings.php';</code>. Si lo ves, restaura el archivo desde un backup limpio.</li>
<li>Comprueba si existe un archivo <code>wp-comments-posts.php</code> en la raíz de tu WordPress. Si está lo borras sin piedad.</li>
<li>Instala y activa un plugin de seguridad que tenga detector de plugins cerrados. Wordfence o Vigilante, el que prefieras.</li>
<li>Activa el escáner de integridad de archivos con email diario del plugin de seguridad, y si no tiene esa herramienta cambia de plugin de seguridad.</li>
<li>Activa también la auditoría de registro de actividad para saber quién toca qué.</li>
<li>Desactiva las actualizaciones automáticas en los plugins que tocan dinero o datos sensibles. Planifica un proceso manual de revisión semanal.</li>
<li>Suscríbete al boletín de Patchstack o Wordfence Intelligence. Cinco minutos a la semana leyendo te ahorra muchos disgustos.</li>
<li>Si gestionas webs de clientes, comprueba estos mismos puntos en todas las que mantienes. Si tienes diez webs, una hora por web es un día de trabajo bien invertido.</li>
</ul>
<p>Lo del software libre que confías a desconocidos por defecto va a seguir pasando.</p>
<p>Mientras WordPress.org no obligue a firmar el código y a auditar las transferencias de propiedad (que no parece que vaya a pasar pronto), <strong>la mejor defensa eres tú</strong>.</p>
<p>Aplícate al cuento, sigue estos consejos que te he dado, no pasa nada por ser un poco paranoico con los plugins críticos, dormirás más tranquilo, es mucho peor ser confiado con las cosas del comer.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ayudawp.com/ataques-cadena-suministro-plugins/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>¿Sirven de algo actualmente los plugins de SEO?</title>
		<link>https://ayudawp.com/sirven-actualmente-plugins-seo/</link>
					<comments>https://ayudawp.com/sirven-actualmente-plugins-seo/#comments</comments>
		
		<dc:creator><![CDATA[Fernando Tellado]]></dc:creator>
		<pubDate>Thu, 04 Jun 2026 06:28:00 +0000</pubDate>
				<category><![CDATA[IA + WordPress]]></category>
		<category><![CDATA[Opinión]]></category>
		<category><![CDATA[Plugins WordPress]]></category>
		<category><![CDATA[SEO / AEO / GEO / LLMO]]></category>
		<category><![CDATA[Tutoriales - Trucos]]></category>
		<category><![CDATA[WordPress.com]]></category>
		<category><![CDATA[WordPress.org]]></category>
		<category><![CDATA[AI Share & Summarize]]></category>
		<category><![CDATA[ChatGPT]]></category>
		<category><![CDATA[GEO]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Perplexity]]></category>
		<category><![CDATA[Principiante]]></category>
		<category><![CDATA[Rank Math]]></category>
		<category><![CDATA[SEOPress]]></category>
		<category><![CDATA[Sitemap]]></category>
		<category><![CDATA[VigIA]]></category>
		<category><![CDATA[Yoast]]></category>
		<guid isPermaLink="false">https://ayudawp.com/?p=158964</guid>

					<description><![CDATA[Tengo que decirte algo que incluso me cuesta un poquito:  la mayoría de lo que hacen los plugins de SEO ya no te sirve de nada.]]></description>
										<content:encoded><![CDATA[<p>Llevo casi 20 años recomendando plugins de SEO para WordPress. He escrito guías, he hecho comparativas, he dado charlas explicando cómo configurar prácticamente todos los plugins de SEO que han existido y existen.</p>
<p>Pues bien, ahora tengo que decirte algo que incluso me cuesta un poquito, y es que <strong>la mayoría de lo que hacen los plugins de SEO ya no te sirve de nada</strong>.</p>
<p>No es que sean malos plugins, algunos son muy buenos, el problema es que <strong>fueron diseñados para un mundo donde Google mostraba diez enlaces azules y tú competías por estar entre los tres primeros, ese mundo se está acabando</strong>.</p>
<p>Las <a href="https://ayudawp.com/ai-overviews-google/" target="_blank" rel="noopener ugc">AI Overviews</a> responden directamente en la página de resultados, ChatGPT y Perplexity dan respuestas sin que nadie visite tu web, y <strong>el tráfico orgánico lleva meses cayendo en casi todos los sectores</strong>. Se le ha llamado <a href="https://ayudawp.com/gran-desacoplamiento-google/">el gran desacoplamiento</a>, y si aún no lo has notado en tu Search Console dale tiempo.</p>
<blockquote><p>La pregunta que hay que hacerse no es si tu plugin de SEO funciona bien, <strong>la pregunta es si lo que hace tu plugin de SEO sigue siendo relevante</strong>.</p></blockquote>
<p>Vamos a repasar función por función, sin ánimo de hacer leña de nadie, pero también sin andarme con jilipolleces o medias tintas.</p>
<h2>Las funciones de los plugins de SEO que ya no pintan (casi) nada</h2>
<p>Vamos a ver juntos, funcionalidad a funcionalidad (casi todas) las <strong>herramientas que ofrecen los plugins de SEO que a día de hoy tiene poco o ningún sentido utilizar</strong>.</p>
<h3>Focus keyword y densidad de keywords</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158983 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/focus-keyword-density-aioseo-1200x724.jpg" alt="focus keyword density aioseo" width="1200" height="724" srcset="https://ayudawp.com/wp-content/uploads/2026/04/focus-keyword-density-aioseo-1200x724.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/focus-keyword-density-aioseo-768x463.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/focus-keyword-density-aioseo.jpg 1512w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Escribes una «<strong>palabra clave objetivo</strong>» y el plugin analiza tu contenido para decirte si la has usado suficientes veces, si aparece en el título, en el primer párrafo, en algún H2, en el texto alternativo de una imagen.</p>
<p>Es una de las funciones más reconocibles de cualquier plugin de SEO, es la base de los famosos <strong>semáforos</strong>.</p>
<h4>Por qué ya no tiene sentido</h4>
<p><strong>Los motores de búsqueda con IA no cuentan cuántas veces aparece una palabra</strong> en tu texto, entienden de qué va tu contenido por contexto semántico. Puedes escribir un artículo completo sobre «cómo migrar WordPress a otro hosting» sin usar esa frase exacta ni una sola vez, y tanto Google como ChatGPT van a entender perfectamente de qué hablas.</p>
<p>La <em>keyword density</em> fue una métrica útil cuando los algoritmos de búsqueda eran básicos y necesitaban pistas explícitas. <strong>Los LLMs (modelos grandes de lenguaje) procesan el significado, no la repetición</strong>. De hecho, un texto donde se fuerza la keyword suena peor, se lee peor y probablemente la IA lo descarte antes que uno escrito con naturalidad.</p>
<p>El concepto mismo de «<strong>optimizar para una keyword</strong>» se ha quedado viejo. <strong>Ahora se trata de responder a una intención, de cubrir un tema con profundidad</strong>, y eso no se mide contando palabras.</p>
<h4>¿Sirve aún de algo?</h4>
<p>Como recordatorio de que tu contenido debe tener un enfoque claro, sí, como herramienta de optimización real, no.</p>
<blockquote><p>Si necesitas un plugin para saber si tu artículo trata sobre lo que se supone que trata, el problema no es de SEO, es de redacción.</p></blockquote>
<h3>Plantillas de title y description</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158984 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/plantillas-meta-title-rewrite-seo-plugin-seopress-1200x421.jpg" alt="plantillas meta title rewrite seo plugin seopress" width="1200" height="421" srcset="https://ayudawp.com/wp-content/uploads/2026/04/plantillas-meta-title-rewrite-seo-plugin-seopress-1200x421.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/plantillas-meta-title-rewrite-seo-plugin-seopress-768x269.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/plantillas-meta-title-rewrite-seo-plugin-seopress-1536x539.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/plantillas-meta-title-rewrite-seo-plugin-seopress.jpg 1700w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Patrones automáticos para generar el title tag de tus entradas y páginas: <code>%%title%% | %%sitename%%</code>, <code>%%title%% - %%category%% | %%sitename%%</code>, etc.</p>
<h4>Por qué ya no tiene sentido</h4>
<p>La meta tag <code>title</code> sigue existiendo y Google sigue usándolo, pero cada vez lo reescribe más. <strong>Google modifica la tag <code>title</code> en más del 60% de los resultados de búsqueda</strong>, y para las IAs que procesan tu contenido el <code>title</code> es un dato más dentro de la página, no la señal principal que determina si te van a citar o no.</p>
<p>La idea de que un patrón como «<code>Keyword | Marca</code>» te daba ventaja competitiva ya no se sostiene. Lo que importa es <strong>que el título sea descriptivo y realista</strong>, y eso lo puedes escribir tú directamente en WordPress sin plantillas de por medio.</p>
<h4>¿Sirve aún de algo?</h4>
<p>Para sitios grandes con cientos o miles de páginas donde poner títulos coherentes a mano sería inviable (tiendas online, directorios) las plantillas siguen siendo prácticas. Para un blog o web de contenidos donde escribes cada título individualmente no aportan nada.</p>
<p>Yo mismo hace más de 2 años que no lo uso.</p>
<h3>Optimización de title y description</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158985 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/rewrite-metas-title-description-plugin-seo-framwork-1200x242.jpg" alt="rewrite metas title description plugin seo framwork" width="1200" height="242" srcset="https://ayudawp.com/wp-content/uploads/2026/04/rewrite-metas-title-description-plugin-seo-framwork-1200x242.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/rewrite-metas-title-description-plugin-seo-framwork-768x155.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/rewrite-metas-title-description-plugin-seo-framwork-1536x310.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/rewrite-metas-title-description-plugin-seo-framwork.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Un campo para escribir las metas <code>title</code> y <code>description</code> de cada entrada y página, con un <strong>contador de caracteres y sugerencias para hacerlas atractivas</strong> y que el usuario haga clic en tu resultado.</p>
<h4>Por qué ya no tiene sentido</h4>
<p>Dos razones principalmente.</p>
<p>La primera es que Google reescribe los <code>title</code> y <code>description</code> cuando le da la gana, y cada vez lo hace con más frecuencia. Ignora las que tú escribes más de la mitad de las veces y genera otras a partir del título y contenido de la página, así que gran <strong>parte del tiempo estás escribiendo para nadie</strong>.</p>
<p>La segunda, más gorda, pues resulta que <strong>si una AI Overview responde a la búsqueda directamente, el usuario ni siquiera llega a ver tu snippet</strong>. No hay clic que optimizar si no hay resultado que mostrar, tu <code>title</code> y tu <code>meta description</code> perfectamente redactadas se quedab ahí, invisibles, mientras <strong>la IA le da al usuario lo que busca sin que puedas hacer nada ni por impedirlo ni por modificar ese resultado</strong>.</p>
<h4>¿Sirve aún de algo?</h4>
<p>Para las <strong>búsquedas donde aún no aparecen AI Overviews</strong> (que todavía las hay, más en español) siguen teniendo <strong>cierta utilidad</strong>. Pero el tiempo que dedicas a escribir etiquetas <code>title</code> y  <code>meta descriptions</code> personalizadas para cada artículo <strong>tiene un retorno cada vez menor</strong>.</p>
<p>Si escribes mucho contenido probablemente <strong>no merece la pena hacerlo a mano para cada entrada</strong>. Un título de tu publicación bien elegido y una <code>meta description</code> decente generada automáticamente <a href="https://ayudawp.com/tag/extracto/" target="_blank" rel="noopener ugc">a partir de tu extracto o primer párrafo</a>, sobre todo si los defines conscientemente de su valor, suelen ser más que suficiente.</p>
<h3>Puntuaciones de legibilidad</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158986 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/analisis-legibilidad-yoast-seo-1200x798.jpg" alt="analisis legibilidad yoast seo" width="1200" height="798" srcset="https://ayudawp.com/wp-content/uploads/2026/04/analisis-legibilidad-yoast-seo-1200x798.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/analisis-legibilidad-yoast-seo-768x511.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/analisis-legibilidad-yoast-seo.jpg 1494w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Análisis del texto con métricas tipo <em>Flesch-Kincaid</em>, que miden la <strong>longitud de frases, uso de voces pasivas, variedad de conectores, complejidad de vocabulario, etc</strong>. El típico semáforo que te dice si tu texto es «<strong>fácil de leer</strong>» o no.</p>
<h4>Por qué ya no tiene sentido</h4>
<p>Estas puntuaciones <strong>miden la legibilidad mecánica no la calidad del contenido</strong>. Un artículo técnico sobre configuración de servidores puede tener frases largas y vocabulario especializado y ser exactamente lo que tanto un lector experto como una IA necesitan.</p>
<p>La IA no penaliza las frases largas, <strong>la IA no busca textos «fáciles» busca textos que respondan bien a una pregunta</strong>, con autoridad y precisión.</p>
<p>Además, estas métricas de legibilidad se diseñaron para textos educativos en inglés. Su aplicación al español siempre ha sido discutible, y <strong>en el contexto de optimización para IAs es directamente irrelevante</strong>.</p>
<h4>¿Sirve aún de algo?</h4>
<p>Escribir claro siempre es buena idea pero no necesitas un plugin que te ponga un semáforo para eso. Si llevas tiempo escribiendo en la web ya sabes cuándo un párrafo es demasiado denso. <strong>Fíate más de tu criterio y de la respuesta de tus lectores que de una fórmula matemática</strong> pensada para otro idioma y otra época.</p>
<h3>Mapas del sitio XML de los plugins SEO</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158987 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/sitemap-rank-math-seo-1200x1080.jpg" alt="sitemap rank math seo" width="1200" height="1080" srcset="https://ayudawp.com/wp-content/uploads/2026/04/sitemap-rank-math-seo-1200x1080.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/sitemap-rank-math-seo-768x691.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/sitemap-rank-math-seo-1536x1382.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/sitemap-rank-math-seo.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Generación de <strong>sitemaps XML personalizados</strong>, con opciones para incluir o excluir tipos de contenido, establecer prioridades y frecuencias de actualización.</p>
<h4>Por qué ya no tiene sentido</h4>
<p>WordPress genera mapas del sitio nativos desde la versión 5.5. Funcionan bien, Google los lee sin problemas y, lo más importante ahora, <strong>los bots de IA también los devoran</strong>. Si miras los logs de tu servidor con <a href="https://ayudawp.com/vigia/">VigIA</a> verás que <strong>los crawlers de ChatGPT, Claude, Perplexity y compañía van directos a tu sitemap</strong> para saber qué contenido tienes y por dónde empezar a rastrear.</p>
<p>Los plugins de SEO lo primero que hacen es desactivar los sitemaps nativos de WordPress y sustituirlos por los suyos. ¿Por qué?, pues porque así te crean una dependencia, para que si desactivas el plugin te quedes sin mapa del sitio (en teoría).</p>
<blockquote><p>Para la inmensa mayoría de sitios WordPress el mapa del sitio nativo de WordPress es más que suficiente.</p></blockquote>
<h4>¿Sirve aún de algo?</h4>
<p>Si necesitas <strong>personalizar el mapa del sitio que ya crea WordPress</strong> (excluir ciertas taxonomías, ajustar qué se incluye, añadir tipos de contenido personalizados), puedes hacerlo sin un plugin de SEO enorme. <a href="https://es.wordpress.org/plugins/native-sitemap-customizer/">Native Sitemap Customizer</a> te permite configurar los sitemaps nativos de WordPress sin desactivarlos ni sustituirlos por otros, que es justo lo que <strong>tiene sentido ahora que los bots de IA dependen tanto de ellos</strong>.</p>
<h3>Editor de robots.txt</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158988 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/editor-robots-txt-aioseo-1200x864.jpg" alt="editor robots txt aioseo" width="1200" height="864" srcset="https://ayudawp.com/wp-content/uploads/2026/04/editor-robots-txt-aioseo-1200x864.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/editor-robots-txt-aioseo-768x553.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/editor-robots-txt-aioseo-1536x1106.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/editor-robots-txt-aioseo.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Una interfaz para editar el archivo <code>robots.txt</code> desde el panel de WordPress, con reglas para permitir o bloquear el acceso de bots a distintas partes de tu sitio.</p>
<h4>Por qué <del>ya no</del> nunca tiene sentido</h4>
<p>WordPress genera un <code>robots.txt</code> virtual (accesible en <code>/?robots=1</code>) que para la mayoría de sitios es suficiente. Los plugins de SEO te ofrecen un editor gráfico que parece más complejo de lo necesario, pero sobre todo es que <strong>¡es un puñetero archivo de texto!</strong>.</p>
<p>Estoy aún esperando que alguien me explique <strong>qué sentido tiene usar un plugin para algo que no cambia</strong> a diario, ni todas las semanas, ni todos los meses, algo que normalmente configuras una vez y lo repasas cada mucho tiempo.</p>
<p>Pero la cosa es aún peor, porque hay plugins de SEO que, ahora, tratan de justificar su inclusión bajo el pretexto de que así puedes <em>bloquear</em> a los bots de IA que no quieras, a GPTBot, ClaudeBot, PerplexityBot, Bytespider, CCBot y otros crawlers de IA.</p>
<p>Pues no, resulta que <strong>los <code>disallow</code> en el <code>robots.txt</code> son meramente declarativos, y no es que los bots de IA no los hagan caso, es que ni Google los tiene en cuenta</strong>.</p>
<p>¿No te lo crees? Instala y activa VigIA, dale a <em>bloquear</em> mediante <code>robots.txt</code> a cualquier rastreador, vas a descubrir (también tiene una utilidad para ver eso) que pasan totalmente, que <strong>lo único que los detiene es bloquearlos mediante PHP</strong>.</p>
<h4>¿Sirve aún de algo?</h4>
<p>El editor en sí es una comodidad menor, para algo que no hace falta. Lo que de verdad necesitas es informarte sobre <a href="https://ayudawp.com/evitar-chatgpt-rastreo-contenido/">qué crawlers de IA existen y cuáles quieres permitir</a>, y eso es una decisión estratégica que ningún plugin puede tomar por ti. Un simple fichero <code>robots.txt</code> editado a mano o con un plugin ligero cubre cualquier necesidad que pienses que necesitas más que de sobra.</p>
<h3>Etiquetas canónicas</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158989 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/canonical-seopress-1200x284.jpg" alt="canonical seopress" width="1200" height="284" srcset="https://ayudawp.com/wp-content/uploads/2026/04/canonical-seopress-1200x284.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/canonical-seopress-768x182.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/canonical-seopress-1536x364.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/canonical-seopress.jpg 1656w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Gestión automática de la etiqueta <code>rel="canonical"</code> para <strong>evitar problemas de contenido duplicado</strong>, con opción de personalizar la URL canónica de cada entrada.</p>
<h4>Por qué ya no tiene sentido</h4>
<p>WordPress genera etiquetas canónicas de forma nativa desde casi siempre y para el 90% de los sitios la canónica automática que pone WordPress es correcta. Los casos donde necesitas personalizar la canónica manualmente (contenido sindicado, variaciones de URL con parámetros, contenido duplicado entre dominios) son bastante específicos y <strong>no justifican un plugin de SEO completo</strong>.</p>
<p>Además, <strong>a los LLMs la etiqueta canónica les da bastante igual</strong>. Procesan el contenido que encuentran, no las instrucciones de indexación pensadas para Googlebot.</p>
<h4>¿Sirve aún de algo?</h4>
<p>Si tienes un sitio con <strong>problemas reales de contenido duplicado</strong> (tienda con productos en varias categorías, contenido publicado en varios dominios), sí puede ser útil. Para un blog o web de contenidos normal, lo que hace WordPress solo es suficiente.</p>
<h3>Meta tags para redes sociales (Open Graph, Twitter Cards)</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158990 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/open-graph-plugin-seopress-1200x569.jpg" alt="open graph plugin seopress" width="1200" height="569" srcset="https://ayudawp.com/wp-content/uploads/2026/04/open-graph-plugin-seopress-1200x569.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/open-graph-plugin-seopress-768x364.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/open-graph-plugin-seopress-1536x728.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/open-graph-plugin-seopress.jpg 1802w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Generación automática de las etiquetas Open Graph y Twitter Cards para <strong>que cuando alguien comparta tu contenido en redes sociales aparezca con título, descripción e imagen</strong> correctos.</p>
<h4>Por qué ya no tiene sentido</h4>
<p>El tráfico desde redes sociales también va en caída libre para la mayoría de webs, nos empeñamos, pero la realidad es que la gente lee lo que pones en su red, y raramente visitan tu web para saber más que lo que les ofrece el titular, <strong>¿no te has dado cuenta de que la gente comenta tus contenidos en sus redes, no en tu web? </strong></p>
<p>Facebook reduce el alcance orgánico cada trimestre, Twitter/X es un caos algorítmico, y las plataformas donde sí se comparte contenido (LinkedIn, Threads) cada vez priorizan más el contenido nativo sobre los enlaces externos.</p>
<blockquote><p>Que tu enlace se vea bonito cuando alguien lo comparte está bien, pero <strong>el impacto real en tráfico es mínimo para la mayoría de sitios</strong>.</p></blockquote>
<h4>¿Sirve aún de algo?</h4>
<p>Si las redes sociales son una fuente de tráfico importante para ti sí merece la pena tenerlo. Pero <strong>muchos temas WordPress ya generan Open Graph básico sin necesidad de un plugin de SEO</strong> aparte.</p>
<p>Y si no compartes activamente en redes o tu audiencia no llega por ahí, es <strong>una función que ocupa espacio en tu base de datos, aumentan el peso de tu HTML y no te aporta nada</strong>.</p>
<h3>Marcado Schema automático</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158991 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/marcado-schema-automatico-yoast-seo-1200x327.jpg" alt="marcado schema automatico yoast seo" width="1200" height="327" srcset="https://ayudawp.com/wp-content/uploads/2026/04/marcado-schema-automatico-yoast-seo-1200x327.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/marcado-schema-automatico-yoast-seo-768x209.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/marcado-schema-automatico-yoast-seo-1536x418.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/marcado-schema-automatico-yoast-seo.jpg 1594w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Generación automática de <strong>datos estructurados</strong> (Schema.org) para artículos, páginas, breadcrumbs, organización, autor, etc. Los plugins más avanzados añaden tipos como FAQ, HowTo, productos, recetas, eventos.</p>
<h4>Por qué ya no tiene sentido</h4>
<p>Bueno, esta función <strong>sí tiene sentido, pero no como la implementan la mayoría de plugins de SEO </strong>por un par de motivos, a saber:</p>
<ol>
<li>El Schema que añaden por defecto (<code>Article</code>, <code>BreadcrumbList</code>, <code>Organization</code>) es tan básico que apenas aporta diferenciación. <strong>Google ya es capaz de entender que un artículo es un artículo sin que se lo digas con JSON-LD</strong>.</li>
<li>El Schema que de verdad te puede ayudar a aparecer en AI Overviews y en respuestas de IAs (<code>FAQ</code>, <code>HowTo</code>, datos de producto con precio e inventario, reseñas con puntuación, datos con fuentes citadas) requiere un <strong>trabajo manual y específico que los plugins de SEO automatizan mal o directamente no ofrecen</strong> en sus versiones gratuitas.</li>
</ol>
<h4>¿Sirve aún de algo?</h4>
<p>Los datos estructurados <strong>siguen siendo importantes, quizá más que nunca, pero la forma de implementarlos tiene que cambiar</strong>.</p>
<p>Necesitas <a href="https://ayudawp.com/json-ld/">JSON-LD bien pensado y específico para tu contenido</a>, no el Schema genérico que un plugin genera automáticamente para todos tus artículos o páginas.</p>
<blockquote><p>Los tipos de datos que interesan a las IAs son los que <strong>aportan información práctica verificable</strong>, como precios, fechas, pasos de un proceso, respuestas a preguntas concretas.</p></blockquote>
<h3>Auditorías y puntuaciones SEO</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158992 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/analisis-seo-yoast-1200x675.jpg" alt="analisis seo yoast" width="1200" height="675" srcset="https://ayudawp.com/wp-content/uploads/2026/04/analisis-seo-yoast-1200x675.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/analisis-seo-yoast-768x432.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/analisis-seo-yoast-1536x864.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/analisis-seo-yoast.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Un panel con una puntuación global de tu SEO, alertas sobre «problemas» (entradas sin <code>meta description</code>, títulos demasiado largos, imágenes sin texto alternativo, enlaces rotos) y <strong>recomendaciones para mejorar</strong>.</p>
<h4>Por qué ya no tiene sentido</h4>
<p>Estas auditorías <strong>miden lo que era importante en 2018</strong>. Te dicen que tienes 47 entradas sin <code>meta description</code> y tú sales corriendo a escribirlas todas, pero <strong>ninguna de esas métricas tiene correlación con que aparezcas o no en una AI Overview, ni con que ChatGPT te cite como fuente</strong>.</p>
<p>Que sí, que nos seguimos empeñando, que los hábitos no se curan en unos meses, pero en serio, pasa ya de eso, no sirve de nada, solo te hace perder tiempo y vida.</p>
<p><strong>Lo que no te dice ninguna auditoría de plugin SEO</strong>:</p>
<ul>
<li>Si los bots de IA están rastreando tu sitio (y cuáles).</li>
<li>Si tu contenido aparece citado en respuestas de IAs.</li>
<li>Si tu Schema es el adecuado para los formatos de respuesta de AI Overviews.</li>
<li>Si tu sitemap está siendo consumido correctamente por los crawlers de IA.</li>
<li>Si la estructura de tu contenido facilita que una IA extraiga una respuesta directa.</li>
</ul>
<p>Básicamente, <strong>te auditan para un juego que se está terminando y te ignoran el que está empezando</strong>.</p>
<h4>¿Sirve aún de algo?</h4>
<p>Como lista de <strong>revisión de higiene técnica básica</strong>, como imágenes sin <code>alt</code>, enlaces rotos o títulos duplicados sigue siendo útil, pero <strong>no necesitas un plugin de SEO para eso</strong>.</p>
<p>Hay plugins específicos más ligeros para cada una de esas tareas, y la mayoría se pueden comprobar con herramientas externas gratuitas.</p>
<h3>SEO automático con IA integrada en el plugin</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158993 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/seo-automatico-ia-1200x472.jpg" alt="seo automatico ia" width="1200" height="472" srcset="https://ayudawp.com/wp-content/uploads/2026/04/seo-automatico-ia-1200x472.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/seo-automatico-ia-768x302.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/seo-automatico-ia-1536x604.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/seo-automatico-ia.jpg 1896w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Los plugins de SEO más recientes han añadido <strong>funciones de IA para generar metas, títulos, esquemas de contenido e incluso artículos completos</strong> desde el propio editor de WordPress.</p>
<h4>Por qué ya no tiene sentido</h4>
<p>Aquí la cosa tiene miga, porque usar una IA para generar contenido que luego va a ser evaluado por otra IA es, como mínimo, un círculo un poco absurdo. Pero el problema no es ese, que también, hay más, lo peor.</p>
<p>El problema es que <strong>estas funciones de IA integradas en plugins de SEO son básicamente un formulario de conexión para la API de OpenAI o de otro proveedor</strong>, con un prompt predefinido.</p>
<p>Hacen <strong>lo mismo que podrías hacer directamente en ChatGPT, Claude o cualquier otro asistente</strong>, pero con menos control y normalmente con un coste adicional (créditos de IA incluidos en el plan premium del plugin).</p>
<p>Además, el contenido generado masivamente por IA sin edición humana es exactamente el tipo de <strong>contenido que Google está penalizando y que las IAs van a ignorar como fuente</strong>.</p>
<p>Si generas 50 artículos con la IA de tu plugin de SEO y los publicas tal cual, <strong>no estás haciendo SEO, estás haciendo ruido, publicando basura y tratando de engañar a los algoritmos (imposible) y a tus lectores (imperdonable)</strong>.</p>
<h4>¿Sirve aún de algo?</h4>
<p>Como ayuda puntual para generar borradores o ideas sí, pero para eso <strong>ya tienes herramientas de IA dedicadas que son mucho más potentes y flexibles</strong>.</p>
<blockquote><p>Pagar un plan premium de un plugin de SEO para acceder a una IA limitada no tiene ningún sentido cuando puedes usar directamente cualquier LLM con prompts bien hechos.</p></blockquote>
<h3>Sugerencias de enlazado interno</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158994 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/sugerencias-enlazado-interno-seopress-1200x272.jpg" alt="sugerencias enlazado interno seopress" width="1200" height="272" srcset="https://ayudawp.com/wp-content/uploads/2026/04/sugerencias-enlazado-interno-seopress-1200x272.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/sugerencias-enlazado-interno-seopress-768x174.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/sugerencias-enlazado-interno-seopress-1536x349.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/sugerencias-enlazado-interno-seopress.jpg 1886w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<h4>Qué te ofrecen</h4>
<p>Análisis de tu contenido para sugerirte enlaces internos a otras entradas relacionadas, normalmente basándose en coincidencias de palabras clave encontradas en el texto.</p>
<h4>Por qué ya no tiene sentido</h4>
<p>El enlazado interno <strong>sigue siendo útil para que los usuarios naveguen por tu sitio y que los bots descubran contenido</strong>. Eso no ha cambiado, <strong>lo que sí ha cambiado es la lógica de estas sugerencias</strong>.</p>
<p>Los plugins te sugieren enlazar a posts que contienen la misma keyword, pero <strong>las IAs no procesan tu sitio siguiendo enlaces como hacía Googlebot</strong>.</p>
<p><strong>Los bots de IA van al mapa del sitio, lo devoran</strong> página a página, <strong>identifican las URLs que les interesan y rastrean cada una de forma independiente</strong>.</p>
<p>El «link juice» que se distribuía por enlaces internos pierde relevancia en un modelo donde <strong>el bot no navega tu sitio, se lo descarga prácticamente</strong>.</p>
<h4>¿Sirve aún de algo?</h4>
<p>Para la experiencia del usuario humano <strong>enlazar bien sigue siendo una buena práctica</strong>, pero <strong>las sugerencias automáticas basadas en keywords son demasiado simplistas</strong>.</p>
<blockquote><p><strong>Un buen enlazado interno se hace pensando en el lector, no en un algoritmo</strong>, y eso <strong>es trabajo editorial que ningún plugin puede sustituir</strong>.</p></blockquote>
<h2>Lo que sí tiene sentido ahora (con o sin plugins de SEO)</h2>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-154009 size-full" src="https://ayudawp.com/wp-content/uploads/2025/04/seo-llmo-e1757953674258.jpg" alt="seo llmo" width="1200" height="690" srcset="https://ayudawp.com/wp-content/uploads/2025/04/seo-llmo-e1757953674258.jpg 1200w, https://ayudawp.com/wp-content/uploads/2025/04/seo-llmo-e1757953674258-768x442.jpg 768w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<p>Después de habernos puesto serios con lo que ya no funciona, <strong>sería injusto no hablar de lo que sí marca la diferencia a día de hoy</strong> (no sé mañana).</p>
<p>Algunas cosas las puedes hacer con <strong>plugins específicos</strong>, otras con <strong>el propio WordPress</strong>, y otras son <strong>pura estrategia y redacción</strong>.</p>
<h3>Mapas del sitio bien configurados</h3>
<p><a href="https://ayudawp.com/?attachment_id=158996" rel="nofollow"><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158996 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/sitemap-wordpress-personalizar-1200x739.jpg" alt="sitemap wordpress personalizar" width="1200" height="739" srcset="https://ayudawp.com/wp-content/uploads/2026/04/sitemap-wordpress-personalizar-1200x739.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/sitemap-wordpress-personalizar-768x473.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/sitemap-wordpress-personalizar-1536x946.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/sitemap-wordpress-personalizar.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></a></p>
<p>Si hay algo que los bots de IA hacen nada más llegar a tu sitio es ir al <strong>Sitemap XML</strong>, <strong>es su mapa del tesoro, el índice de todo lo que tienes</strong>.</p>
<p>Si tu sitemap está inflado con páginas irrelevantes, taxonomías vacías o contenido que no quieres que rastreen, les estás complicando la vida. Igualmente, si olvidas incluir tus mejores contenidos igual ni los descubren, en serio, no pierden el tiempo.</p>
<p><strong>WordPress genera un buen mapa del sitio sin necesidad de plugins</strong>, limpio, completo, que no tienes que mantener ni depende de otros. Luego con <a href="https://es.wordpress.org/plugins/native-sitemap-customizer/">Native Sitemap Customizer</a> puedes ajustar exactamente qué se incluye y qué no sin necesidad de instalar un plugin de SEO que sustituya el sitemap que ya tienes gratis por el suyo.</p>
<p>Esto es algo que ahora mismo poca gente tiene en cuenta y que <strong>tiene un impacto directo en cómo las IAs descubren y priorizan tu contenido</strong>.</p>
<h3>Monitorizar qué hacen las IAs con tu contenido</h3>
<p><a href="https://ayudawp.com/?attachment_id=158997" rel="nofollow"><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158997 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/analitica-inteligencia-artificial-wordpress-1200x566.jpg" alt="analitica inteligencia artificial wordpress" width="1200" height="566" srcset="https://ayudawp.com/wp-content/uploads/2026/04/analitica-inteligencia-artificial-wordpress-1200x566.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/analitica-inteligencia-artificial-wordpress-768x362.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/analitica-inteligencia-artificial-wordpress-1536x725.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/analitica-inteligencia-artificial-wordpress.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></a></p>
<p>¿Cómo sabes si las IAs están rastreando tu sitio, cuáles lo hacen, con qué frecuencia y qué contenido se llevan?</p>
<p>Tu plugin de SEO no te lo dice, Google Analytics tampoco las identifica al detalle, Search Console te cuenta lo de Google, pero no lo de ChatGPT, Claude, Perplexity ni el resto. El único que te muestra algo es Bing.</p>
<p>Para eso necesitas algo como <a href="https://ayudawp.com/vigia/">VigIA</a>, que <strong>monitoriza los bots de IA que visitan tu WordPress, te muestra cuáles rastrean, cuándo, qué URLs se llevan y cómo se comportan</strong>. <strong>Es gratis</strong>, te da una información valiosísima y muchas <strong>herramientas que te ayudan a reforzar tu visibilidad para este nuevo modo en el que ya funciona Internet</strong>.</p>
<p>Sin esta información estás completamente a ciegas, es como hacer SEO clásico sin Search Console, impensable. Pues lo mismo, pero para el nuevo escenario.</p>
<h3>La cápsula de respuesta</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158197 size-full" src="https://ayudawp.com/wp-content/uploads/2026/02/posicionar-ai-overviews-google-vista-previa-con-ia.jpg" alt="posicionar ai overviews google vista previa con ia" width="1200" height="800" srcset="https://ayudawp.com/wp-content/uploads/2026/02/posicionar-ai-overviews-google-vista-previa-con-ia.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/02/posicionar-ai-overviews-google-vista-previa-con-ia-768x512.jpg 768w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<p>Esto no lo hace ningún plugin, es pura redacción, y es <strong>probablemente lo que más impacta en si apareces o no en una AI Overview</strong>.</p>
<p>Las IAs generativas, cuando buscan una fuente para responder a una pregunta, tienden a extraer la respuesta de las primeras frases de un contenido que la responda de forma directa y concisa.</p>
<blockquote><p>Si tu artículo empieza con tres párrafos de contexto antes de ir al grano la IA se va a otro sitio.</p></blockquote>
<p>La técnica es sencilla, simplemente empieza cada artículo con una respuesta directa a la pregunta que el artículo responde, en las primeras 50 a 70 palabras. Luego desarrolla todo lo que quieras. Esa «cápsula» es <strong>lo que la IA puede extraer como respuesta</strong>, y <a href="https://ayudawp.com/ai-overviews-google/">es <strong>una de las señales que más peso tienen para aparecer en AI Overviews</strong></a>.</p>
<h3>E-E-A-T real, no de formulario</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158998 size-full" src="https://ayudawp.com/wp-content/uploads/2026/04/eeat-vs-ia.jpg" alt="eeat vs ia" width="1200" height="800" srcset="https://ayudawp.com/wp-content/uploads/2026/04/eeat-vs-ia.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/eeat-vs-ia-768x512.jpg 768w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<p>Los plugins de SEO te ponen un campo para rellenar el nombre del autor y poco más. Ahora bien, las señales de <em><strong>E</strong>xperience </em>(Experiencia), <em><strong>E</strong>xpertise </em>(Especialización), <em><strong>A</strong>uthoritativeness </em>(Autoridad) y <em><strong>T</strong>rustworthiness </em>(Fiabilidad) que Google y las IAs buscan <strong>no se configuran en un campo de texto</strong>.</p>
<p><strong>Se construyen con contenido que demuestre experiencia real</strong> (datos propios, ejemplos de primera mano, capturas de tu propio trabajo), con una presencia de marca coherente en la web, con menciones y citas en otros sitios, con <strong>autoría verificable</strong> y con un historial de <strong>contenido de calidad</strong> mantenido en el tiempo.</p>
<blockquote><p>Ningún plugin te da E-E-A-T, es algo que se gana publicando buen contenido durante años, y eso no hay atajo que lo sustituya.</p></blockquote>
<h3>JSON-LD con intención, no por defecto</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-157780 size-full" src="https://ayudawp.com/wp-content/uploads/2026/01/json-ld-wordpress-seo-geo.jpg" alt="json ld wordpress seo geo" width="1200" height="631" srcset="https://ayudawp.com/wp-content/uploads/2026/01/json-ld-wordpress-seo-geo.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/01/json-ld-wordpress-seo-geo-768x404.jpg 768w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<p>Ya he mencionado que el Schema genérico que añaden los plugins de SEO aporta poco.</p>
<p>Lo que sí funciona es <a href="https://ayudawp.com/json-ld/">implementar JSON-LD específico y pensado para tu contenido</a>, con preguntas frecuentes con respuestas directas, pasos de un proceso con tiempos y herramientas, datos de producto con precio, disponibilidad y valoraciones, información de organización con datos de contacto verificables.</p>
<p>Este tipo de datos estructurados es lo que las IAs pueden <strong>interpretar directamente como información basada en hechos y verificable</strong>, y lo que marca la diferencia entre ser citado como fuente o ser ignorado.</p>
<h3>Gestión de bots de IA</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-158999 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/bloqueo-bots-ia-wordpress-1200x903.jpg" alt="bloqueo bots ia wordpress" width="1200" height="903" srcset="https://ayudawp.com/wp-content/uploads/2026/04/bloqueo-bots-ia-wordpress-1200x903.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/bloqueo-bots-ia-wordpress-768x578.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/bloqueo-bots-ia-wordpress-1536x1156.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/bloqueo-bots-ia-wordpress.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<p>Decidir <strong>qué crawlers de IA pueden acceder a tu contenido y cuáles no</strong> es una decisión estratégica que ahora mismo los plugins de SEO ni mencionan.</p>
<p>Cada rastreador de IA funciona de manera diferente, y tú tienes que decidir si quieres que rastreen tu contenido (y potencialmente te citen como fuente) o si prefieres bloquearlo.</p>
<p>Esto te dirán que se gestina desde <code>robots.txt</code>, pero es mentira, no sirve de nada, no le hacen ni caso como ya te he comentado. <strong>Solo se les puede poner freno desde las cabeceras HTTP</strong> de tu web/servidor, si de verdad quieres bloquearlos, no desde un plugin de SEO.</p>
<p>Si quieres hacerlo fácil en <a href="https://ayudawp.com/vigia/" target="_blank" rel="noopener ugc">VigIA</a> puedes <strong>bloquear los que tú decidas a golpe de clic</strong>, y además después de haber podido <strong>analizar qué rastrean y cómo lo hacen</strong>, que es el modo correcto, actuando con información.</p>
<h3>Visibilidad ante las IAs</h3>
<p><img loading="lazy" decoding="async" class="sombra alignnone wp-image-159000 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/04/analisis-visibilidad-ia-wordpress-1200x819.jpg" alt="analisis visibilidad ia wordpress" width="1200" height="819" srcset="https://ayudawp.com/wp-content/uploads/2026/04/analisis-visibilidad-ia-wordpress-1200x819.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/04/analisis-visibilidad-ia-wordpress-768x524.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/04/analisis-visibilidad-ia-wordpress-1536x1048.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/04/analisis-visibilidad-ia-wordpress.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></p>
<p>El SEO tradicional medía posiciones en Google, El nuevo SEO, <a href="https://ayudawp.com/tag/geo/" target="_blank" rel="noopener ugc">el GEO</a> que le llaman ahora, debería <strong>medir si las IAs te conocen, si te citan y si te recomiendan</strong>. Esto es lo que llamo <a href="https://ayudawp.com/visibilidad-ia/">visibilidad IA</a>, y es un terreno donde casi todo está por hacer.</p>
<p>Herramientas como <a href="https://ayudawp.com/vigia/">VigIA</a> te dan el primer paso (<strong>saber qué bots te rastrean</strong>), y plugins como <a href="https://ayudawp.com/ai-share-summarize-analitica-clics/">AI Share &amp; Summarize</a> te ayudan a <strong>facilitar que se compartan tus contenidos</strong> y, además, también a <strong>medir la interacción real</strong> de los usuarios con tu contenido más allá del clic tradicional.</p>
<p>La métrica definitiva de «<strong>¿me citan las IAs?</strong>» aún está en pañales y <strong>es algo que ningún plugin de SEO</strong> aborda, y si te dicen que lo hace sospecha, y <strong>si te quieren cobrar por ello además te quieren robar</strong>.</p>
<h2>Entonces, ¿desinstalo mi plugin de SEO?</h2>
<p><strong>No necesariamente</strong>, pero sí tienes que aplicar sentido común y ser realista sobre qué parte del plugin usas realmente y qué parte es realmente innecesaria.</p>
<p>Si lo único que usas de tu plugin de SEO es el campo de <code>meta description</code>, el semáforo de keywords y la puntuación de legibilidad, pues sí, probablemente puedes quitarlo y no va a pasar nada malo. WordPress hace lo básico solo, y <strong>lo que de verdad necesitas ahora no viene de ahí</strong>.</p>
<p>Si usas <strong>funciones más avanzadas</strong> como redirecciones, Schema personalizado o gestión de contenido duplicado en un sitio grande, quizá te compense mantenerlo, pero siendo <strong>consciente de que estás usando un 10% de lo que ofrece y el otro 90% es grasa</strong>. De hecho, si solo quieres las redirecciones, <a href="https://ayudawp.com/tag/redirection/" target="_blank" rel="noopener ugc">mejor usa Redirection</a>, es mejor que cualquier herramienta de los plugins de SEO, es gratis, es ligero, es perfecto.</p>
<p><strong>Lo que seguro que no tiene sentido es pagar por un plan premium de un plugin de SEO</strong> solo para acceder a la generación de contenido por IA o a funciones de «<strong>SEO Score</strong>» que miden parámetros que ya <strong>no determinan tu visibilidad</strong>.</p>
<p>Y, por cierto, igual estás pensando que te estoy hablando de sustituir un solo plugin de SEO por varios plugins, y si no es mejor tener un único plugin que varios, pues no, <strong>el problema nunca es la cantidad de plugins, el problema es sobrecargar tu web con código que no necesitas</strong>.</p>
<p><strong>Es mejor tener 5 plugins pequeños que cada uno hace lo suyo y bien que un monstruo</strong> que carga montones de recursos, scripts y estilos de los que solo sirven de algo un pequeño porcentaje.</p>
<p>El juego ha cambiado y las reglas son otras. La herramienta que necesitas ya no es un plugin que te ponga semáforos en verde, sino una <strong>estrategia clara de contenido, datos estructurados bien pensados, sitemaps limpios y la capacidad de saber qué están haciendo las IAs con tu web</strong>.</p>
<p>Si quieres profundizar en cómo preparar tu WordPress para este nuevo escenario, te recomiendo empezar por la <a href="https://ayudawp.com/seo-ia/">guía de SEO para IAs generativas</a> y por el artículo sobre <a href="https://ayudawp.com/ai-overviews-google/">cómo posicionar en AI Overviews</a>. Ahí está lo que de verdad te puede ayudar en algo a <strong>seguir en el <em>bisnes</em>, tener presencia, existir en Internet</strong>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ayudawp.com/sirven-actualmente-plugins-seo/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>¡Ah! ¿pero se podía personalizar el widget de actividad del escritorio de WordPress? … y yo sin saberlo</title>
		<link>https://ayudawp.com/personalizar-widget-actividad-escritorio/</link>
					<comments>https://ayudawp.com/personalizar-widget-actividad-escritorio/#respond</comments>
		
		<dc:creator><![CDATA[Fernando Tellado]]></dc:creator>
		<pubDate>Wed, 03 Jun 2026 06:28:40 +0000</pubDate>
				<category><![CDATA[Programación + WordPress]]></category>
		<category><![CDATA[Tutoriales - Trucos]]></category>
		<category><![CDATA[WordPress.com]]></category>
		<category><![CDATA[WordPress.org]]></category>
		<category><![CDATA[Avanzado]]></category>
		<category><![CDATA[Escritorio]]></category>
		<category><![CDATA[Experto]]></category>
		<category><![CDATA[widgets]]></category>
		<guid isPermaLink="false">https://ayudawp.com/?p=159372</guid>

					<description><![CDATA[¿Sabes que puedes personalizar el widget de actividad del escritorio de WordPress? Te cuento todas las posibilidades, y si merece la pena…]]></description>
										<content:encoded><![CDATA[<p>El widget de actividad del escritorio de WordPress es uno de esos elementos que la mayoría de gente da por sentado. Te muestra las cinco últimas entradas publicadas, las cinco próximas programadas y los cinco últimos comentarios, y ahí se queda.</p>
<p>Para una web grande con flujo editorial, una tienda con productos que se agotan o un sitio con tipos de contenido personalizados, esa información por defecto se queda corta enseguida.</p>
<p>La buena noticia es que se puede personalizar mucho más de lo que parece, y se me ocurren hasta cuatro formas de hacerlo, de la menos invasiva a la más completa, y cada una sirve para un caso distinto. Vamos a verlas todas con código que puedes copiar y pegar.</p>
<h2>Antes de empezar: lo que el widget muestra por defecto</h2>
<p>Si abres el escritorio de WordPress de cualquier instalación ahí lo tienes, es esa caja que pone «Actividad» y muestra tres bloques bien diferenciados:</p>
<ul>
<li><strong>Próximas publicaciones</strong>, con las cinco siguientes entradas programadas, ordenadas por fecha de salida.</li>
<li><strong>Publicaciones recientes</strong>, con las cinco últimas entradas que ya están en la web.</li>
<li><strong>Comentarios recientes</strong>, con los últimos cinco recibidos, da igual su estado (aprobados, pendientes, spam).</li>
</ul>
<p><a href="https://ayudawp.com/?attachment_id=159433" rel="nofollow"><img loading="lazy" decoding="async" class="sombra alignnone wp-image-159433 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp-por-defecto-1200x754.jpg" alt="" width="1200" height="754" srcset="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp-por-defecto-1200x754.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp-por-defecto-768x482.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp-por-defecto-1536x965.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp-por-defecto.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></a></p>
<p>En una web personal con poco movimiento sirve para hacerse una idea rápida, pero en cuanto tienes un flujo editorial activo varios autores escribiendo o una tienda online, esa información se queda corta, o larga, o no del todo útil, según tu necesidad.</p>
<p>Igual quieres ver solo lo que va a salir esta semana, solo los comentarios pendientes de moderar, los productos que se han quedado sin stock o las entradas que están pendientes de revisión. Cada caso pide algo distinto.</p>
<p><strong>Para personalizarlo te planteo cuatro formas</strong> de abordarlo como te decía, <strong>de más sencilla a más completa</strong>.</p>
<p>La primera solo cambia las entradas que aparecen y se monta con tres líneas, la segunda reescribe el contenido del widget original sin tocar la caja, la tercera quita el widget y mete uno tuyo de cero, y la última un formulario para que el propio usuario lo configure desde el escritorio.</p>
<p>Al final del artículo también verás plugins que hacen lo mismo sin código, por si no te apetece pelearte con el editor de código.</p>
<h2>Método 1: Cambiar las entradas que muestra el widget</h2>
<p>Si lo único que quieres es ajustar las entradas que aparecen (cambiar el número, ocultar las publicadas o las programadas, incluir productos o páginas, o que cada autor solo vea las suyas), este es el camino más corto.</p>
<p>No reemplazas nada, no tocas el HTML, solo le dices a WordPress qué entradas quieres ver.</p>
<p>Lo hacemos con un filtro que que tiene WordPress desde la versión 4.2 (<code>dashboard_recent_posts_query_args</code>). Pega cualquiera de los siguientes snippets en un mu-plugin o en el <code>functions.php</code> del tema hijo y ya está.</p>
<p>Como detalle a tener en cuenta, el filtro se ejecuta dos veces, una para las entradas programadas (<code>post_status =&gt; 'future'</code>) y otra para las publicadas (<code>post_status =&gt; 'publish'</code>), así que mirando el estado puedes tratar cada bloque por separado.</p>
<h3>Cambiar el número de entradas mostradas</h3>
<p>Por defecto son cinco de cada tipo. Si quieres diez:</p>
<pre>/* Mostrar 10 entradas en el widget de actividad del escritorio en vez de 5 */
add_filter( 'dashboard_recent_posts_query_args', 'ayudawp_dashboard_activity_posts_per_page' );
function ayudawp_dashboard_activity_posts_per_page( $query_args ) {
    $query_args['posts_per_page'] = 10;
    return $query_args;
}</pre>
<p>El filtro afecta tanto a publicadas como a programadas. Si quieres números distintos para cada bloque, comprueba el estado dentro de la función y aplica el valor que toque.</p>
<p><a href="https://ayudawp.com/?attachment_id=159432" rel="nofollow"><img loading="lazy" decoding="async" class="sombra alignnone wp-image-159432 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_10-entradas-1200x754.jpg" alt="" width="1200" height="754" srcset="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_10-entradas-1200x754.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_10-entradas-768x482.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_10-entradas-1536x965.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_10-entradas.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></a></p>
<h3>Ocultar las entradas publicadas y dejar solo las programadas</h3>
<p>En sitios con flujo editorial activo, lo que interesa es ver lo que está por publicar, no lo que ya salió. Poniendo <code>posts_per_page</code> a cero en el bloque de publicadas, esa sección desaparece sin tocar el resto:</p>
<pre>/* Mostrar solo entradas programadas en el widget de actividad */
add_filter( 'dashboard_recent_posts_query_args', 'ayudawp_hide_published_in_activity' );
function ayudawp_hide_published_in_activity( $query_args ) {
if ( 'publish' === $query_args['post_status'] ) {
$query_args['post__in'] = array( 0 );
}
return $query_args;
}</pre>
<p>Funciona también al revés: cambia <code>'publish'</code> por <code>'future'</code> y desaparecen las programadas. La función <code>wp_dashboard_recent_posts()</code> devuelve sin pintar nada si la consulta no tiene resultados.</p>
<h3>Incluir tipos de contenido personalizados</h3>
<p>Por defecto el widget solo muestra entradas estándar (<code>post</code>). Si tienes páginas, productos de WooCommerce o un CPT propio, puedes incluirlos así:</p>
<pre>/* Mostrar también productos y páginas en widget de actividad del escritorio */
add_filter( 'dashboard_recent_posts_query_args', 'ayudawp_include_cpts_in_activity' );
function ayudawp_include_cpts_in_activity( $query_args ) {
    $query_args['post_type'] = array( 'post', 'page', 'product' );
    return $query_args;
}</pre>
<p>Si lo quieres a lo bestia y mostrar cualquier tipo de contenido, pasa <code>'any'</code> en lugar del array.</p>
<p><a href="https://ayudawp.com/?attachment_id=159434" rel="nofollow"><img loading="lazy" decoding="async" class="sombra alignnone wp-image-159434 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_tambien-productos-paginas-1200x754.jpg" alt="" width="1200" height="754" srcset="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_tambien-productos-paginas-1200x754.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_tambien-productos-paginas-768x482.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_tambien-productos-paginas-1536x965.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_tambien-productos-paginas.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></a></p>
<h3>Filtrar por autor</h3>
<p>En un sitio con varios redactores, igual quieres que cada editor solo vea sus propias entradas en el widget. Los administradores ven todo, los demás solo lo suyo:</p>
<pre>/* Mostrar solo entradas propias (salvo admins) en widget de actividad del escritorio */
add_filter( 'dashboard_recent_posts_query_args', 'ayudawp_activity_filter_by_author' );
function ayudawp_activity_filter_by_author( $query_args ) {
    if ( ! current_user_can( 'manage_options' ) ) {
        $query_args['author'] = get_current_user_id();
    }
    return $query_args;
}</pre>
<h3>Y los comentarios, ¿qué?</h3>
<p>El filtro <code>dashboard_recent_posts_query_args</code> solo toca entradas. Para los comentarios no hay un filtro equivalente, de modo que si lo único que quieres es ocultar la sección de comentarios entera, hay un apaño rápido con CSS:</p>
<pre>/* Ocultar comentarios en widget de actividad del escritorio */
add_action( 'admin_enqueue_scripts', 'ayudawp_hide_dashboard_comments_css' );
function ayudawp_hide_dashboard_comments_css( $hook ) {
    if ( 'index.php' !== $hook ) {
        return;
    }
    $css = '#latest-comments { display: none; }';
    wp_add_inline_style( 'dashboard', $css );
}</pre>
<p><a href="https://ayudawp.com/?attachment_id=159435" rel="nofollow"><img loading="lazy" decoding="async" class="sombra alignnone wp-image-159435 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_sin-comentarios-1200x754.jpg" alt="" width="1200" height="754" srcset="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_sin-comentarios-1200x754.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_sin-comentarios-768x482.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_sin-comentarios-1536x965.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_sin-comentarios.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></a></p>
<p>Para cambiar qué comentarios se muestran (solo pendientes, solo aprobados) o reorganizar las secciones, hace falta saltar al método 2 y reemplazar el <code>callback</code> del widget.</p>
<p>Es la limitación natural de este enfoque, que el filtro te da control sobre la consulta de entradas, pero no sobre el resto del widget.</p>
<p><b>Detalles técnicos:</b> La función <code>wp_dashboard_site_activity()</code> llama a <code>wp_dashboard_recent_posts()</code> dos veces pasándole un array de argumentos. Antes de ejecutar <code>WP_Query</code> con esos argumentos, aplica el filtro <code>dashboard_recent_posts_query_args</code>, que te permite modificarlos. Es un patrón muy típico de WordPress y la forma idiomática de tocar consultas internas sin reemplazar funciones del core.</p>
<h2>Método 2: Reescribir el contenido del widget original</h2>
<p>Si necesitas controlar también los comentarios, cambiar el aspecto del widget o reorganizar las secciones, el método 1 se queda corto.</p>
<p>La siguiente opción menos invasiva es reemplazar el callback del widget original (la función PHP que dibuja el contenido). Mantienes el meta box (con su ID, su posición y los ajustes de visibilidad que tenga cada usuario) pero reescribes lo que se pinta dentro.</p>
<h3>Plantilla base</h3>
<p>Este es el esqueleto. Muestra entradas programadas y comentarios pendientes de moderación, dos secciones que suelen ser las más útiles en un flujo de trabajo real:</p>
<pre>/**
* Sustituir el contenido del widget de actividad del escritorio con contenido propio
*/
add_action( 'wp_dashboard_setup', 'ayudawp_replace_activity_callback', 999 );
function ayudawp_replace_activity_callback() {
global $wp_meta_boxes;
if ( isset( $wp_meta_boxes['dashboard']['normal']['core']['dashboard_activity'] ) ) {
$wp_meta_boxes['dashboard']['normal']['core']['dashboard_activity']['callback'] = 'ayudawp_render_activity_widget';
}
}

/* Mostrar un widget de actividad personalizada: solo entradas programadas y comentarios pendientes */
function ayudawp_render_activity_widget() {
if ( ! current_user_can( 'edit_posts' ) ) {
return;
}

// Entradas programadas
$scheduled = new WP_Query( array(
'post_type' =&gt; 'post',
'post_status' =&gt; 'future',
'posts_per_page' =&gt; 5,
'orderby' =&gt; 'date',
'order' =&gt; 'ASC',
'no_found_rows' =&gt; true,
) );

if ( $scheduled-&gt;have_posts() ) {
echo '&lt;div class="activity-block"&gt;';
echo '&lt;h3&gt;' . esc_html__( 'Próximas publicaciones', 'ayudawp' ) . '&lt;/h3&gt;';
echo '&lt;ul&gt;';
while ( $scheduled-&gt;have_posts() ) {
$scheduled-&gt;the_post();
printf(
'&lt;li&gt;&lt;a href="%1$s"&gt;%2$s&lt;/a&gt; — %3$s&lt;/li&gt;',
esc_url( get_edit_post_link() ),
esc_html( get_the_title() ),
esc_html( get_the_date( 'd/m/Y H:i' ) )
);
}
echo '&lt;/ul&gt;';
echo '&lt;/div&gt;';
wp_reset_postdata();
}

// Solo comentarios pendientes (con acciones AJAX nativas: aprobar, responder, editar, spam, papelera)
$pending = get_comments( array(
'status' =&gt; 'hold',
'number' =&gt; 5,
) );

if ( ! empty( $pending ) ) {
echo '&lt;div class="activity-block"&gt;';
echo '&lt;h3&gt;' . esc_html__( 'Comentarios pendientes de moderación', 'ayudawp' ) . '&lt;/h3&gt;';
echo '&lt;ul id="the-comment-list" data-wp-lists="list:comment"&gt;';
foreach ( $pending as $comment ) {
_wp_dashboard_recent_comments_row( $comment );
}
echo '&lt;/ul&gt;';
echo '&lt;/div&gt;';
}
}</pre>
<p><a href="https://ayudawp.com/?attachment_id=159436" rel="nofollow"><img loading="lazy" decoding="async" class="sombra alignnone wp-image-159436 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_personalizado-plantilla-base-1200x754.jpg" alt="" width="1200" height="754" srcset="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_personalizado-plantilla-base-1200x754.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_personalizado-plantilla-base-768x482.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_personalizado-plantilla-base-1536x965.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_personalizado-plantilla-base.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></a></p>
<h3>Variación: cambiar qué comentarios se muestran</h3>
<p>El bloque de comentarios usa <code>get_comments()</code> con un argumento <code>status</code>. Cambiándolo cambias el filtro. Los valores aceptados son:</p>
<ul>
<li><code>'hold'</code>: solo comentarios pendientes de moderación (lo que tiene la plantilla base).</li>
<li><code>'approve'</code>: solo comentarios aprobados.</li>
<li><code>'spam'</code>: comentarios marcados como spam.</li>
<li><code>'trash'</code>: comentarios en la papelera.</li>
<li><code>'all'</code>: todos, sin filtrar por estado.</li>
</ul>
<p>También puedes filtrar comentarios por entrada concreta, por usuario, por rango de fechas o por tipo (incluyendo o excluyendo pingbacks y trackbacks). La documentación de <a href="https://developer.wordpress.org/reference/classes/wp_comment_query/__construct/" target="_blank" rel="nofollow noopener">WP_Comment_Query</a> recoge todas las opciones.</p>
<h3>Variación: quitar la sección de comentarios entera</h3>
<p>Si no quieres comentarios en el widget, borra el bloque <code>// Pending comments only</code> entero del callback. Tan sencillo como eso. El widget mostrará solo las entradas programadas.</p>
<h3>Variación: añadir las entradas publicadas recientemente</h3>
<p>La plantilla base solo muestra programadas, pero si quieres recuperar también las publicadas (al estilo del widget original), añade este bloque antes o después del de programadas:</p>
<pre>// Entradas publicadas recientes
$recent = new WP_Query( array(
    'post_type'      =&gt; 'post',
    'post_status'    =&gt; 'publish',
    'posts_per_page' =&gt; 5,
    'orderby'        =&gt; 'date',
    'order'          =&gt; 'DESC',
    'no_found_rows'  =&gt; true,
) );

if ( $recent-&gt;have_posts() ) {
    echo '&lt;div class="activity-block"&gt;';
    echo '&lt;h3&gt;' . esc_html__( 'Publicado recientemente', 'ayudawp' ) . '&lt;/h3&gt;';
    echo '&lt;ul&gt;';
    while ( $recent-&gt;have_posts() ) {
        $recent-&gt;the_post();
        printf(
            '&lt;li&gt;&lt;a href="%1$s"&gt;%2$s&lt;/a&gt; — %3$s&lt;/li&gt;',
            esc_url( get_edit_post_link() ),
            esc_html( get_the_title() ),
            esc_html( get_the_date( 'd/m/Y H:i' ) )
        );
    }
    echo '&lt;/ul&gt;';
    echo '&lt;/div&gt;';
    wp_reset_postdata();
}</pre>
<h3>Variación: añadir borradores en revisión</h3>
<p>Otro bloque útil para flujos editoriales son los borradores con estado <code>pending</code> (pendientes de revisión por un editor):</p>
<pre>// Entradas pendientes de revisión
$pending_posts = new WP_Query( array(
    'post_type'      =&gt; 'post',
    'post_status'    =&gt; 'pending',
    'posts_per_page' =&gt; 5,
    'orderby'        =&gt; 'modified',
    'order'          =&gt; 'DESC',
    'no_found_rows'  =&gt; true,
) );

if ( $pending_posts-&gt;have_posts() ) {
    echo '&lt;div class="activity-block"&gt;';
    echo '&lt;h3&gt;' . esc_html__( 'Pendientes de revisión', 'ayudawp' ) . '&lt;/h3&gt;';
    echo '&lt;ul&gt;';
    while ( $pending_posts-&gt;have_posts() ) {
        $pending_posts-&gt;the_post();
        printf(
            '&lt;li&gt;&lt;a href="%1$s"&gt;%2$s&lt;/a&gt; — %3$s&lt;/li&gt;',
            esc_url( get_edit_post_link() ),
            esc_html( get_the_title() ),
            esc_html( get_the_author() )
        );
    }
    echo '&lt;/ul&gt;';
    echo '&lt;/div&gt;';
    wp_reset_postdata();
}</pre>
<h3>Variación: productos WooCommerce sin existencias</h3>
<p>Para una tienda WooCommerce, ver productos agotados de un vistazo te ahorra dolores de cabeza, así que igual es una utilidad candidata para aprovechar el widget de actividad.</p>
<p>Mucha gente sigue tirando de <code>WP_Query</code> con <code>meta_query</code> sobre <code>_stock_status</code>, pero esa ya no es la forma recomendada por WooCommerce. La función <code>wc_get_products()</code> es más rápida porque usa la tabla <code>wc_product_meta_lookup</code> y es compatible con HPOS:</p>
<pre>// Productos sin existencias (WooCommerce)
if ( function_exists( 'wc_get_products' ) ) {
    $products = wc_get_products( array(
        'stock_status' =&gt; 'outofstock',
        'status'       =&gt; 'publish',
        'limit'        =&gt; 5,
        'orderby'      =&gt; 'date',
        'order'        =&gt; 'DESC',
    ) );

    if ( ! empty( $products ) ) {
        echo '&lt;div class="activity-block"&gt;';
        echo '&lt;h3&gt;' . esc_html__( 'Productos sin stock', 'ayudawp' ) . '&lt;/h3&gt;';
        echo '&lt;ul&gt;';
        foreach ( $products as $product ) {
            printf(
                '&lt;li&gt;&lt;a href="%1$s"&gt;%2$s&lt;/a&gt;&lt;/li&gt;',
                esc_url( get_edit_post_link( $product-&gt;get_id() ) ),
                esc_html( $product-&gt;get_name() )
            );
        }
        echo '&lt;/ul&gt;';
        echo '&lt;/div&gt;';
    }
}</pre>
<p><b>Detalles técnicos:</b> El array global <code>$wp_meta_boxes</code> guarda todas las meta cajas registradas en el escritorio. Cada widget tiene su ID, su contexto (<code>normal</code> o <code>side</code>) y su callback. Reasignando la propiedad <code>callback</code>, le decimos a WordPress que cuando vaya a pintar ese widget concreto ejecute nuestra función en lugar de la del core. El meta box sigue ahí, los ajustes del usuario también, pero el contenido lo controlamos nosotros. La prioridad 999 en <code>wp_dashboard_setup</code> nos asegura que corremos después de que el core haya registrado el widget original.</p>
<h2>Método 3: Quitar el widget original y crear uno propio</h2>
<p>Cuando lo que quieres es darle al escritorio un aire totalmente distinto (un «Panel editorial» con tu flujo, un «Panel de la tienda» para WooCommerce, o un «Resumen del cliente» en una web a medida), olvídate del widget de actividad y monta uno tuyo.</p>
<p>Aquí lo eliminas del escritorio y registras otro nuevo con su propio título, contenido e identidad. Es un widget completamente nuevo, no una reescritura del de actividad.</p>
<p>La pega frente al método anterior es que los usuarios pierden los ajustes que tuvieran (posición, colapsado, etc.) porque cambia el ID del meta box. La ventaja es que el resultado es mucho más limpio y reutilizable.</p>
<p>Las funciones que entran en juego son <code>remove_meta_box()</code> para quitar el original y <code>wp_add_dashboard_widget()</code> para registrar el nuevo.</p>
<h3>Ejemplo 1: Panel editorial completo</h3>
<p>Esta pequeña-gran virguería muestra un panel editorial, mucho mejor que el habitual widget de actividad ¿no?…</p>
<pre>/* Widget de actividad personalizado para panel editorial */
add_action( 'wp_dashboard_setup', 'ayudawp_register_editorial_widget' );
function ayudawp_register_editorial_widget() {
// Quitamos el widget de actividad original
remove_meta_box( 'dashboard_activity', 'dashboard', 'normal' );
// Registramos el propio
wp_add_dashboard_widget(
'ayudawp_editorial_widget',
__( 'Panel editorial', 'ayudawp' ),
'ayudawp_editorial_widget_render'
);
}

/* Estilos del panel editorial */
add_action( 'admin_enqueue_scripts', 'ayudawp_editorial_widget_styles' );
function ayudawp_editorial_widget_styles( $hook ) {
if ( 'index.php' !== $hook ) {
return;
}
$css = '
.ayudawp-editorial-stats { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 15px; }
.ayudawp-editorial-stats &gt; div { flex: 1; min-width: 90px; background: #f6f7f7; padding: 10px; border-left: 4px solid #2271b1; }
.ayudawp-editorial-stats a { text-decoration: none; color: inherit; display: block; }
.ayudawp-editorial-stats strong { font-size: 22px; display: block; line-height: 1.2; }
.ayudawp-editorial-stats .label { font-size: 12px; color: #50575e; }
';
wp_add_inline_style( 'dashboard', $css );
}

/* Renderizado del widget */
function ayudawp_editorial_widget_render() {
if ( ! current_user_can( 'edit_posts' ) ) {
return;
}

// Cabecera con métricas en una sola consulta cacheada por el core
$counts = wp_count_posts( 'post' );

$stats = array(
'draft' =&gt; array( __( 'Borradores', 'ayudawp' ), (int) $counts-&gt;draft ),
'pending' =&gt; array( __( 'En revisión', 'ayudawp' ), (int) $counts-&gt;pending ),
'future' =&gt; array( __( 'Programadas', 'ayudawp' ), (int) $counts-&gt;future ),
'publish' =&gt; array( __( 'Publicadas', 'ayudawp' ), (int) $counts-&gt;publish ),
);

echo '&lt;div class="ayudawp-editorial-stats"&gt;';
foreach ( $stats as $status =&gt; $data ) {
printf(
'&lt;div&gt;&lt;a href="%1$s"&gt;&lt;strong&gt;%2$d&lt;/strong&gt;&lt;span class="label"&gt;%3$s&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;',
esc_url( admin_url( 'edit.php?post_status=' . $status . '&amp;post_type=post' ) ),
$data[1],
esc_html( $data[0] )
);
}
echo '&lt;/div&gt;';

// Próximas publicaciones programadas
$scheduled = new WP_Query( array(
'post_type' =&gt; 'post',
'post_status' =&gt; 'future',
'posts_per_page' =&gt; 5,
'orderby' =&gt; 'date',
'order' =&gt; 'ASC',
'no_found_rows' =&gt; true,
) );
if ( $scheduled-&gt;have_posts() ) {
echo '&lt;div class="activity-block"&gt;';
echo '&lt;h3&gt;' . esc_html__( 'Próximas publicaciones', 'ayudawp' ) . '&lt;/h3&gt;';
echo '&lt;ul&gt;';
while ( $scheduled-&gt;have_posts() ) {
$scheduled-&gt;the_post();
printf(
'&lt;li&gt;&lt;a href="%1$s"&gt;%2$s&lt;/a&gt; — %3$s (%4$s)&lt;/li&gt;',
esc_url( get_edit_post_link() ),
esc_html( get_the_title() ),
esc_html( get_the_date( 'd/m/Y H:i' ) ),
esc_html( get_the_author() )
);
}
echo '&lt;/ul&gt;';
echo '&lt;/div&gt;';
wp_reset_postdata();
}

// Entradas pendientes de revisión
$pending_review = new WP_Query( array(
'post_type' =&gt; 'post',
'post_status' =&gt; 'pending',
'posts_per_page' =&gt; 5,
'orderby' =&gt; 'modified',
'order' =&gt; 'DESC',
'no_found_rows' =&gt; true,
) );
if ( $pending_review-&gt;have_posts() ) {
echo '&lt;div class="activity-block"&gt;';
echo '&lt;h3&gt;' . esc_html__( 'Pendientes de revisión', 'ayudawp' ) . '&lt;/h3&gt;';
echo '&lt;ul&gt;';
while ( $pending_review-&gt;have_posts() ) {
$pending_review-&gt;the_post();
printf(
'&lt;li&gt;&lt;a href="%1$s"&gt;%2$s&lt;/a&gt; — %3$s&lt;/li&gt;',
esc_url( get_edit_post_link() ),
esc_html( get_the_title() ),
esc_html( get_the_author() )
);
}
echo '&lt;/ul&gt;';
echo '&lt;/div&gt;';
wp_reset_postdata();
}

// Comentarios pendientes de moderación con acciones AJAX nativas
$pending_comments = get_comments( array(
'status' =&gt; 'hold',
'number' =&gt; 5,
) );
if ( ! empty( $pending_comments ) ) {
echo '&lt;div class="activity-block"&gt;';
echo '&lt;h3&gt;' . esc_html__( 'Comentarios pendientes', 'ayudawp' ) . '&lt;/h3&gt;';
echo '&lt;ul id="the-comment-list" data-wp-lists="list:comment"&gt;';
foreach ( $pending_comments as $comment ) {
_wp_dashboard_recent_comments_row( $comment );
}
echo '&lt;/ul&gt;';
echo '&lt;/div&gt;';
}
}</pre>
<p><a href="https://ayudawp.com/?attachment_id=159437" rel="nofollow"><img loading="lazy" decoding="async" class="sombra alignnone wp-image-159437 size-medium" src="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-1200x754.jpg" alt="" width="1200" height="754" srcset="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-1200x754.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-768x482.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-1536x965.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px"></a></p>
<h3>Ejemplo 2: Panel de tienda WooCommerce</h3>
<p>Para una tienda, lo más útil es ver de un vistazo productos sin stock y pedidos pendientes:</p>
<pre>/* Widget de actividad personalizado para panel de tienda WooCommerce */
add_action( 'wp_dashboard_setup', 'ayudawp_register_shop_widget' );
function ayudawp_register_shop_widget() {
// Solo si WooCommerce está activo
if ( ! function_exists( 'wc_get_products' ) ) {
return;
}
// Quitamos el widget de actividad original
remove_meta_box( 'dashboard_activity', 'dashboard', 'normal' );
// Registramos el propio
wp_add_dashboard_widget(
'ayudawp_shop_widget',
__( 'Panel de la tienda', 'ayudawp' ),
'ayudawp_shop_dashboard_widget_render'
);
}

/* Estilos del panel de tienda */
add_action( 'admin_enqueue_scripts', 'ayudawp_shop_widget_styles' );
function ayudawp_shop_widget_styles( $hook ) {
if ( 'index.php' !== $hook ) {
return;
}
$css = '
.ayudawp-shop-stats { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 15px; }
.ayudawp-shop-stats &gt; div { flex: 1; min-width: 90px; background: #f6f7f7; padding: 10px; border-left: 4px solid #674399; }
.ayudawp-shop-stats strong { font-size: 22px; display: block; line-height: 1.2; }
.ayudawp-shop-stats .label { font-size: 12px; color: #50575e; }
';
wp_add_inline_style( 'dashboard', $css );
}

/* Renderizado del widget */
function ayudawp_shop_dashboard_widget_render() {
if ( ! current_user_can( 'manage_woocommerce' ) ) {
return;
}

// Métricas de pedidos por estado
$stats = array(
__( 'Pendientes', 'ayudawp' ) =&gt; wc_orders_count( 'pending' ),
__( 'En proceso', 'ayudawp' ) =&gt; wc_orders_count( 'processing' ),
__( 'En espera', 'ayudawp' ) =&gt; wc_orders_count( 'on-hold' ),
__( 'Completados', 'ayudawp' ) =&gt; wc_orders_count( 'completed' ),
);

echo '&lt;div class="ayudawp-shop-stats"&gt;';
foreach ( $stats as $label =&gt; $count ) {
printf(
'&lt;div&gt;&lt;strong&gt;%1$d&lt;/strong&gt;&lt;span class="label"&gt;%2$s&lt;/span&gt;&lt;/div&gt;',
(int) $count,
esc_html( $label )
);
}
echo '&lt;/div&gt;';

// Productos sin stock
$out_of_stock = wc_get_products( array(
'stock_status' =&gt; 'outofstock',
'status' =&gt; 'publish',
'limit' =&gt; 5,
) );
if ( ! empty( $out_of_stock ) ) {
echo '&lt;div class="activity-block"&gt;';
echo '&lt;h3&gt;' . esc_html__( 'Productos sin stock', 'ayudawp' ) . '&lt;/h3&gt;';
echo '&lt;ul&gt;';
foreach ( $out_of_stock as $product ) {
printf(
'&lt;li&gt;&lt;a href="%1$s"&gt;%2$s&lt;/a&gt;&lt;/li&gt;',
esc_url( get_edit_post_link( $product-&gt;get_id() ) ),
esc_html( $product-&gt;get_name() )
);
}
echo '&lt;/ul&gt;';
echo '&lt;/div&gt;';
}

// Pedidos por atender (pendientes y en espera)
$orders = wc_get_orders( array(
'status' =&gt; array( 'pending', 'on-hold' ),
'limit' =&gt; 5,
'orderby' =&gt; 'date',
'order' =&gt; 'DESC',
) );
if ( ! empty( $orders ) ) {
echo '&lt;div class="activity-block"&gt;';
echo '&lt;h3&gt;' . esc_html__( 'Pedidos por atender', 'ayudawp' ) . '&lt;/h3&gt;';
echo '&lt;ul&gt;';
foreach ( $orders as $order ) {
printf(
'&lt;li&gt;&lt;a href="%1$s"&gt;#%2$s — %3$s&lt;/a&gt; — %4$s&lt;/li&gt;',
esc_url( $order-&gt;get_edit_order_url() ),
esc_html( $order-&gt;get_order_number() ),
esc_html( $order-&gt;get_formatted_billing_full_name() ),
wp_kses_post( wc_price( $order-&gt;get_total() ) )
);
}
echo '&lt;/ul&gt;';
echo '&lt;/div&gt;';
}
}</pre>
<p>Para registrarlo, igual que antes: cambia la llamada a <code>wp_add_dashboard_widget()</code> con el nuevo callback. Y restringe la visibilidad a usuarios con la capacidad <code>manage_woocommerce</code>.</p>
<h3>Ejemplo 3: Panel de eventos próximos</h3>
<p>Si gestionas reservas, eventos o un tipo de contenido con una fecha guardada, esta versión lista los próximos ordenados por la fecha del evento, no por la fecha de publicación:</p>
<pre>/* Widget de actividad personalizado para panel de eventos */
function ayudawp_events_widget_render() {
    if ( ! current_user_can( 'edit_posts' ) ) {
        return;
    }

    $upcoming = new WP_Query( array(
        'post_type'      =&gt; 'evento',
        'post_status'    =&gt; 'publish',
        'posts_per_page' =&gt; 5,
        'meta_key'       =&gt; 'fecha_del_evento',
        'orderby'        =&gt; 'meta_value',
        'order'          =&gt; 'ASC',
        'meta_query'     =&gt; array(
            array(
                'key'     =&gt; 'fecha_del_evento',
                'value'   =&gt; current_time( 'Y-m-d' ),
                'compare' =&gt; '&gt;=',
                'type'    =&gt; 'DATE',
            ),
        ),
        'no_found_rows'  =&gt; true,
    ) );

    if ( ! $upcoming-&gt;have_posts() ) {
        echo '&lt;p&gt;' . esc_html__( 'No hay eventos próximos.', 'ayudawp' ) . '&lt;/p&gt;';
        return;
    }

    echo '&lt;ul&gt;';
    while ( $upcoming-&gt;have_posts() ) {
        $upcoming-&gt;the_post();
        $event_date = get_post_meta( get_the_ID(), 'fecha_del_evento', true );
        printf(
            '&lt;li&gt;&lt;a href="%1$s"&gt;%2$s&lt;/a&gt; — %3$s&lt;/li&gt;',
            esc_url( get_edit_post_link() ),
            esc_html( get_the_title() ),
            esc_html( $event_date )
        );
    }
    echo '&lt;/ul&gt;';
    wp_reset_postdata();
}</pre>
<p>Si el sitio tiene muchos eventos, mete los resultados en un transient de unos minutos para no machacar la base de datos cada vez que alguien entra al escritorio.</p>
<p><b>Detalles técnicos:</b> <code>remove_meta_box()</code> elimina la entrada del array <code>$wp_meta_boxes</code>, así que el widget original deja de existir para WordPress. <code>wp_add_dashboard_widget()</code> registra uno nuevo con su propio ID, título y callback. Como es un meta box nuevo, los ajustes del usuario (posición, colapsado, oculto) no se heredan del anterior. Es un widget partido de cero.</p>
<h2>Método 4: Widget con opciones configurables desde el escritorio</h2>
<p>Cuando montas algo para un cliente o quieres aplicar la misma personalización en varios sitios, lo suyo es que el propio usuario pueda elegir cuántas entradas mostrar, si quiere ver comentarios o no, o si solo quiere ver lo suyo.</p>
<p>En lugar de dejar cada cosa fijada en el código, le añades un pequeño formulario de configuración que aparece al pulsar en el enlace «Configurar» del widget, ahí cada uno decide.</p>
<p>WordPress lo admite de fábrica, al registrar el widget con <code>wp_add_dashboard_widget()</code>, pasas un cuarto parámetro (el llamado <em>control callback</em>) que se encarga tanto de pintar el formulario como de guardar los valores cuando el usuario lo envía.</p>
<p>El nonce y la verificación de permisos los gestiona el propio core, así que tú solo te ocupas de leer la opción guardada y aplicarla cuando se pinta el widget.</p>
<h3>Widget configurable con opciones configurables</h3>
<p>Esta es una versión más completa que el típico «número de entradas». Permite configurar cuántas entradas programadas mostrar, si incluir comentarios pendientes, si filtrar por autor (cada editor solo ve lo suyo) y mucho más:</p>
<pre>/**
* Widget de actividad configurable mediante panel de ajustes propio
*/
class AyudaWP_Configurable_Activity_Widget {

const OPTION_NAME = 'ayudawp_activity_widget_settings';
const WIDGET_ID = 'ayudawp_configurable_activity';

public function __construct() {
add_action( 'wp_dashboard_setup', array( $this, 'register_widget' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
}

public function register_widget() {
wp_add_dashboard_widget(
self::WIDGET_ID,
__( 'Actividad editorial configurable', 'ayudawp' ),
array( $this, 'render_widget' ),
array( $this, 'render_form' )
);
}

public function enqueue_styles( $hook ) {
if ( 'index.php' !== $hook ) {
return;
}
$css = '
.ayudawp-editorial-stats { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 15px; }
.ayudawp-editorial-stats &gt; div { flex: 1; min-width: 90px; background: #f6f7f7; padding: 10px; border-left: 4px solid #2271b1; }
.ayudawp-editorial-stats a { text-decoration: none; color: inherit; display: block; }
.ayudawp-editorial-stats strong { font-size: 22px; display: block; line-height: 1.2; }
.ayudawp-editorial-stats .label { font-size: 12px; color: #50575e; }
';
wp_add_inline_style( 'dashboard', $css );
}

public function render_widget() {
if ( ! current_user_can( 'edit_posts' ) ) {
return;
}

$settings = $this-&gt;get_settings();
$limit = (int) $settings['limit'];
$author = ! empty( $settings['only_mine'] ) ? get_current_user_id() : 0;

if ( ! empty( $settings['show_stats'] ) ) {
$this-&gt;render_stats();
}

if ( ! empty( $settings['show_scheduled'] ) ) {
$this-&gt;render_posts_block(
'future',
'ASC',
'date',
__( 'Próximas publicaciones', 'ayudawp' ),
__( 'No hay entradas programadas.', 'ayudawp' ),
$limit,
$author,
'date'
);
}

if ( ! empty( $settings['show_pending_review'] ) ) {
$this-&gt;render_posts_block(
'pending',
'DESC',
'modified',
__( 'Pendientes de revisión', 'ayudawp' ),
__( 'Sin entradas pendientes de revisión.', 'ayudawp' ),
$limit,
$author,
'author'
);
}

if ( ! empty( $settings['show_recent'] ) ) {
$this-&gt;render_posts_block(
'publish',
'DESC',
'date',
__( 'Publicado recientemente', 'ayudawp' ),
__( 'Sin publicaciones recientes.', 'ayudawp' ),
$limit,
$author,
'date'
);
}

if ( ! empty( $settings['show_comments'] ) ) {
$this-&gt;render_comments_block( $limit );
}
}

private function render_stats() {
$counts = wp_count_posts( 'post' );
$stats = array(
'draft' =&gt; array( __( 'Borradores', 'ayudawp' ), (int) $counts-&gt;draft ),
'pending' =&gt; array( __( 'En revisión', 'ayudawp' ), (int) $counts-&gt;pending ),
'future' =&gt; array( __( 'Programadas', 'ayudawp' ), (int) $counts-&gt;future ),
'publish' =&gt; array( __( 'Publicadas', 'ayudawp' ), (int) $counts-&gt;publish ),
);

echo '&lt;div class="ayudawp-editorial-stats"&gt;';
foreach ( $stats as $status =&gt; $data ) {
printf(
'&lt;div&gt;&lt;a href="%1$s"&gt;&lt;strong&gt;%2$d&lt;/strong&gt;&lt;span class="label"&gt;%3$s&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;',
esc_url( admin_url( 'edit.php?post_status=' . $status . '&amp;post_type=post' ) ),
$data[1],
esc_html( $data[0] )
);
}
echo '&lt;/div&gt;';
}

private function render_posts_block( $status, $order, $orderby, $title, $empty_msg, $limit, $author, $meta ) {
$query_args = array(
'post_type' =&gt; 'post',
'post_status' =&gt; $status,
'posts_per_page' =&gt; $limit,
'orderby' =&gt; $orderby,
'order' =&gt; $order,
'no_found_rows' =&gt; true,
);

if ( $author ) {
$query_args['author'] = $author;
}

$query = new WP_Query( $query_args );

echo '&lt;div class="activity-block"&gt;';
echo '&lt;h3&gt;' . esc_html( $title ) . '&lt;/h3&gt;';

if ( $query-&gt;have_posts() ) {
echo '&lt;ul&gt;';
while ( $query-&gt;have_posts() ) {
$query-&gt;the_post();
$extra = ( 'date' === $meta )
? esc_html( get_the_date( 'd/m/Y H:i' ) )
: esc_html( get_the_author() );
printf(
'&lt;li&gt;&lt;a href="%1$s"&gt;%2$s&lt;/a&gt; — %3$s&lt;/li&gt;',
esc_url( get_edit_post_link() ),
esc_html( get_the_title() ),
$extra
);
}
echo '&lt;/ul&gt;';
wp_reset_postdata();
} else {
echo '&lt;p&gt;' . esc_html( $empty_msg ) . '&lt;/p&gt;';
}
echo '&lt;/div&gt;';
}

private function render_comments_block( $limit ) {
$pending = get_comments( array(
'status' =&gt; 'hold',
'number' =&gt; $limit,
) );

echo '&lt;div class="activity-block"&gt;';
echo '&lt;h3&gt;' . esc_html__( 'Comentarios pendientes', 'ayudawp' ) . '&lt;/h3&gt;';

if ( ! empty( $pending ) ) {
// Estructura nativa para que el JS del core enganche las acciones AJAX
echo '&lt;ul id="the-comment-list" data-wp-lists="list:comment"&gt;';
foreach ( $pending as $comment ) {
_wp_dashboard_recent_comments_row( $comment );
}
echo '&lt;/ul&gt;';
} else {
echo '&lt;p&gt;' . esc_html__( 'Sin comentarios pendientes.', 'ayudawp' ) . '&lt;/p&gt;';
}
echo '&lt;/div&gt;';
}

public function render_form() {
// Procesar envío del formulario. El nonce ya lo verifica WP
if ( 'POST' === $_SERVER['REQUEST_METHOD']
&amp;&amp; isset( $_POST['widget_id'] )
&amp;&amp; self::WIDGET_ID === $_POST['widget_id']
&amp;&amp; current_user_can( 'edit_dashboard' ) ) {

$limit = isset( $_POST['ayudawp_limit'] )
? absint( wp_unslash( $_POST['ayudawp_limit'] ) )
: 5;

update_option( self::OPTION_NAME, array(
'limit' =&gt; max( 1, min( 20, $limit ) ),
'show_stats' =&gt; ! empty( $_POST['ayudawp_show_stats'] ),
'show_scheduled' =&gt; ! empty( $_POST['ayudawp_show_scheduled'] ),
'show_pending_review' =&gt; ! empty( $_POST['ayudawp_show_pending_review'] ),
'show_recent' =&gt; ! empty( $_POST['ayudawp_show_recent'] ),
'show_comments' =&gt; ! empty( $_POST['ayudawp_show_comments'] ),
'only_mine' =&gt; ! empty( $_POST['ayudawp_only_mine'] ),
) );
}

$settings = $this-&gt;get_settings();
?&gt;
&lt;p&gt;
&lt;label for="ayudawp_limit"&gt;
&lt;?php esc_html_e( 'Cantidad de elementos por sección:', 'ayudawp' ); ?&gt;
&lt;/label&gt;
&lt;input class="small-text"
id="ayudawp_limit"
name="ayudawp_limit"
type="number"
min="1"
max="20"
value="&lt;?php echo esc_attr( $settings['limit'] ); ?&gt;"&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;?php esc_html_e( 'Secciones a mostrar:', 'ayudawp' ); ?&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;
&lt;label&gt;
&lt;input type="checkbox" name="ayudawp_show_stats" value="1" &lt;?php checked( ! empty( $settings['show_stats'] ) ); ?&gt;&gt;
&lt;?php esc_html_e( 'Resumen de cifras (borradores, en revisión, programadas, publicadas)', 'ayudawp' ); ?&gt;
&lt;/label&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;label&gt;
&lt;input type="checkbox" name="ayudawp_show_scheduled" value="1" &lt;?php checked( ! empty( $settings['show_scheduled'] ) ); ?&gt;&gt;
&lt;?php esc_html_e( 'Próximas publicaciones programadas', 'ayudawp' ); ?&gt;
&lt;/label&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;label&gt;
&lt;input type="checkbox" name="ayudawp_show_pending_review" value="1" &lt;?php checked( ! empty( $settings['show_pending_review'] ) ); ?&gt;&gt;
&lt;?php esc_html_e( 'Entradas pendientes de revisión', 'ayudawp' ); ?&gt;
&lt;/label&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;label&gt;
&lt;input type="checkbox" name="ayudawp_show_recent" value="1" &lt;?php checked( ! empty( $settings['show_recent'] ) ); ?&gt;&gt;
&lt;?php esc_html_e( 'Publicaciones recientes', 'ayudawp' ); ?&gt;
&lt;/label&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;label&gt;
&lt;input type="checkbox" name="ayudawp_show_comments" value="1" &lt;?php checked( ! empty( $settings['show_comments'] ) ); ?&gt;&gt;
&lt;?php esc_html_e( 'Comentarios pendientes de moderación', 'ayudawp' ); ?&gt;
&lt;/label&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;?php esc_html_e( 'Filtros:', 'ayudawp' ); ?&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;
&lt;label&gt;
&lt;input type="checkbox" name="ayudawp_only_mine" value="1" &lt;?php checked( ! empty( $settings['only_mine'] ) ); ?&gt;&gt;
&lt;?php esc_html_e( 'Mostrar solo mis entradas (no las de otros autores)', 'ayudawp' ); ?&gt;
&lt;/label&gt;
&lt;/p&gt;
&lt;?php
}

private function get_settings() {
$defaults = array(
'limit' =&gt; 5,
'show_stats' =&gt; true,
'show_scheduled' =&gt; true,
'show_pending_review' =&gt; true,
'show_recent' =&gt; false,
'show_comments' =&gt; true,
'only_mine' =&gt; false,
);
$saved = get_option( self::OPTION_NAME, array() );
return wp_parse_args( $saved, $defaults );
}
}

new AyudaWP_Configurable_Activity_Widget();</pre>
<p>Con esto, al pasar el ratón por el título del widget aparece el enlace «Configurar», y al hacer clic se despliega el formulario con las opciones disponibles, ejemplo de lo que hemos estado viendo, pero ya a golpe de clic.</p>

<a href="https://ayudawp.com/personalizar-widget-actividad-escritorio/widget-actividad-escritorio-wp_panel-editorial-configurable/" rel="nofollow"><img width="1200" height="754" src="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-configurable-1200x754.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-configurable-1200x754.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-configurable-768x482.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-configurable-1536x965.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-configurable.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>
<a href="https://ayudawp.com/personalizar-widget-actividad-escritorio/widget-actividad-escritorio-wp_panel-editorial-configurable-ajustes/" rel="nofollow"><img width="1200" height="754" src="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-configurable-ajustes-1200x754.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-configurable-ajustes-1200x754.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-configurable-ajustes-768x482.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-configurable-ajustes-1536x965.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/06/widget-actividad-escritorio-wp_panel-editorial-configurable-ajustes.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>

<p>Al guardar, los valores se persisten en la opción <code>ayudawp_activity_widget_settings</code> y se aplican al renderizado del widget.</p>
<p>Si quieres añadir más controles solo tienes que añadir más campos al formulario, ampliar los valores por defecto en <code>get_settings()</code> y leerlos en <code>render_widget()</code>, la estructura ya está montada.</p>
<p><b>Detalles técnicos:</b> <code>wp_add_dashboard_widget()</code> acepta hasta cuatro parámetros principales: ID, título, callback de renderizado y callback de configuración. Si pasas el cuarto, WordPress añade automáticamente el enlace «Configurar» en el título del widget. Cuando el usuario guarda el formulario, WordPress se encarga del nonce (lo verifica con <code>check_admin_referer( 'edit-dashboard-widget_' . $widget_id, 'dashboard-widget-nonce' )</code>) antes de llamar al control callback. Por eso dentro del callback solo necesitamos comprobar capacidad y procesar los datos. El formulario se envía a la misma página del escritorio y se procesa en el mismo callback que lo pinta.</p>
<h2>Mostrar el widget solo a ciertos perfiles de usuario</h2>
<p>En muchos casos no quieres que un autor o colaborador vea actividad del resto del sitio, o que un editor vea productos de la tienda. Combinando <code>remove_meta_box()</code> con <code>current_user_can()</code> filtras con una línea:</p>
<pre>/* Mostrar widget de actividad solo a ciertos usuarios */
add_action( 'wp_dashboard_setup', 'ayudawp_hide_activity_for_non_editors', 100 );
function ayudawp_hide_activity_for_non_editors() {
    if ( ! current_user_can( 'edit_others_posts' ) ) {
        remove_meta_box( 'dashboard_activity', 'dashboard', 'normal' );
    }
}</pre>
<p>El widget desaparece para todo el mundo salvo editores y administradores. Cambia la capacidad por la que necesites según el caso: <code>manage_woocommerce</code>, <code>manage_options</code>, <code>publish_posts</code>, etc.</p>
<h2>Plugins alternativos si prefieres no tocar código</h2>
<p>Si no quieres meter mano al código, hay tres plugins que cubren bien el caso:</p>
<ul>
<li><a href="https://wordpress.org/plugins/ultimate-dashboard/" target="_blank" rel="nofollow noopener">Ultimate Dashboard</a>, de David Vongries. Desactiva los widgets nativos de un clic y permite crear los tuyos con texto, HTML o iconos. La versión pro añade restricciones por rol y limpieza de widgets de otros plugins.</li>
<li><a href="https://wordpress.org/plugins/admin-menu-editor/" target="_blank" rel="nofollow noopener">Admin Menu Editor</a>, de Janis Elsts. Aunque está pensado para el menú lateral, también permite reordenar y dar permisos por rol al panel de control. Más de 400.000 instalaciones activas y mantenimiento constante.</li>
<li><a href="https://wordpress.org/plugins/dashboard-directory-size/" target="_blank" rel="nofollow noopener">Dashboard Directory Size</a>, de Pete Nelson. Añade un widget que muestra el tamaño en disco de las carpetas de WordPress y de la base de datos. Útil para sitios donde controlar el crecimiento del almacenamiento importa.</li>
</ul>
<p>Si lo que te interesa es ver actividad de bots de IA en el panel (un caso cada vez más habitual con ChatGPT, Claude o Perplexity rastreando tu sitio), echa un vistazo a <a href="https://servicios.ayudawp.com" target="_blank" rel="ugc noopener">VigIA</a>, que añade su propio widget de analítica de crawlers de IA al escritorio.</p>
<h2>Dónde poner cada bloque de código</h2>
<p>Cualquiera de estos snippets puede ir en tres sitios:</p>
<ul>
<li>El <code>functions.php</code> de un tema hijo. La opción más rápida pero también la más frágil, porque si cambias de tema pierdes los cambios.</li>
<li>Un mu-plugin en <code>wp-content/mu-plugins/</code>. Es la forma que prefiero para personalizaciones del escritorio porque se mantienen activas pase lo que pase con temas y plugins. Si todavía no usas mu-plugins, tengo una <a href="https://ayudawp.com/?s=mu-plugins" target="_blank" rel="ugc noopener">serie de artículos sobre ellos</a> que te puede ayudar.</li>
<li>Un plugin propio. La opción más profesional si vas a empaquetar varias personalizaciones del admin y compartirlas entre sitios.</li>
</ul>
<p>Si combinas varios métodos, ojo con el orden. El filtro <code>dashboard_recent_posts_query_args</code> (método 1) no tiene sentido si reemplazas el widget entero (métodos 2, 3 o 4), porque ya no se ejecuta la función que lo dispara. Elige uno según lo lejos que necesites llegar y ya está.</p>
<p>Mi consejo, así en general, es que <strong>empieces por el método 1</strong>, que es el más limpio, y si te quedas corto pasa al 2.</p>
<p>Solo monta el 4 si necesitas que el propio usuario configure el widget desde el panel, que cuanto menos toques el comportamiento del core menos cosas se romperán cuando WordPress se actualice.</p>
<p>Para todo lo demás, aquí me tienes.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ayudawp.com/personalizar-widget-actividad-escritorio/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>¡Deberías deshacerte de las páginas de adjuntos de WordPress pero ya!</title>
		<link>https://ayudawp.com/problema-seo-adjuntos-wordpress/</link>
					<comments>https://ayudawp.com/problema-seo-adjuntos-wordpress/#respond</comments>
		
		<dc:creator><![CDATA[Fernando Tellado]]></dc:creator>
		<pubDate>Tue, 02 Jun 2026 06:28:00 +0000</pubDate>
				<category><![CDATA[SEO / AEO / GEO / LLMO]]></category>
		<category><![CDATA[Tutoriales - Trucos]]></category>
		<category><![CDATA[WordPress.com]]></category>
		<category><![CDATA[WordPress.org]]></category>
		<category><![CDATA[adjuntos]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Imágenes]]></category>
		<category><![CDATA[Principiante]]></category>
		<guid isPermaLink="false">https://ayudawp.com/?p=159132</guid>

					<description><![CDATA[WordPress crea una URL pública por cada imagen que subes a la biblioteca de medios. Sí, has leído bien, por cada una, a cada imagen que subes se crea automáticamente y sin preguntar una URL nueva.]]></description>
										<content:encoded><![CDATA[<p>WordPress crea <strong>una URL pública por cada imagen que subes</strong> a la biblioteca de medios. Sí, has leído bien, por cada una, a cada imagen que subes se crea automáticamente y sin preguntar una URL nueva.</p>
<p>Si llevas años con tu web y has subido 800 imágenes, tienes 800 URLs que Google puede rastrear, indexar y mostrar en los resultados de búsqueda, y <strong>la mayoría de esas páginas no tienen prácticamente nada de contenido</strong>.</p>
<p>Eso <strong>tiene impacto (no bueno precisamente) en el SEO</strong> y es más fácil de solucionar de lo que parece.</p>
<h2>Qué es una página de adjunto y por qué existe tal cosa</h2>
<p>Cuando subes una imagen a WordPress, el sistema hace dos cosas. Por un lado guarda el archivo en <code>/wp-content/uploads/</code>, y además crea una entrada en la base de datos de tipo <code>attachment</code>. Esa entrada tiene su propia URL, su propio título y, si tienes instalado el tema por defecto, <strong>la propia página con la imagen y poco más</strong>.</p>
<p>La URL suele tener este aspecto:</p>
<pre>https://tudominio.com/nombre-del-archivo/</pre>
<p>O si la imagen está adjunta a una entrada concreta:</p>
<pre>https://tudominio.com/nombre-de-la-entrada/nombre-del-archivo/</pre>
<p>WordPress las creó así porque en los primeros tiempos era una plataforma de blogging pura y tenía sentido que los adjuntos fueran un tipo de contenido con entidad propia. Hoy <strong>la mayoría de los sitios no las necesita para nada, pero siguen ahí</strong>.</p>
<h2>El problema de SEO, que lo es</h2>
<p>Abre Google Search Console, ve a <strong>Indexación &gt; Páginas</strong> y fíjate en las razones por las que Google no indexa algunas URLs. Hay muchas posibilidades de que veas un grupo grande en categorías como «El contenido duplicado es el contenido enviado» o «Rastreada, actualmente sin indexar». Si echas un vistazo a esas URLs, es probable que muchas sean páginas de adjuntos.</p>
<p>El problema tiene varias caras. Por un lado, <strong>estas páginas tienen muy poco contenido, porque solo tienen la imagen, quizá el título que le pusiste al subir el archivo y nada más</strong>, y Google las ve como páginas de baja calidad, el tristemente famoso <strong>thin content</strong>.</p>
<p>Por otro lado, si subes una imagen para ilustrar un artículo sobre WordPress, <strong>esa página de adjunto va a competir en los resultados de búsqueda con el artículo original</strong>, que es donde está todo el contenido de verdad.</p>
<p>No es algo que vaya a hundir tu web de golpe, pero sí consume presupuesto de rastreo, diluye la autoridad y ensucia los informes de Search Console con <strong>URLs que no aportan nada</strong>.</p>
<h2>Cómo solucionarlo con plugins</h2>
<p>Hay <strong>varias maneras de abordar este problema con plugins</strong>, y decidir cuál usar depende mucho de lo que busques y cómo quieras tratar las páginas de adjuntos.</p>
<p>Los <strong>plugins de SEO</strong> más usados en WordPress incluyen alguna opción, aunque cada uno la pone en un sitio distinto, lo trata de distinta forma y con un nombre diferente.</p>
<ul>
<li><strong>Yoast SEO:</strong> ve a <em>SEO &gt; Apariencia en el buscador &gt; Medios</em>. Encontrarás una opción para redirigir las URLs de adjuntos. Ojo con el nombre porque lleva a confusión: lo que hace Yoast es redirigir al archivo de imagen directamente, no al post al que pertenece. Así que si alguien llega a <code>tudominio.com/foto-de-portada/</code>, lo que ve es la imagen cruda en el navegador. Desde la versión 20.x aproximadamente la opción se muestra algo más clara y en algunos casos ofrece también redirigir al post padre, pero la interfaz ha cambiado varias veces así que comprueba qué tienes delante.</li>
<li><strong>Rank Math:</strong> está en <em>Rank Math &gt; Ajustes generales &gt; Medios</em>, bajo el nombre «Redirección de adjuntos». Por defecto también redirige al archivo de imagen, pero incluye una opción para redirigir al post padre cuando la imagen está adjunta a uno, que es más útil desde el punto de vista del SEO.</li>
<li><strong>SEOPress:</strong> lo tienes en <em>SEO &gt; Ajustes &gt; Avanzado &gt; Adjuntos</em>. La opción se llama «Redirigir páginas de adjunto» y redirige al post padre si existe o a la home si la imagen no está adjunta a ningún contenido, que es el comportamiento más sensato de los tres.</li>
</ul>
<p>Además de estos, tienes <a href="https://es.wordpress.org/plugins/attachment-pages-redirect/" target="_blank" rel="noopener">Attachment Pages Redirect</a>, que genera automáticamente <strong>redirecciones 301, 302 o incluso 404</strong>, dependiendo del resultado que quieras ofrecer.</p>
<p>Y luego está el enfoque de <a href="https://es.wordpress.org/plugins/noindexer/" target="_blank" rel="noopener"><strong>NoIndexer</strong></a>, que propone una <strong>solución sencilla, pero igual de efectiva</strong>, y es simplemente añadir la etiqueta meta <code>noindex</code> a todas las páginas de adjuntos, de manera <strong>que simplemente los buscadores no indexen estas URLs</strong>. Es perfectamente válido para webs nuevas, pero no para sitios con estas URLs ya indexadas.</p>
<h2>Soluciones con código, sin depender de un plugin de SEO</h2>
<p>Si no usas plugins de SEO, o simplemente prefieres <strong>no depender de ningún plugin para esto</strong>, puedes resolverlo con código. Hay tres opciones según lo que quieras conseguir.</p>
<h3>Opción 1: redirigir al contenido</h3>
<p>Esta es la <strong>opción más recomendable cuando la imagen está adjunta a una entrada o página</strong>. El visitante llega a una URL con contenido que merece la pena, que tiene eso, contenido, y Google entiende la relación entre la imagen y el texto.</p>
<p>Añade esto al <code>functions.php</code> de tu tema hijo o a un plugin de funciones:</p>
<pre>add_action( 'template_redirect', 'ayudawp_redirect_attachment_pages' );
function ayudawp_redirect_attachment_pages() {
    if ( ! is_attachment() ) {
        return;
    }

    $post_parent = wp_get_post_parent_id( get_queried_object_id() );

    if ( $post_parent ) {
        // Redirige al post padre si existe
        wp_safe_redirect( get_permalink( $post_parent ), 301 );
    } else {
        // Si no hay post padre, redirige a la home
        wp_safe_redirect( home_url(), 301 );
    }

    exit;
}</pre>
<h3>Opción 2: redirigir al archivo de imagen</h3>
<p>Si prefieres <strong>que la URL de adjunto lleve directamente a la imagen en lugar de a la publicación</strong> usa esto:</p>
<pre>add_action( 'template_redirect', 'ayudawp_redirect_attachment_to_file' );
function ayudawp_redirect_attachment_to_file() {
    if ( ! is_attachment() ) {
        return;
    }

    $attachment_url = wp_get_attachment_url( get_queried_object_id() );

    if ( $attachment_url ) {
        wp_safe_redirect( $attachment_url, 301 );
        exit;
    }
}</pre>
<h3>Opción 3: devolver un 404</h3>
<p>La tercera opción es <strong>decirle a Google directamente que esas páginas no existen</strong>. Es la más agresiva y hace que el rastreador deje de visitarlas del todo. Funciona bien si llevas tiempo con la web y ya tienes muchas páginas de adjunto indexadas que quieres eliminar del índice rápido.</p>
<pre>add_action( 'template_redirect', 'ayudawp_404_attachment_pages' );
function ayudawp_404_attachment_pages() {
    if ( ! is_attachment() ) {
        return;
    }

    global $wp_query;
    $wp_query-&gt;set_404();
    status_header( 404 );
    nocache_headers();
}</pre>
<h2>Qué elegir según tu caso</h2>
<ul>
<li><strong>Si tienes un blog o web de contenidos</strong> donde las imágenes siempre van dentro de artículos, redirigir a la publicación de donde partía es lo más limpio. El usuario llega a donde tiene que llegar y no pierdes nada.</li>
<li><strong>Si tienes una web con muchas imágenes que no están adjuntas</strong> a ningún contenido (galerías libres, páginas de porfolio subidas sueltas), redirigir al archivo puede tener sentido. También si usas las páginas de adjunto para algo concreto, como mostrar fichas de productos fotográficos, aunque ese caso es muy raro.</li>
<li><strong>El 404</strong> es útil si llevas mucho tiempo con la web, tienes cientos o miles de páginas de adjunto ya indexadas y quieres limpiar eso cuanto antes. Google las elimina del índice con más rapidez cuando devuelven 404 que cuando simplemente no las enlazas a nada.</li>
</ul>
<p>Lo que no tiene ningún sentido es no hacer nada. Si abres Search Console y ves decenas o cientos de páginas de adjunto en el informe de indexación, estás dejando que el rastreador gaste presupuesto en páginas que no te aportan nada.</p>
<h2>Un par de cosas antes de aplicarlo</h2>
<p>S ya tienes páginas de adjunto indexadas y las redirigirás con un 301, debes saber que Google tardará un tiempo en procesar los cambios. No esperes que Search Console se limpie de un día para otro, puede tardar semanas dependiendo de con qué frecuencia rastree tu web.</p>
<p>También debes comprobar que ninguna parte de tu web enlaza directamente a páginas de adjunto. A veces los maquetadores o algunos plugins de galerías enlazan a la URL del adjunto en lugar de al archivo de imagen directamente. Si rediriges esas URLs asegúrate de que los enlaces dentro de tu web también estén actualizados para no crear cadenas de redirecciones innecesarias.</p>
<p>Por último, si usas plugins de SEO no combines su opción de redirección con los códigos que hemos visto. Elige uno u otro o tendrás redirecciones en bucle.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ayudawp.com/problema-seo-adjuntos-wordpress/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Cómo proteger WordPress de ataques XSS</title>
		<link>https://ayudawp.com/ataques-xss-wordpress/</link>
					<comments>https://ayudawp.com/ataques-xss-wordpress/#comments</comments>
		
		<dc:creator><![CDATA[Fernando Tellado]]></dc:creator>
		<pubDate>Mon, 01 Jun 2026 06:28:00 +0000</pubDate>
				<category><![CDATA[Seguridad WordPress]]></category>
		<category><![CDATA[Tutoriales - Trucos]]></category>
		<category><![CDATA[WordPress.com]]></category>
		<category><![CDATA[WordPress.org]]></category>
		<category><![CDATA[Avanzado]]></category>
		<category><![CDATA[Principiante]]></category>
		<category><![CDATA[XSS]]></category>
		<guid isPermaLink="false">https://ayudawp.com/?p=159366</guid>

					<description><![CDATA[Defenderse de XSS no requiere ser pentester ni gastar dinero en plugins premium, basta con entender qué es un XSS, cómo aterriza en una web WordPress y dónde te conviene frenarlo.]]></description>
										<content:encoded><![CDATA[<p>Hay un plugin con más de 30.000 instalaciones activas que lleva meses sin actualizar y arrastra una <a href="https://wpscan.com/vulnerability/e54804c7-68a9-4c4c-94f9-1c3c9b97e8ca/" target="_blank" rel="nofollow noopener">vulnerabilidad XSS reportada en WPScan</a> que nadie ha corregido.</p>
<p>Me refiero a <em>Twenty20 Image Before-After</em>, y <strong>cualquier autor o colaborador con permisos para escribir puede dejarte un JavaScript malicioso</strong> guardado en una entrada esperando a que la abra el primer visitante, así que <strong>si lo tienes activo ahora mismo eres vulnerable</strong>.</p>
<p>No es un caso raro ni aislado, porque <strong>los ataques XSS (Cross-Site Scripting) están entre las vulnerabilidades más reportadas</strong> en aplicaciones web según <a href="https://ayudawp.com/owasp-wordpress/" target="_blank" rel="noopener">el OWASP Top 10</a>.</p>
<p>WordPress, por su naturaleza de CMS abierto con miles de plugins y temas de terceros que nadie audita en profundidad, acaba siendo objetivo recurrente, y Patchstack publica <strong>cientos de vulnerabilidades XSS al año solo en plugins WordPress</strong>, y muchas afectan a webs con decenas de miles de instalaciones activas que siguen sin parchear.</p>
<p>La buena noticia es que <strong>defenderse de XSS no requiere ser <code>pentester</code> ni gastar dinero en plugins premium</strong>, basta con <strong>entender qué es un XSS, cómo aterriza en una web WordPress y dónde te conviene frenarlo</strong>.</p>
<p>En esta guía te lo cuento aplicando la misma estrategia por capas que en la <a href="https://ayudawp.com/fuerza-bruta/" target="_blank" rel="ugc noopener">guía de defensa contra ataques de fuerza bruta</a>, porque al final <strong>cualquier defensa que se precie se monta así, de fuera hacia dentro</strong>.</p>
<p>Pero primero…</p>
<h2>Qué es un ataque XSS y por qué WordPress es objetivo recurrente</h2>
<p>Un XSS es un ataque que consigue <strong>meter código JavaScript</strong> donde no debería, y que <strong>el navegador del visitante ejecuta como si fuera parte legítima de tu web</strong>.</p>
<p>Lo curioso del asunto es que <strong>el atacante no toca tu servidor</strong>, no roba contraseñas de admin ni tiene que saltar tu cortafuegos, le basta con que tu web pinte en pantalla algo que un usuario haya escrito sin filtrarlo bien. <strong>El daño potencial es enorme</strong> y va por dentro, no por fuera.</p>
<p>Un XSS bien colocado puede <strong>robar la cookie de sesión de un administrador</strong> para entrar en su nombre, <strong>redirigir a tus visitantes a una web falsa</strong> que parece la tuya, <strong>recolectar datos de formularios</strong> antes de que se envíen, <strong>convertir el navegador del visitante en parte de una botnet</strong> o desfigurar la web con propaganda.</p>
<p>Lo peor es que <strong>todo eso pasa en el navegador del visitante, así que tu servidor sigue funcionando como si nada</strong> y muchas veces ni te enteras de que estás comprometido.</p>
<p><strong>WordPress es objetivo principal por dos razones</strong> que se refuerzan entre sí.</p>
<ol>
<li><strong>La cuota de mercado</strong>, que ronda el 42% de las webs del planeta, así que si vas a atacar webs en serie atacas WordPress y multiplicas resultados.</li>
<li><strong>El modelo de plugins y temas</strong>, donde cualquiera puede subir código al repositorio o vender un tema en un marketplace y meterlo en miles de webs sin que nadie haga auditoría a fondo.</li>
</ol>
<p>El núcleo de WordPress está bastante controlado, <strong>los plugins y temas son el coladero habitual</strong> y sino echa un vistazo a <a href="https://www.youtube.com/playlist?list=PLfssPFN20glHhHgKDNcJOGBGNjyr5cwTu" target="_blank" rel="nofollow noopener">los resúmenes de actualidad WordPress</a> que hago cada lunes para comprobarlo.</p>
<h2>Los tres tipos de XSS, con ejemplos aplicados a WordPress</h2>
<p>No todos los XSS funcionan igual, hay tres tipos clásicos y <strong>cada uno se cuela por una rendija distinta de tu WordPress</strong>. Saber distinguirlos te ayuda a saber dónde mirar cuando algo huele mal.</p>
<h3>XSS reflejado</h3>
<p>Es <strong>el más sencillo y el menos peligroso</strong> de los tres, aunque <strong>tampoco te confíes</strong> porque sigue haciendo daño. El código malicioso viaja <strong>en la URL y se ejecuta en cuanto el visitante hace clic</strong> en un enlace manipulado.</p>
<p>El escenario típico en WordPress es un plugin que muestra el parámetro de búsqueda en pantalla sin escaparlo, así que si el plugin recibe <code>?s=zapatillas</code> y pinta «No hay resultados para zapatillas», un atacante puede preparar un enlace tipo <code>tudominio.com/?s=&lt;script&gt;alert(1)&lt;/script&gt;</code> y mandárselo a la víctima por email, redes o un comentario en un foro.</p>
<p>Cuando la víctima hace clic, el navegador interpreta el <code>&lt;script&gt;</code> como código de tu web y lo ejecuta, y si en vez de un <code>alert</code> inofensivo lleva código que roba la cookie de sesión, <strong>el atacante puede entrar como ese usuario</strong>.</p>
<p>Otro ataque típico es el parámetro de mensaje de error o de notificación, esos <code>?msg=usuario+creado</code>, <code>?error=...</code> o <code>?notice=...</code> que muchos plugins pintan tal cual, y donde cabe lo que quieras meter si no hay algún tipo de filtrado.</p>
<h3>XSS almacenado</h3>
<p>Este es el malo de la película, porque <strong>el código malicioso se queda guardado en tu base de datos y se ejecuta cada vez que alguien visita la página afectada</strong>.</p>
<p>Aquí no hace falta engañar a nadie con un enlace, basta con que la víctima entre a leer un artículo, un comentario o una ficha de producto y ya tiene el regalo servido.</p>
<p>El caso del plugin Twenty20 que abre este artículo es exactamente este, porque el plugin tenía un shortcode <code>[twenty20]</code> con varios atributos (<code>img1</code>, <code>img2</code>, <code>before</code>, <code>after</code>, <code>offset</code>, <code>orientation</code>) que no se filtraban antes de pintarse en pantalla, así que cualquier usuario con permisos para escribir contenido podía dejar guardado en una entrada algo así:</p>
<pre>[twenty20 img1="javascript:alert(1)" img2="..." before="&lt;script&gt;alert(1)&lt;/script&gt;"]</pre>
<p>A partir de ese momento, cada visitante que abriese la entrada ejecutaba ese JavaScript, y si en vez de un <code>alert</code> hubiese un script que recopila cookies de admin o redirige al banco falso, el daño se multiplica por cada visita que reciba la entrada.</p>
<p>El XSS almacenado se cuela por sitios donde WordPress acepta que los usuarios introduzcan información, y la guarda para enseñarla luego.</p>
<p>Los candidatos típicos son:</p>
<ul>
<li><strong>Comentarios</strong> mal filtrados.</li>
<li><strong>Descripciones de productos</strong> en WooCommerce con HTML permitido sin filtrar.</li>
<li><strong>Formularios de contacto</strong> que muestran el último envío en el panel del admin.</li>
<li><strong>Campos personalizados</strong> que una plantilla custom pinta en el frontend sin pasar por <code>esc_html()</code></li>
<li><strong>Perfiles de autor</strong> con biografía editable.</li>
<li><strong>Ociones de plugins</strong> que se muestran en el escritorio sin escapar.</li>
</ul>
<p>Donde algo entra y luego sale sin filtrar hay candidato a XSS almacenado.</p>
<h3>XSS basado en DOM</h3>
<p>El más moderno de los tres y <strong>el que peor cubren los WAF</strong> tradicionales, porque <strong>el ataque ocurre íntegramente en el navegador y tu servidor ni se entera</strong> de que ha pasado.</p>
<p>El JavaScript de tu propia web lee algo del URL o del almacenamiento local del navegador y lo pinta en pantalla, y si no filtra bien lo que ha leído, el atacante consigue meter su código.</p>
<p>En WordPress aparece sobre todo en carruseles, maquetadores, bloques personalizados y plugins de calculadoras o formularios, <strong>cualquier cosa que use mucho JavaScript en en la parte pública de la web</strong>.</p>
<p>Un caso típico es el carrusel que lee el hash de la URL (<code>#slide=3</code>) y pinta el contenido correspondiente, o el plugin de búsqueda con autocompletado que muestra el término que el usuario está tecleando en tiempo real.</p>
<p>El XSS DOM es engañoso porque <strong>no aparece en los logs del servidor</strong>, no lo detectan los escáneres clásicos y muchos plugins de seguridad lo dejan pasar al no encontrar nada raro en la petición HTTP, ya que lo malicioso está íntegramente en el JavaScript del cliente.</p>
<h2>Errores fáciles de detectar aunque no seas técnico</h2>
<p><strong>No hace falta saber programar para descubrir si tu WordPress tiene problemas</strong> evidentes, hay tres <strong>pruebas tontas que puede hacer cualquiera en cinco minutos</strong> y que te dan pistas de sobra para saber si tienes que ponerte serio con la defensa.</p>
<ol>
<li><strong>HTML básico: </strong>Consiste en escribir <code>&lt;b&gt;probando&lt;/b&gt;</code> en el formulario de comentarios o en cualquier campo de texto que se vaya a mostrar después, y enviarlo. Si al ver el resultado aparece la palabra «<code>probando</code>» en negrita, tienes un problema, porque tu web está aceptando HTML donde no debería, y aunque eso no es XSS en sí, sí es por donde se pueden colar. Si acepta <code>&lt;b&gt;</code> es muy probable que acepte <code>&lt;script&gt;</code>.</li>
<li><strong>La prueba del parámetro reflejado:</strong> Añade a la URL de tu web un parámetro inventado con un valor distintivo, tipo <code style="font-size: 16px; font-style: inherit; font-weight: inherit;">?test=hola12345</code><span style="font-size: 16px;">, recorre la página y mira el código fuente con botón derecho buscando «<code>hola12345</code>». Si aparece tal cual, sin escapar, tienes un <strong>XSS reflejado en potencia</strong> porque algún plugin o el tema está pintando ese parámetro en algún sitio sin filtrarlo.</span></li>
<li><strong>La prueba del campo personalizado:</strong> Esta consiste en editar cualquier campo que tu tema o plugin pinte en el frontend (una biografía de autor, un campo personalizado, un campo de perfil), meterle <code style="font-size: 16px; font-style: inherit; font-weight: inherit;">&lt;b&gt;negrita&lt;/b&gt;</code><span style="font-size: 16px;">, guardar y recargar. Si sale en negrita en la parte pública de la web es que se pueden colar más cosas.</span></li>
</ol>
<p>Estas pruebas no son <code>pentesting</code>, son sentido común aplicado en segundos, y si alguna falla sabes que tienes que ponerte las pilas con las capas que vienen a continuación.</p>
<h2>Cómo saber si tu WordPress es vulnerable a ataques XSS</h2>
<p>Las pruebas anteriores te dicen si hay agujeros evidentes, pero no te dan la foto completa, para eso <strong>necesitas herramientas que escaneen de verdad</strong>. Hay <strong>4 gratuitas</strong> que van bien y son las que uso yo.</p>
<p>La primera es el <a href="https://herramientas.ayudawp.com/security-check/" target="_blank" rel="ugc noopener">analizador de seguridad que tienes en las herramientas WordPress</a>, que <strong>hace más de 30 comprobaciones</strong> sin tener siquiera que entrar a tu web y revisa cabeceras de seguridad, exposición de información, scripts sospechosos y archivos sensibles. No te dice si tienes XSS concretos, pero sí si tu web está bien armada contra esa clase de ataques.</p>
<p>Si tienes acceso a la web, o simplemente es la tuya, <strong>una versión mejor de la anterior</strong> es <a href="https://es.wordpress.org/plugins/vigilante/" target="_blank" rel="nofollow noopener">el analizador incluido en el plugin gratuito Vigilante</a>, que ya sí <strong>analiza además las tripas de tu WordPress, desde dentro</strong> con lo que <strong>el escaneo es más profundo, y efectivo</strong>.</p>
<p>Las otras dos son las <strong>bases públicas de vulnerabilidades de plugins y temas</strong> <a href="https://wpscan.com/" target="_blank" rel="nofollow noopener">WPScan</a> y <a href="https://patchstack.com/database/" target="_blank" rel="nofollow noopener">Patchstack</a>. Busca por nombre cada uno de tus plugins activos y mira si tienen XSS reportados sin parchear, y si alguno está como «<code>closed for download</code>» o lleva meses sin que el autor responda en los foros, plantéate sustituirlo, porque las vulnerabilidades no se arreglan solas.</p>
<p>Si quieres una pista rápida sin instalar nada, abre tu web en el navegador, pulsa <code>F12</code> y mira la consola, porque cuando aparecen mensajes con palabras tipo «<code>Refused to execute</code>», «<code>Content Security Policy violation</code>» o referencias a scripts de dominios que no reconoces, <strong>algo está pasando que merece la pena investigar</strong>.</p>
<h2>Dónde frenes el XSS lo cambia todo</h2>
<p>Igual que pasa con la fuerza bruta, <strong>no es lo mismo frenar un XSS a tres calles de tu casa que frenarlo cuando ya ha llegado al portal</strong>, y con XSS hay una diferencia que pesa todavía más, que es el daño potencial.</p>
<p>Un XSS que llega a ejecutarse en el navegador de un visitante puede robar su cookie de sesión y mandársela al atacante en milisegundos, y si ese visitante era un administrador, e<strong>l atacante tiene admin de tu WordPress sin haber pasado nunca por <code>wp-login</code></strong>.</p>
<p>Una vez dentro te <strong>instala una puerta trasera, modifica plugins, mete malware</strong> en archivos del tema y vienen los disgustos de verdad, así que <strong>cuanto más lejos del navegador del visitante frenes el ataque, mejor</strong> para todos.</p>
<p>La defensa funciona como <strong>tres filtros encadenados, cada uno más fino que el anterior</strong>. Lo que se le escapa al primero lo retiene el segundo, lo que pasa el segundo lo pilla el tercero, y si los tienes los tres bien puestos llega muy poca cosa al fondo.</p>
<ul>
<li>La <strong>CDN</strong> es el filtro más grueso, el que <strong>frena los patrones XSS conocidos antes de que la petición toque tu servidor</strong>, y desde donde puedes inyectar cabeceras de seguridad que mitigan el daño aunque el XSS exista.</li>
<li>El <strong>hosting</strong> es el filtro intermedio, el que <strong>bloquea peticiones maliciosas que han pasado la CDN</strong>, mantiene PHP actualizado para tapar agujeros conocidos a nivel servidor y <strong>aísla tu cuenta</strong> del resto.</li>
<li><strong>WordPress</strong> es el filtro fino, <strong>donde se mantiene todo el ecosistema</strong> actualizado, se gestionan cabeceras y CSP, se filtran entradas y se escapa la salida cuando tocas código.</li>
</ul>
<p><strong>Ninguno de los tres sustituye a los otros</strong>, así que si uno falla los demás siguen filtrando. Vamos a por cada uno.</p>
<h2>Filtro 1: la CDN como primer barrido</h2>
<p>Una CDN seria delante de tu WordPress <strong>es la mejor inversión gratuita que puedes hacer en seguridad</strong>.</p>
<p>En los ejemplos uso, como siempre, Cloudflare porque es el que utilizo y tiene plan gratuito de sobra, pero Bunny Shield, KeyCDN o Sucuri Firewall ofrecen funciones equivalentes y los conceptos que vienen valen para cualquiera.</p>
<h3>WAF gestionado contra patrones XSS</h3>
<ul>
<li><strong>Qué consigues</strong>: Que las peticiones con payloads XSS conocidos no lleguen ni siquiera a tu servidor, porque el filtrado ocurre en el borde de la red de Cloudflare a miles de kilómetros de tu hosting.</li>
<li><strong>Cómo funciona</strong>: El WAF aplica reglas basadas en el <a href="https://owasp.org/www-project-modsecurity-core-rule-set/" target="_blank" rel="nofollow noopener">OWASP Core Rule Set</a> que detectan patrones típicos de XSS como <code>&lt;script&gt;</code> en parámetros de URL, <code>javascript:</code> en valores donde se espera un URL, <code>onerror=</code> y compañía, y las peticiones que coinciden con esos patrones se bloquean o reciben un desafío automático.</li>
<li><strong>Cómo se hace en Cloudflare:</strong> El plan gratuito ya trae el OWASP Core Rule Set activado y lo encuentras en la sección de seguridad, en las llamadas «Conjunto de reglas administradas de Cloudflare» donde solo tienes que comprobar que esté en «On» y con nivel «Medium» o «High», porque con «Low» se cuelan demasiadas cosas. El plan Pro añade reglas gestionadas afinadas para WordPress.</li>
<li><strong>Pros</strong>: El filtrado lo gestiona Cloudflare con un equipo dedicado que actualiza las firmas constantemente y tú no tocas nada, los patrones nuevos se aplican solos.</li>
<li><strong>Contras</strong>: Ningún WAF gestionado pilla todo, sobre todo los XSS basados en DOM, porque el patrón malicioso no viaja en la petición HTTP, y los XSS específicos de un plugin concreto pueden no entrar en las firmas genéricas. Esto te quita un 80% del ruido, el otro 20% lo tiene que parar otra cosa.</li>
</ul>
<h3>Reglas personalizadas para tu caso</h3>
<p>Las reglas gestionadas funcionan bien para lo común, pero si has detectado un patrón de ataque específico contra tu web te merece la pena crear reglas a medida en <strong>Seguridad → Reglas de seguridad</strong>, que se aplican antes que las gestionadas.</p>
<p>Una regla útil de partida es bloquear cualquier petición cuya cadena de consulta (<code>query string</code>) contenga las cadenas más típicas de inyección, algo así:</p>
<pre>(http.request.uri.query contains "&lt;script") or 
(http.request.uri.query contains "javascript:") or 
(http.request.uri.query contains "onerror=") or 
(http.request.uri.query contains "onload=")</pre>
<p>La acción puede ser <code>Block</code> directo o <code>Managed Challenge</code> si prefieres dar opción a falsos positivos.</p>
<p>La gracia de estas reglas es que filtran lo que de verdad te ataca a ti, pero el coste es que tienes que revisar los logs cada cierto tiempo para detectar patrones nuevos y ajustar.</p>
<h3>Cabeceras de seguridad desde la CDN</h3>
<p>Esta es la jugada que casi nadie aprovecha y que es canela fina, porque puedes añadir o reescribir cabeceras de seguridad desde Cloudflare sin tocar tu servidor ni tu WordPress, y eso te viene de maravilla si tu hosting no te deja modificar cabeceras o quieres aplicar la misma política a varias webs de golpe.</p>
<p>En Cloudflare lo configuras en <strong>Reglas → Configuración → Encabezados de respuestas HTTP</strong> y activas las cabeceras que te interesen.</p>
<p>La más importante contra XSS es <strong>Content-Security-Policy</strong>, que le dice al navegador qué scripts puede ejecutar y desde dónde, de manera que aunque un atacante consiga meter un <code>&lt;script&gt;</code> en tu web, si viene de un dominio que no está en tu CSP el navegador se niega a ejecutarlo.</p>
<p>Las complementarias son <strong>X-Content-Type-Options</strong> con valor <code>nosniff</code> para evitar que el navegador interprete archivos de un tipo distinto del declarado, y <strong>Referrer-Policy</strong> con <code>strict-origin-when-cross-origin</code> para reducir la información que se filtra a sitios externos.</p>
<p>Si quieres montarte la CSP a medida sin pelearte con la sintaxis, tienes <a href="https://herramientas.ayudawp.com/security-headers/" target="_blank" rel="ugc noopener">el generador de cabeceras de seguridad de Ayuda WordPress</a> para sacar la cadena correcta con interfaz visual, y una vez la tengas la pegas en la regla de Cloudflare y listo.</p>
<p>La pega de la CSP es que configurarla bien lleva su tiempo, porque si te pasas de estricta rompes Google Analytics, Tag Manager, captchas y la mitad de los plugins que cargan recursos externos.</p>
<p>La estrategia que funciona es empezar con <code>Content-Security-Policy-Report-Only</code> durante una semana o dos, ver qué se rompería en producción real, ajustar y solo entonces pasar a la modalidad de bloqueo.</p>
<h3>Modo Bot Fight</h3>
<p>La mayoría de los ataques XSS son oportunistas, no dirigidos, y los lanza un bot que prueba miles de URLs con payloads conocidos a ver si pillan algo, así que filtrar bots ya te quita parte del ruido sin afectar a nadie real.</p>
<p>Lo activas en <strong>Seguridad → Configuración → Tráfico de bots → Modo Bot Fight</strong> y va solo, aunque conviene comprobar que tus servicios automatizados legítimos (monitorización, backups externos, y sobre todo pasarelas de pago) siguen funcionando bien después.</p>
<h2>Filtro 2: el hosting como malla intermedia</h2>
<p>El hosting <strong>tiene un papel en la defensa contra XSS que la mayoría subestima</strong>, porque no es donde se filtran las peticiones HTTP (de eso ya se ocupa la CDN) sino <strong>donde se cierran los huecos que permiten que un XSS escale a algo peor</strong>.</p>
<p>Sigo con SiteGround en los ejemplos porque es el hosting que uso y conozco a fondo, pero <a href="https://ayudawp.com/hosting-wordpress/" target="_blank" rel="ugc noopener">los hosting serios para WordPress</a> traen funciones equivalentes en sus paneles.</p>
<h3>WAF a nivel servidor</h3>
<p>Los hosting de calidad traen <code>mod_security</code> con el OWASP Core Rule Set o un WAF propio que filtra ataques antes de que la petición llegue a PHP, y esto no es algo que tú configures, viene <strong>activado por defecto</strong>. Lo mejor es que preguntes a tu hosting, y si duda malo, empieza a buscar alteranativas.</p>
<p>SiteGround tiene un WAF propio con reglas escritas por su equipo de seguridad según van apareciendo vulnerabilidades nuevas en el ecosistema WordPress, y casi todos los buenos hosting ofrecen capas equivalentes.</p>
<p>Si tu hosting no te garantiza un WAF a nivel servidor, lo que tienes es un problema de hosting más que de WordPress.</p>
<h3>Mantén PHP actualizado</h3>
<p>Suena obvio pero te sorprendería saber cuántas webs hay funcionando todavía en PHP 7.2, 7.0 o incluso 5.6, y <strong>las versiones antiguas de PHP no reciben parches de seguridad</strong>.</p>
<p>La mayoría de XSS son problema de la aplicación y no del lenguaje, pero hay funciones de PHP relacionadas con el manejo de cadenas, la codificación de URLs y el filtrado de entrada que han recibido mejoras en cada versión, así que ejecutar la última versión te ahorra dolores de cabeza y disgustos.</p>
<p>WordPress recomienda <strong>PHP 8.3 o superior</strong>, y si estás por debajo cambia ya, que en la mayoría de hosting se hace desde el panel en dos clics.</p>
<h3>Aislamiento entre cuentas</h3>
<p>En hosting compartidos malos <strong>una web vulnerable puede contaminar a las vecinas en el mismo servidor</strong>, así que <strong>los hosting profesionales aíslan cada cuenta</strong> para que un compromiso en una web no afecte al resto.</p>
<p>SiteGround y otros lo llaman aislamiento de cuentas, y otros tienen nombres parecidos. No tienes que configurar nada, solo <strong>comprobar que tu hosting lo ofrece, y si no es razón suficiente para cambiarte</strong>.</p>
<h3>El plugin de seguridad del hosting</h3>
<p>Algunos hosting tienen su propio plugin de seguridad para WordPress, SiteGround tiene <a href="https://es.wordpress.org/plugins/sg-security/" target="_blank" rel="nofollow noopener">Security Optimizer</a>, que se puede usar en cualquier hosting y no solo en SiteGround, otros no lo sé, pero pregunta.</p>
<p>Este tiene 2FA, URL de login personalizada, registro de actividad y ocultación de la versión de WordPress, aunque no incluye WAF de aplicación propio, así que conviene combinarlo con un plugin que sí lo traiga o con un WAF en CDN.</p>
<p>Y como siempre, si ya usas otro plugin de seguridad con WAF no instales dos a la vez, porque suelen pelearse.</p>
<h2>Filtro 3: WordPress, el colador más fino</h2>
<p>Esta es la capa <strong>donde más control tienes y la que más afecta a la defensa contra XSS específicos de WordPress</strong>, porque las capas anteriores frenan el ruido y los patrones conocidos, pero <strong>los XSS específicos de un plugin o un tema concreto solo los frenas desde aquí</strong>.</p>
<h3>Mantén todo actualizado, esta es la regla número uno</h3>
<p>La gran mayoría de XSS que afectan a WordPress vienen de plugins o temas con vulnerabilidades conocidas, parcheadas en versiones posteriores, en webs donde nadie actualiza desde hace meses. <strong>Si llevas las actualizaciones al día te ahorras el 80% del problema</strong> antes de que exista.</p>
<p>El plan es sencillo y se resume en tener actualizaciones automáticas activadas para todo, core, plugins y temas, <strong>sustituir cualquier plugin que lleve más de seis meses sin actualizar, y prescindir sí o sí de los que llevan más de un año o cuyo autor no responde en el foro de soporte</strong>.</p>
<p>El caso de Twenty20 es exactamente esto, un plugin abandonado con una vulnerabilidad reportada en WPScan desde hace tiempo, que solo se puede usar parchándolo por tu cuenta o sustituyéndolo por otro.</p>
<p>Lo que mucha gente no sabe es que <strong>ni siquiera los plugins de seguridad más populares son inmunes a estos problemas</strong>. Wordfence, con 5 millones de instalaciones activas, tiene <a href="https://wpscan.com/plugin/wordfence/" target="_blank" rel="nofollow noopener">13 vulnerabilidades reportadas en WPScan</a>, varias de XSS en su propio código en 2014, 2018 y 2022.</p>
<p>Las han ido parcheando en cada versión, pero <strong>si no actualizas el plugin de seguridad, ni el propio plugin de seguridad te protege</strong>, así que <strong>la actualización no es opcional ni accesoria, es la base de todo lo demás</strong>.</p>
<h3>Plugin de seguridad con WAF de aplicación</h3>
<p>Un plugin de seguridad con cortafuegos dentro de WordPress es la última red para todo lo que se cuela por las capas anteriores. Cuando entras en este terreno hay varias opciones gratuitas decentes, y la elección depende menos de cuál es mejor en abstracto y más de qué encaja con tu caso.</p>
<ul>
<li><a href="https://es.wordpress.org/plugins/all-in-one-wp-security-and-firewall/" target="_blank" rel="nofollow noopener">All-In-One Security (AIOS)</a> es muy completo, con reglas PHP firewall específicas contra XSS, las reglas 6G de Perishable Press, 2FA con integración nativa para WooCommerce, Elementor Pro y bbPress, smart 404 y audit log. Su cortafuegos depende mucho de <code>.htaccess</code> y en Nginx pierde parte de su fuerza, así que tenlo en cuenta si tu hosting es Nginx puro.</li>
<li><a href="https://es.wordpress.org/plugins/wordfence/" target="_blank" rel="nofollow noopener">Wordfence</a> es el más popular, y trae WAF con base de firmas amplia gestionada por su equipo, que además es CNA y asigna identificadores CVE. En la versión gratuita las firmas nuevas se aplican con 30 días de retraso respecto a la Premium, ese es el modelo de negocio, pero como cortafuegos gratuito sigue cumpliendo relativamente bien. Yo tengo una mala opinión por otras cosas, pero tú decides.</li>
<li><a href="https://es.wordpress.org/plugins/vigilante/" target="_blank" rel="nofollow noopener">Vigilante</a> es 100% gratis sin opciones de pago, con WAF de aplicación que filtra patrones XSS, SQLi y LFI, CSP visual, 2FA por correo y aplicación, escáner de integridad de archivos, modo bajo ataque y registro de auditoría de actividad. Es el que yo desarrollo y mantengo activamente, y que utilizo en todas mis. webs y las de mis clientes, está pensado para tener todo en un solo plugin sin pagar por nada. Como es nuevo todavía tiene poca base instalada, ese es su único pero (de momento).</li>
</ul>
<p>Los tres bloquean payloads XSS conocidos a nivel de aplicación, y cuál elegir depende de si quieres todo en uno sin pagar (Vigilante o AIOS), si prefieres la base de firmas más amplia aceptando pagar por tenerla al día (Wordfence Premium), o si tu hosting es Apache y quieres aprovechar a tope las reglas <code>.htaccess</code> (AIOS).</p>
<h3>Cabeceras de seguridad y Content Security Policy</h3>
<p>La CSP es <strong>la cabecera que más frena los XSS</strong> con diferencia, porque aunque un atacante consiga meter un <code>&lt;script&gt;</code> en tu base de datos, <strong>si tu CSP solo permite scripts de tu propio dominio ese script externo no se ejecuta</strong>.</p>
<p>Si ya configuras las cabeceras desde Cloudflare en el filtro 1 no hace falta repetirlas aquí, sería duplicar trabajo, pero <strong>si no usas CDN o tu CDN no te deja tocar cabeceras, gestiónalas desde el plugin de seguridad</strong>.</p>
<p>Vigilante trae ajustes precisos para CSP, AIOS también gestiona cabeceras, y Security Optimizer las trae configuradas por defecto con valores razonables.</p>
<p>El <a href="https://herramientas.ayudawp.com/security-headers/" target="_blank" rel="ugc noopener">generador de cabeceras de Ayuda WordPress</a> te vale igual para sacar la cadena CSP a medida y pegarla donde te convenga, sea en el plugin, en el <code>.htaccess</code> o en una regla de Cloudflare.</p>
<h3>2FA en todas las cuentas con permisos</h3>
<p>Esta es <strong>defensa en profundidad pura</strong>, porque si un XSS consigue robar la cookie de sesión de un administrador, el atacante todavía no podrá iniciar sesión nueva en otro dispositivo sin el segundo factor, así que el daño queda limitado a la sesión activa que pueda exprimir antes de que la víctima cierre el navegador.</p>
<p>Activa <a href="https://ayudawp.com/tag/2fa/" target="_blank" rel="ugc noopener">la identificación en dos pasos</a> para administradores, editores y autores, que es la gente con permisos suficientes para meterte un disgusto.</p>
<p>La mayoría de plugins de seguridad la traen integrada, pero si prefieres un plugin específico el más completo gratuito es <a href="https://es.wordpress.org/plugins/two-factor/" target="_blank" rel="nofollow noopener">Two-Factor</a>.</p>
<h3>HTML controlado en comentarios y formularios</h3>
<p>WordPress filtra los comentarios por defecto con <code>wp_kses</code>, pero <strong>algunos plugins de comentarios o formularios desactivan ese filtrado para permitir formato enriquecido, y esto es mala idea</strong> casi siempre.</p>
<p>Si tienes que permitir HTML en comentarios, <strong>restringe las etiquetas a las imprescindibles</strong> (<code>&lt;p&gt;</code>, <code>&lt;a&gt;</code>, <code>&lt;b&gt;</code>, <code>&lt;i&gt;</code>) y no permitas nunca <code>&lt;script&gt;</code>, <code>&lt;iframe&gt;</code> ni <code>&lt;object&gt;</code>.</p>
<p>Y en plugins de formularios de contacto o captura de leads, <strong>comprueba que los datos del usuario se filtran antes de mostrarse en el panel de admin</strong>, porque si tu CRM o tu plugin de formularios pinta el último envío con un avance en el escritorio sin filtrar, ahí entra perfectamente un XSS almacenado dirigido contra ti.</p>
<h3>Si tocas código hay tres reglas básicas</h3>
<p>Si desarrollas plugins, temas personalizados o snippets que pegas en <code>functions.php</code>, <strong>tienes responsabilidad directa en la defensa contra ataques XSS</strong>, y las reglas son las mismas que pone <a href="https://developer.wordpress.org/apis/security/" target="_blank" rel="nofollow noopener">la documentación oficial de WordPress</a>.</p>
<ol>
<li><strong>Validar todo lo que entra</strong>, comprobando antes de hacer nada que el dato del usuario tiene el formato esperado, así que si esperas un número validas que es número, si esperas un email validas el email, y si esperas un valor de una lista cerrada (por ejemplo <code>horizontal</code> o <code>vertical</code>) rechazas cualquier otra cosa que no esté en la lista.</li>
<li><strong>Sanear todo lo que guardas</strong>, pasando los datos por la función adecuada según el tipo, con <code style="font-size: 16px; font-style: inherit; font-weight: inherit;">sanitize_text_field()</code><span style="font-size: 16px;"> para texto plano, </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">sanitize_email()</code><span style="font-size: 16px;"> para emails, </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">esc_url_raw()</code><span style="font-size: 16px;"> para URLs que van a la base de datos, y </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">wp_kses_post()</code><span style="font-size: 16px;"> o </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">wp_kses()</code><span style="font-size: 16px;"> con una lista blanca de etiquetas cuando necesitas permitir algo de HTML.</span></li>
<li><strong>Escapar todo lo que sacas</strong>, en cada sitio donde pintas algo en pantalla, usando la función adecuada según el contexto, con <code style="font-size: 16px; font-style: inherit; font-weight: inherit;">esc_html()</code><span style="font-size: 16px;"> para texto dentro de etiquetas, </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">esc_attr()</code><span style="font-size: 16px;"> para valores de atributos, </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">esc_url()</code><span style="font-size: 16px;"> para URLs y </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">esc_js()</code><span style="font-size: 16px;"> para datos dentro de JavaScript inline.</span></li>
</ol>
<p>El parche que escribí para el plugin Twenty20 abandonado, de nuevo, es el ejemplo perfecto de cómo aplicar las tres reglas en un caso real, porque el shortcode no validaba ni saneaba sus atributos y enganché un hook que los procesa antes de pasarlos al plugin original. Quedaba así:</p>
<pre>// IDs de imagen: solo números o URLs válidas
$atts['img1'] = is_numeric( $atts['img1'] ) 
    ? absint( $atts['img1'] ) 
    : esc_url_raw( $atts['img1'] );

// Offset: decimal entre 0 y 1
$atts['offset'] = max( 0, min( 1, floatval( $atts['offset'] ) ) );

// Orientación: solo valores permitidos
$atts['orientation'] = in_array( $atts['orientation'], array( 'horizontal', 'vertical' ), true )
    ? $atts['orientation']
    : 'horizontal';

// Textos: saneados como texto plano
$atts['before_label'] = sanitize_text_field( $atts['before_label'] );
$atts['after_label']  = sanitize_text_field( $atts['after_label'] );</pre>
<p>Cada atributo se valida según lo que se espera de él, así que los IDs van como números, el offset como decimal entre 0 y 1, la orientación como uno de dos valores fijos, y los textos pasan por el saneador estándar de WordPress.</p>
<p>Eso convierte un payload tipo <code>before="&lt;script&gt;alert(1)&lt;/script&gt;"</code> en texto plano inofensivo. Tienes <a href="https://ayudawp.com/parche-xss-twenty20/" target="_blank" rel="ugc noopener">el parche completo en el artículo dedicado</a> y el código en <a href="https://github.com/fernandotellado/twenty20-image-before-after-xss-security-patch" target="_blank" rel="nofollow noopener">mi repositorio de GitHub</a> por si te interesa curiosear.</p>
<h2>Qué hago si ya están explotando un XSS en mi web</h2>
<p>Si <strong>has detectado un XSS activo</strong> porque ves JavaScript raro en el código fuente, Google te ha marcado la web como peligrosa o algún visitante te ha avisado de que la web le redirige a sitios extraños, <strong>esto es lo que tienes que hacer, en orden, desde fuera hacia dentro</strong>.</p>
<p><strong>Primero activa el modo bajo ataque</strong> en la CDN, que en Cloudflare lo tienes en el panel principal del dominio. Cualquier visitante pasa un desafío JavaScript antes de cargar la web y el ataque deja de propagarse mientras tú investigas. No es agradable para los visitantes legítimos, pero priorizas contener el daño.</p>
<p><strong>A continuación localiza el origen del XSS</strong>. Si es reflejado viene por algún parámetro de URL, así que mira los logs del hosting buscando peticiones con cadenas raras, y si es almacenado está en tu base de datos, y en este caso busca en las tablas <code style="font-size: 16px; font-style: inherit; font-weight: inherit;">wp_posts</code><span style="font-size: 16px;">, </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">wp_postmeta</code><span style="font-size: 16px;">, </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">wp_options</code><span style="font-size: 16px;"> y </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">wp_comments</code><span style="font-size: 16px;"> cualquier ocurrencia de </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">&lt;script</code><span style="font-size: 16px;">, </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">javascript:</code><span style="font-size: 16px;">, </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">onerror=</code><span style="font-size: 16px;"> u </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">onload=</code><span style="font-size: 16px;">. Donde encuentres algo en un campo que no debería tener HTML, ese es tu XSS.</span></p>
<p>Una vez localizado el origen, sea un plugin que no escapaba un parámetro o un tema personalizado que pintaba un custom field sin filtrar, <strong>desactiva al culpable</strong> desde el panel o renombrando la carpeta por FTP si te bloquea el acceso.</p>
<p>El tercer paso solo aplica si era XSS almacenado, y consiste en <strong>limpiar la base de datos</strong>, porque parchear el plugin no basta, el JavaScript malicioso sigue guardado y se ejecutará la próxima vez. Con WP-CLI lo haces así, primero en seco para ver qué afecta y luego en serio:</p>
<pre>wp search-replace '&lt;script&gt;...código malicioso...&lt;/script&gt;' '' --all-tables --dry-run</pre>
<p>Cuando estés seguro de lo que va a cambiar quita el <code>--dry-run</code> y ejecútalo de verdad. Si no te apañas WP-CLI, lo mismo se hace con phpMyAdmin buscando el patrón en cada tabla afectada.</p>
<p>Primero localizas dónde está el patrón con la pestaña «<strong>Buscar</strong>» al nivel de base de datos (no de tabla). Entras en tu base de datos, pulsas en <em>Buscar</em>, pegas el código malicioso en el campo de búsqueda, marcas <em>la frase exacta</em> y seleccionas todas las tablas. Esto te dice en qué tablas y cuántas filas están afectadas sin tocar nada todavía.</p>
<p>Cuando ya sabes qué tablas tienen el problema, haces una copia de seguridad de la base de datos y pasas a la pestaña <strong>SQL</strong> para ejecutar el reemplazo real con la función <code>REPLACE</code> de MySQL. Las tablas habituales donde se aloja XSS almacenado son <code>wp_posts</code>, <code>wp_postmeta</code>, <code>wp_options</code> y <code>wp_comments</code> (ojo si tu prefijo no es <code>wp_</code>).</p>
<p>Para contenido de entradas y páginas:</p>
<pre>UPDATE wp_posts
SET post_content = REPLACE(post_content, '&lt;script&gt;...código malicioso...&lt;/script&gt;', '')
WHERE post_content LIKE '%&lt;script&gt;...código malicioso...%';</pre>
<p>Para comentarios:</p>
<pre>UPDATE wp_comments
SET comment_content = REPLACE(comment_content, '&lt;script&gt;...código malicioso...&lt;/script&gt;', '')
WHERE comment_content LIKE '%&lt;script&gt;...%';</pre>
<p>El <code>WHERE</code> no es imprescindible pero limita el <code>UPDATE</code> a las filas que realmente contienen el patrón, así evitas reescribir toda la tabla sin necesidad. phpMyAdmin te mostrará cuántas filas se han modificado al ejecutar la consulta, que es lo más parecido a la confirmación que da WP-CLI.</p>
<blockquote><p><strong>Precaución importante con los datos serializados: </strong>Las tablas <code>wp_options</code> (campo <code>option_value</code>) y <code>wp_postmeta</code> (campo <code>meta_value</code>) suelen contener arrays serializados de PHP, donde la longitud de cada string forma parte del propio dato. Si haces un REPLACE directo y el script malicioso está dentro de un valor serializado, vas a romper la serialización y dejar la opción o el meta inservibles. Aquí WP-CLI gana por goleada porque sabe deserializar y volver a serializar al vuelo. Si el patrón aparece en <code>wp_options</code> o <code>wp_postmeta</code> y no puedes usar WP-CLI, lo más seguro es editar manualmente cada fila afectada desde phpMyAdmin ajustando el contenido sin romper la estructura.</p></blockquote>
<p>Un detalle con las comillas es que si el JavaScript malicioso tiene comillas simples dentro, escápalas con barra invertida (<code>\'</code>) o cambia los delimitadores de la cadena SQL a comillas dobles.</p>
<p><strong>El cuarto paso</strong> <strong>es forzar el restablecimiento de contraseñas de todos los administradores</strong>, porque si el XSS pudo robar cookies de sesión tienes que asumir que tu sesión está comprometida.</p>
<p>La mayoría de plugins de seguridad  (los buenos) traen una herramienta de restablecer contraseñas para todos los usuarios que invalida sesiones activas y obliga a iniciar sesión de nuevo, y aprovecha el momento para activar 2FA si no lo tenías.</p>
<p><strong>Para finalizar revisa los logs y aprende del incidente</strong>. Mira el registro de actividad de tu plugin de seguridad para entender cuándo se metió el XSS, qué usuario o IP lo coló y qué pasó después, porque esa información te sirve para crear reglas nuevas en la CDN y reforzar la defensa contra el siguiente intento.</p>
<h2>Errores típicos que cometemos todos al tratar con ataques XSS</h2>
<p>Te lo digo desde la experiencia (mala), que es de lo que mejor se aprende, luego tú decides:</p>
<ul>
<li><strong>Confiar solo en el plugin de seguridad, que es la última capa y no la primera</strong>, porque si todo el peso recae en él cada ataque consume recursos de PHP antes de que pueda filtrarlo.</li>
<li><strong>No usar CDN con WAF</strong>, cuando Cloudflare gratuito ya filtra patrones XSS conocidos y es la inversión cero más rentable que existe en seguridad WordPress.</li>
<li><strong>No mantener todo actualizado</strong>, porque la mayoría de XSS que afectan a WordPress vienen de plugins desactualizados con vulnerabilidades parcheadas en versiones posteriores, así que actualizar te ahorra el 80% del problema.</li>
<li><strong>Usar plugins abandonados</strong>, como el caso Twenty20, porque si el autor no actualiza no hay parches y las vulnerabilidades persisten para siempre. Busca alternativas o aplica parches comunitarios.</li>
<li><strong>Permitir HTML libre en comentarios o formularios</strong>, cuando casi nunca lo necesitas y siempre puedes restringir las etiquetas con <code>wp_kses</code> a las imprescindibles.</li>
<li><strong>No filtrar campos personalizados en plantillas a medida</strong>, porque si tu tema imprime un campo personalizado en la parte pública de la web sin pasar por <code>esc_html()</code> cualquiera con permisos para editar ese campo puede meterte scripts.</li>
<li><strong>Asumir que muchas instalaciones equivale a seguridad</strong>, cuando plugins con cientos de miles de instalaciones aparecen en WPScan con XSS reportados todos los meses y la popularidad no es garantía de nada.</li>
<li><strong>Configurar mal la CSP</strong>, porque una CSP demasiado laxa con <code>unsafe-inline</code> o <code>unsafe-eval</code> no defiende contra XSS y solo da sensación de hacerlo, mientras que una demasiado estricta rompe la web. La estrategia que funciona es empezar en modo Report-Only y refinar antes de poner en bloqueo.</li>
<li><strong>No tener copias de seguridad</strong>, porque si todo falla y un XSS escala a compromiso total del WordPress, una copia limpia es lo único que te salva.</li>
</ul>
<h2>El plan completo en plan chuleta</h2>
<p>Aquí tienes el resumen aplicable de todo lo visto, de fuera hacia dentro, para que lo configures en orden y en una tarde.</p>
<h3>Capa 1: CDN</h3>
<table>
<thead>
<tr>
<th>Acción</th>
<th>Prioridad</th>
<th>Herramienta</th>
<th>Impacto en usuario</th>
</tr>
</thead>
<tbody>
<tr>
<td>WAF gestionado con OWASP CRS activo</td>
<td>Alta</td>
<td>Cloudflare → Seguridad → Reglas de seguridad</td>
<td>Ninguno</td>
</tr>
<tr>
<td>Reglas personalizadas contra patrones XSS</td>
<td>Alta</td>
<td>Cloudflare → Reglas de seguridad</td>
<td>Mínimo</td>
</tr>
<tr>
<td>Cabeceras de seguridad y CSP</td>
<td>Alta</td>
<td>Cloudflare → Reglas → Configuración → Encabezados de respuestas HTTP</td>
<td>Ninguno bien configurada</td>
</tr>
<tr>
<td>Modo Bot Fight</td>
<td>Media</td>
<td>Cloudflare → Seguridad → Bots</td>
<td>Ninguno</td>
</tr>
<tr>
<td>Modo Under Attack</td>
<td>Emergencias</td>
<td>Cloudflare → Escritorio</td>
<td>Alto, todos pasan desafío</td>
</tr>
</tbody>
</table>
<h3>Capa 2: Hosting</h3>
<table>
<thead>
<tr>
<th>Acción</th>
<th>Prioridad</th>
<th>Herramienta</th>
<th>Impacto en usuario</th>
</tr>
</thead>
<tbody>
<tr>
<td>WAF a nivel servidor activo</td>
<td>Alta</td>
<td>Hosting, viene activado</td>
<td>Ninguno</td>
</tr>
<tr>
<td>PHP en versión 8.1 o superior</td>
<td>Alta</td>
<td>Panel del hosting</td>
<td>Ninguno</td>
</tr>
<tr>
<td>Aislamiento de cuentas</td>
<td>Alta</td>
<td>Hosting, debe ofrecerlo</td>
<td>Ninguno</td>
</tr>
<tr>
<td>Plugin de seguridad del hosting si no hay otro</td>
<td>Media</td>
<td>Security Optimizer o equivalente</td>
<td>Ninguno</td>
</tr>
</tbody>
</table>
<h3>Capa 3: WordPress</h3>
<table>
<thead>
<tr>
<th>Acción</th>
<th>Prioridad</th>
<th>Herramienta</th>
<th>Impacto en usuario</th>
</tr>
</thead>
<tbody>
<tr>
<td>Actualizaciones automáticas de WordPress, plugins, temas</td>
<td>Crítica</td>
<td>WordPress</td>
<td>Ninguno</td>
</tr>
<tr>
<td>Plugin de seguridad con WAF de aplicación</td>
<td>Alta</td>
<td>Vigilante, AIOS o Wordfence</td>
<td>Ninguno</td>
</tr>
<tr>
<td>CSP configurada en plugin si no está en CDN</td>
<td>Alta</td>
<td>Vigilante, AIOS o generador de cabeceras</td>
<td>Ninguno si está bien configurada</td>
</tr>
<tr>
<td>2FA para administradores, editores y autores</td>
<td>Alta</td>
<td>Vigilante o Two-Factor</td>
<td>Bajo, un paso más en el login</td>
</tr>
<tr>
<td>HTML restringido en comentarios y formularios</td>
<td>Alta</td>
<td>Configuración de WordPress</td>
<td>Mínimo</td>
</tr>
<tr>
<td>Campos personalizados escapados en plantillas</td>
<td>Alta</td>
<td>Código del tema</td>
<td>Ninguno</td>
</tr>
<tr>
<td>Registro de actividad activo</td>
<td>Media</td>
<td>Vigilante o WP Activity Log</td>
<td>Ninguno</td>
</tr>
</tbody>
</table>
<h3>Seguridad continua</h3>
<table>
<thead>
<tr>
<th>Acción</th>
<th>Frecuencia</th>
</tr>
</thead>
<tbody>
<tr>
<td>Comprobar plugins en WPScan y Patchstack</td>
<td>Semanal</td>
</tr>
<tr>
<td>Revisar errores JavaScript en consola del navegador</td>
<td>Semanal</td>
</tr>
<tr>
<td>Pasar el analizador de seguridad externo</td>
<td>Mensual</td>
</tr>
<tr>
<td>Auditar plugins activos y eliminar los que no se usan</td>
<td>Mensual</td>
</tr>
<tr>
<td>Sustituir plugins sin actualizar desde hace más de 6 meses</td>
<td>Cuando los detectes</td>
</tr>
<tr>
<td>Revisar logs del CDN y del hosting</td>
<td>Cuando pase algo raro</td>
</tr>
<tr>
<td>Comprobar copias de seguridad</td>
<td>Semanal</td>
</tr>
</tbody>
</table>
<p>Todo lo que ves aquí lo puedes configurar en poco rato, <strong>sin gastar dinero en plugins premium ni servicios de pago</strong>, porque <strong>la diferencia entre una web con XSS explotables y una web protegida no está en cuánto te gastes sino en si tienes los tres filtros montados y trabajando juntos</strong>. Si uno falla los otros siguen filtrando, esa es toda la gracia del asunto.</p>
<p>Si llevas tiempo sin revisar la seguridad de tu web empieza pasando el <a href="https://herramientas.ayudawp.com/security-check/" target="_blank" rel="ugc noopener">analizador de seguridad de Ayuda WordPress</a> para ver qué tienes expuesto, busca tus plugins activos en WPScan, y monta los tres filtros siguiendo las tablas de arriba.</p>
<p>Cuando termines vuelve a pasar el analizador y verás la diferencia. Y, recuerda, por terminar por donde empecé, si tienes Twenty20 instalado, instálale <a href="https://ayudawp.com/parche-xss-twenty20/" target="_blank" rel="ugc noopener">el parche</a> o sustitúyelo cuanto antes por otro plugin del tipo <em>antes-después</em> de los muchos que hay, que no es el único.</p>
<p>¿Dudas, abrazos, insultos? lo que quieras ahí abajo, en los comentarios.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ayudawp.com/ataques-xss-wordpress/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>¿Sabías que WordPress puede crear la meta description sin necesidad plugins SEO</title>
		<link>https://ayudawp.com/meta-description-automatica/</link>
					<comments>https://ayudawp.com/meta-description-automatica/#comments</comments>
		
		<dc:creator><![CDATA[Fernando Tellado]]></dc:creator>
		<pubDate>Thu, 28 May 2026 06:28:00 +0000</pubDate>
				<category><![CDATA[Programación + WordPress]]></category>
		<category><![CDATA[SEO / AEO / GEO / LLMO]]></category>
		<category><![CDATA[Tutoriales - Trucos]]></category>
		<category><![CDATA[WordPress.com]]></category>
		<category><![CDATA[WordPress.org]]></category>
		<category><![CDATA[Avanzado]]></category>
		<category><![CDATA[EEAT]]></category>
		<category><![CDATA[Experto]]></category>
		<category><![CDATA[meta description]]></category>
		<guid isPermaLink="false">https://ayudawp.com/?p=159101</guid>

					<description><![CDATA[¿Sabías que no necesita un plugin de SEO para generar las meta description, que puedes crearlas con información que ya ofrece WordPress?]]></description>
										<content:encoded><![CDATA[<p dir="auto" data-line="428"><del>Una</del> Otra de las <strong>carencias raras-estúpidas-incomprensibles de WordPress</strong> para ser una <strong>máquina perfecta de SEO</strong> es el hecho de que <strong>no aprovecha lo que ya tiene para generar las tags <code>meta description</code> para SEO</strong>.</p>
<p dir="auto" data-line="428"><strong>Es raro</strong> porque, por si no lo sabías, <strong>WordPress sí crea automáticamente la etiqueta</strong> meta <code>&lt;title&gt;</code> a partir de, adivina, sí, a partir del título de la publicación (entrada, página, incluso el título del sitio), pero sin embargo no aprovecha el resto de lo que ya genera para hacer lo mismo para crear las <code>meta description</code>.</p>
<h2 dir="auto" data-line="428">¡Oye!, ¿qué es eso de la meta description?</h2>
<p>¡Ah, perdona!</p>
<p>La <code>meta description</code> es <strong>ese trozo de texto que Google muestra debajo del título</strong> en los resultados de búsqueda o que utiliza para los <a href="https://ayudawp.com/ai-overviews-google/" target="_blank" rel="noopener">resúmenes que genera para las vistas creadas con IA</a>. No afecta al posicionamiento directamente pero sí al CTR, que es lo que luego te trae visitas.</p>
<p>Si tienes un plugin SEO como Yoast o Rank Math ya se ocupan ellos, pero si no lo usas, o lo has quitado porque <strong>solo necesitas esta función concreta</strong>.</p>
<p><strong>WordPress ya tiene los campos para generar una <code>meta description</code> decente,</strong> solo hay que aprovechar el dato correcto en cada tipo de página.</p>
<p>En este tutorial te dejo <strong>cuatro fragmentos de código</strong>, uno por cada caso de uso, los copias al <code>functions.php</code> del tema hijo, los pegas como <code>mu-plugin</code> o metes solo los que te interesen.</p>
<p>Al final del tutorial tienes también el combinado si lo prefieres todo en un archivo.</p>
<h2>Qué campos nativos vamos a aprovechar</h2>
<p>La idea es no reinventar nada, porque <strong>WordPress ya tiene sitios donde guardar descripciones y casi nadie los usa</strong>:</p>
<ul>
<li>La <strong>descripción corta</strong> del sitio (Ajustes → Generales) para la portada.</li>
<li>El <strong>extracto</strong> de cada entrada y página.</li>
<li>La <strong>descripción</strong> de cada categoría, etiqueta o taxonomía personalizada.</li>
<li>La <strong>información biográfica</strong> del perfil de cada autor.</li>
</ul>
<p><strong>Si rellenas estos campos ya tienes las <code>meta description</code> únicas</strong> en cada tipo de página <strong>sin depender de ningún plugin</strong>. Y por supuesto, si no rellenas nada o no saldrá nada o saldrá cualquier cosa.</p>
<h2>Un detalle importante, la prioridad</h2>
<p>Los cuatro snippets de código enganchan con <code>add_action( 'wp_head', ..., 1 )</code>. La prioridad <code>1</code> coloca la etiqueta <code>&lt;meta name="description"&gt;</code> justo debajo del <code>&lt;title&gt;</code>, que es donde la esperan los bots y donde la ponen los plugins SEO serios.</p>
<p>Si dejas la prioridad por defecto (<code>10</code>), la meta aparece muy abajo en el <code>&lt;head&gt;</code>, mezclada con scripts y hojas de estilo. Funcionar, funciona igual, pero queda más limpio arriba.</p>
<h2>Añadir automáticamente la meta description de la portada a partir la descripción corta</h2>
<p>Lo primero es lo primero. La portada usa la descripción corta que tengas en <strong>Ajustes → Generales → Descripción corta</strong>.</p>
<p>Pero ojo, si ahí sigue poniendo «<em>Just another WordPress site</em>» o «<em>Otro sitio WordPress</em>«, eso es lo que va a ver Google, así que antes de nada cámbialo.</p>
<p>Este código contempla también el caso de que tengas una página estática como portada y otra página aparte asignada como blog (en <strong>Ajustes → Lectura</strong>). Para la página del blog usa su propio extracto, y si está vacío, se queda con la descripción corta del sitio.</p>
<pre>/* Meta Description automática en portada a partir de la descripción corta */
add_action( 'wp_head', function() {
	$description = '';

	if ( is_front_page() ) {
		// Front page: tagline from Settings → General
		$description = get_bloginfo( 'description' );
	} elseif ( is_home() ) {
		// Blog page assigned to a separate page: excerpt of that page
		$blog_page_id = (int) get_option( 'page_for_posts' );
		if ( $blog_page_id ) {
			$description = get_post_field( 'post_excerpt', $blog_page_id );
		}
		// Fallback to the tagline if the blog page has no excerpt
		if ( empty( $description ) ) {
			$description = get_bloginfo( 'description' );
		}
	} else {
		return;
	}

	$description = trim( wp_strip_all_tags( $description ) );
	if ( ! empty( $description ) ) {
		echo '&lt;meta name="description" content="' . esc_attr( $description ) . '"&gt;' . "\n";
	}
}, 1 );</pre>
<h2>Añadir automáticamente la meta description a entradas y páginas a partir del extracto</h2>
<p>Para las entradas y páginas tira del campo de <strong>extracto</strong> que tienes en la barra lateral del editor (o debajo del contenido en el editor clásico).</p>
<p>Si has escrito uno a mano se usa tal cual, y si lo has dejado en blanco el código corta las primeras líneas del contenido hasta los 155 caracteres, que es <strong>lo que suele entrar en el resultado de Google</strong>.</p>
<p>Antes de cortar quita lo que no debería aparecer como texto plano, como los bloques de Gutenberg que no son texto (galerías, embeds, vídeos) y los shortcodes, para que no te salga <code>[shortcode loquesea]</code> <span style="font-size: 16px;">en medio de la <code>meta description</code>. </span></p>
<p><span style="font-size: 16px;">El corte se hace en el último espacio antes del límite, así no dejas palabras partidas.</span></p>
<p>Un consejo que te doy es que si quieres usar el campo de extracto en las páginas (por defecto WordPress no lo muestra en páginas), añadas <code>add_post_type_support( 'page', 'excerpt' );</code> a tu <code>functions.php</code>.</p>
<pre>/* Meta description a partir del extracto de entradas o páginas */
add_action( 'wp_head', function() {
	if ( ! is_singular() ) {
		return;
	}

	$post = get_queried_object();
	if ( ! $post instanceof WP_Post ) {
		return;
	}

	// First try the manual excerpt
	$description = $post-&gt;post_excerpt;

	// Fallback: trim the content to 155 characters at the last space
	if ( empty( $description ) ) {
		$content = $post-&gt;post_content;

		// Remove Gutenberg blocks not meant for excerpts (when available)
		if ( function_exists( 'excerpt_remove_blocks' ) ) {
			$content = excerpt_remove_blocks( $content );
		}

		// Remove shortcodes so they don't leak into the description
		$content = strip_shortcodes( $content );

		// Strip all HTML and normalize whitespace
		$content = wp_strip_all_tags( $content );
		$content = trim( preg_replace( '/\s+/', ' ', $content ) );

		if ( mb_strlen( $content ) &gt; 155 ) {
			$cut        = mb_substr( $content, 0, 155 );
			$last_space = mb_strrpos( $cut, ' ' );
			if ( false !== $last_space ) {
				$cut = mb_substr( $cut, 0, $last_space );
			}
			$description = $cut . '...';
		} else {
			$description = $content;
		}
	}

	$description = trim( wp_strip_all_tags( $description ) );
	if ( ! empty( $description ) ) {
		echo '&lt;meta name="description" content="' . esc_attr( $description ) . '"&gt;' . "\n";
	}
}, 1 );</pre>
<h2>Añadir automáticamente la meta description para categorías, etiquetas y taxonomías personalizadas</h2>
<p><strong>Las categorías y etiquetas tienen un campo de descripción que casi nadie rellena</strong> y que es oro puro para el SEO. Lo tienes al editar cualquier categoría, en <strong>Entradas → Categorías → Editar → Descripción</strong>.</p>
<p>Lo mismo para etiquetas y para taxonomías personalizadas que hayas creado con algún plugin o tu tema.</p>
<p>Si rellenas ese campo <strong>este código lo usa como <code>meta description</code> del archivo de esa categoría o etiqueta</strong>. Si no lo has rellenado no hace nada, para no colar una <code>meta description</code> vacía o repetida.</p>
<pre>*/ Añadir automáticamente meta description a taxonomías */
add_action( 'wp_head', function() {
	if ( ! ( is_category() || is_tag() || is_tax() ) ) {
		return;
	}

	$description = trim( wp_strip_all_tags( term_description() ) );
	if ( ! empty( $description ) ) {
		echo '&lt;meta name="description" content="' . esc_attr( $description ) . '"&gt;' . "\n";
	}
}, 1 );</pre>
<h2>Añadir automáticamente la meta description para archivos de autor a partir de la biografía</h2>
<p>Los archivos de autor son páginas que casi siempre se dejan sin <code>meta description</code>, y eso que tienen un campo clarísimo para rellenarla, el campo de <strong>información biográfica</strong> del perfil del usuario. Lo encuentras en <strong>Usuarios → Tu perfil</strong>.</p>
<p>Rellenar esa biografía con los datos reales del autor, experiencia, especialidad, formación, no solo te da una meta description automática.</p>
<p>Esta información también es una señal de <strong>E-E-A-T</strong> que Google y las IA que rastrean la web valoran cada vez más para <strong>saber quién escribe qué</strong>.</p>
<pre>*/ Añadir automáticamente meta description al archivo de autor usando la bio */
add_action( 'wp_head', function() {
	if ( ! is_author() ) {
		return;
	}

	$description = trim( wp_strip_all_tags( get_the_author_meta( 'description' ) ) );
	if ( ! empty( $description ) ) {
		echo '&lt;meta name="description" content="' . esc_attr( $description ) . '"&gt;' . "\n";
	}
}, 1 );</pre>
<h2>Cómo comprobar que funciona</h2>
<p>Visita cualquier página de tu sitio con <em>clic derecho → Ver código fuente</em>, y busca <code>meta name="description"</code>. Debe aparecer debajo del <code>&lt;title&gt;</code>, con el contenido que toque según el tipo de página que estés viendo.</p>
<p><strong>Si usas un plugin SEO</strong> al mismo tiempo, vas a ver dos <code>meta description</code>, una del plugin y otra de estos snippets, y no es buena idea. En ese caso, o quitas el snippet o desactivas la <code>meta description</code> del plugin SEO, no tiene sentido que convivan ambos.</p>
<h2>Si lo quieres todo en un solo archivo</h2>
<p>Los cuatro fragmentos de código los tienes <strong>unificados en un <code>mu-plugin</code> listo para usar</strong>. Lo pegas en <code>wp-content/mu-plugins/</code> (si no existe la carpeta, la creas) y se activa solo.</p>
<p>El código completo y los snippets sueltos que hemos visto antes también los tienes en <a href="https://github.com/fernandotellado/wordpress-auto-meta-descriptions" target="_blank" rel="noopener">el repositorio de GitHub</a> por si prefieres copiarlos o descargarlos desde ahí.</p>
<h2>Demasiados códigos para mi ¿no hay un plugin para eso?</h2>
<p>Como me lo imaginaba, porque no todos somos de códigos, he preparado <strong>un plugin exactamente para esto</strong>, para aprovechar todo eso que ya tiene WordPress y generar las meta <code>title</code> y<code>description</code> <strong>sin que tengas que depender de un enorme y lleno de funcionalidades de todo pelaje plugin de SEO</strong> mega enorme.</p>
<p>Lo he llamado <a href="https://es.wordpress.org/plugins/native-seo-meta-tags/" target="_blank" rel="noopener"><strong>Native SEO Meta Tags</strong></a>, porque es eso, <strong>hacer SEO con las etiquetas meta, pero sin tener ni que pensar, generadas a partir de lo que ya tienes, de tu contenido</strong>.</p>
<p>Lo instalas, lo activas y listo, hace todo lo que hemos estado hablando. ¿Que prefieres aún definir manualmente alguna <code>meta title</code> o <code>description</code>?, pues <strong>también añade la típica cajita al editor de WordPress</strong> para que las personalices, pero es totalmente opcional.</p>

<a href="https://ayudawp.com/meta-description-automatica/native-seo-meta-tags-ajustes-extracto-description-home/" rel="nofollow"><img width="1200" height="675" src="https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-Meta-Tags-ajustes-extracto-description-home-1200x675.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-Meta-Tags-ajustes-extracto-description-home-1200x675.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-Meta-Tags-ajustes-extracto-description-home-768x432.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-Meta-Tags-ajustes-extracto-description-home-1536x864.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-Meta-Tags-ajustes-extracto-description-home.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>
<a href="https://ayudawp.com/meta-description-automatica/native-seo-meta-tags-title-description-wordpress-editor-clasico/" rel="nofollow"><img width="1200" height="675" src="https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-meta-tags-title-description-WordPress-editor-clasico-1200x675.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-meta-tags-title-description-WordPress-editor-clasico-1200x675.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-meta-tags-title-description-WordPress-editor-clasico-768x432.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-meta-tags-title-description-WordPress-editor-clasico-1536x864.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-meta-tags-title-description-WordPress-editor-clasico.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>
<a href="https://ayudawp.com/meta-description-automatica/native-seo-meta-tags-title-description-wordpress-editor-bloques/" rel="nofollow"><img width="1200" height="675" src="https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-meta-tags-title-description-WordPress-editor-bloques-1200x675.jpg" class="attachment-medium size-medium" alt="" srcset="https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-meta-tags-title-description-WordPress-editor-bloques-1200x675.jpg 1200w, https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-meta-tags-title-description-WordPress-editor-bloques-768x432.jpg 768w, https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-meta-tags-title-description-WordPress-editor-bloques-1536x864.jpg 1536w, https://ayudawp.com/wp-content/uploads/2026/05/Native-SEO-meta-tags-title-description-WordPress-editor-bloques.jpg 1920w" sizes="auto, (max-width: 1200px) 100vw, 1200px" loading="lazy" decoding="async" fetchpriority="low"></a>

<p>Y ya estaría, con estos cuatro códigos <strong>tienes cubiertas las <code>meta description</code> de un sitio WordPress sin necesitar ningún enorme SEO</strong>, todo a partir de lo que ya genera WordPress a partir de tus contenidos.</p>
<p>Si luego quieres escalarlo y <a href="https://ayudawp.com/open-graph-twitter-cards-sin-plugins/">añadir también automáticamente las etiquetas Open Graph y Twitter Cards</a>, ya es otra historia, pero la base la tienes montada con los campos de toda la vida.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ayudawp.com/meta-description-automatica/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Hoy toca optimizar la carga de wp-admin reduciendo las comprobaciones de actualizaciones de WordPress, plugins y temas</title>
		<link>https://ayudawp.com/optimizar-update-core/</link>
					<comments>https://ayudawp.com/optimizar-update-core/#respond</comments>
		
		<dc:creator><![CDATA[Fernando Tellado]]></dc:creator>
		<pubDate>Wed, 27 May 2026 06:28:00 +0000</pubDate>
				<category><![CDATA[Tutoriales - Trucos]]></category>
		<category><![CDATA[WordPress.com]]></category>
		<category><![CDATA[WordPress.org]]></category>
		<category><![CDATA[WPO - Optimizar WordPress]]></category>
		<category><![CDATA[Actualizaciones]]></category>
		<category><![CDATA[Avanzado]]></category>
		<category><![CDATA[WP-Cron]]></category>
		<guid isPermaLink="false">https://ayudawp.com/?p=159146</guid>

					<description><![CDATA[En este tutorial rápido y efectivo vamos a ver como optimizar el admin de WordPress reduciendo la frecuencia de comprobación de actualizaciones disponibles.]]></description>
										<content:encoded><![CDATA[<p>Cada vez que entras en el escritorio de WordPress, o en las pantallas de plugins o de actualizaciones, <strong>el core se lanza a preguntar a api.wordpress.org si hay versiones nuevas</strong>.</p>
<p>No siempre se hace la llamada HTTP entera, pero sí comprueba el <code>transient</code>, y si ha pasado el tiempo que él considera oportuno, dispara la petición.</p>
<p>Lo hace con <strong>tres frecuencias distintas según la página que estás cargando</strong>:</p>
<ul>
<li>En <code>Escritorio → Actualizaciones</code> (<code>update-core.php</code>), cada minuto.</li>
<li>En la pantalla de Plugins (<code>plugins.php</code>), cada hora.</li>
<li>En el resto del <code>wp-admin</code>, cada 12 horas.</li>
</ul>
<p>La primera es la peor, pues <strong>basta con que entres y salgas un par de veces en pocos minutos para que WordPress haga tres llamadas HTTP a WordPress.org</strong> (una para core, otra para plugins, otra para temas) solo por <strong>consultar algo que ya consultó hace cuarenta segundos</strong>.</p>
<p>En servidores justos, o con conexión saliente lenta, se nota y mucho, así que vamos a poner un poco de orden en estas cosas que se hacen sin tu permiso, para <strong>que consuman menos recursos, pero sin dejar de mantener segura tu web</strong>.</p>
<h2>Qué dispara cada llamada</h2>
<p>El mecanismo real son tres acciones enganchadas al hook <code>admin_init</code>:</p>
<ul>
<li><code>_maybe_update_core</code>, que llama a <code>wp_version_check()</code>.</li>
<li><code>_maybe_update_plugins</code>, que llama a <code>wp_update_plugins()</code>.</li>
<li><code>_maybe_update_themes</code>, que llama a <code>wp_update_themes()</code>.</li>
</ul>
<p>Cada una mira el <code>transient</code> correspondiente y, si ha caducado, hace la llamada HTTP.</p>
<p>Si las quitas del <code>admin_init</code> el exceso de comprobaciones desaparece y solo queda el cron programado, que se lanza por defecto dos veces al día sin molestar a nadie.</p>
<h2>El snippet para quitarlo</h2>
<p>En <a href="https://ayudawp.com/que-son-los-mu-plugins-de-wordpress/" target="_blank" rel="noopener">un <code>mu-plugin</code></a> preferentemente:</p>
<pre>/* Frenar comprobaciones de actualizaciones en cada carga de wp-admin
* WP-Cron sigue ejecutándose dos veces al día como siempre
*/
function ayudawp_remove_admin_update_checks() {
    remove_action( 'admin_init', '_maybe_update_core' );
    remove_action( 'admin_init', '_maybe_update_plugins' );
    remove_action( 'admin_init', '_maybe_update_themes' );
}
add_action( 'init', 'ayudawp_remove_admin_update_checks' );</pre>
<p>Con esto <strong>se dejan de hacer esas llamadas fantasma</strong> en los logs de peticiones salientes y <strong>el escritorio carga más rápido</strong>, sobre todo si tienes muchos plugins instalados.</p>
<h2>Lo que sigue funcionando</h2>
<p>Sigues teniendo actualizaciones, no te preocupes. WP-Cron mantiene tres tareas programadas (<code>wp_version_check</code>, <code>wp_update_plugins</code> y <code>wp_update_themes</code>) que se ejecutan cada 12 horas y dejan el transient fresco.</p>
<p>Cuando entres al admin, las notificaciones del tipo <em>hay una nueva versión</em> aparecerán igual, solo que con <strong>datos cacheados hasta como mucho medio día</strong>, mucho más frecuente de lo que la mayoría de la gente suele actualizar.</p>
<p>Si alguna vez quieres <strong>forzar una comprobación manual</strong>, vas a <code>Escritorio → Actualizaciones</code> y pulsas <code>Comprobar de nuevo</code>. El enlace llama a <code>wp_version_check()</code> directamente y se salta todos los aplazamientos definidos.</p>
<h2>Aviso importante con wp-cron desactivado</h2>
<p>Este truco solo funciona bien si tu cron está activo. Si tienes <code>DISABLE_WP_CRON</code> en <code>true</code> en <code>wp-config.php</code> y no has configurado un cron de verdad en el servidor que llame a <code>wp-cron.php</code>, te quedas sin comprobaciones nunca, y <strong>eso sí es un problema de seguridad</strong>.</p>
<p>Comprueba con <a href="https://es.wordpress.org/plugins/wp-crontrol/" target="_blank" rel="nofollow noopener">WP Crontrol</a> que <strong>los tres hooks de actualizaciones están programados y ejecutándose</strong>, y si no lo están, soluciona primero el cron antes de aplicar este código.</p>
<p>Si te ha servido este truco <strong>tienes uno parecido para <a href="https://ayudawp.com/optimizar-salud-del-sitio/" target="_blank" rel="noopener">bajarle el ritmo al widget de salud del sitio</a></strong> y a su cron semanal que ejecuta en segundo plano.</p>
<p>Lo publiqué hace poco en <a href="https://ayudawp.com/categoria/wordpress-performance-optimization/" target="_blank" rel="ugc noopener">la categoría de WPO del blog</a> y van en la misma línea de ir metiendo tijera a todas esas tareas de fondo que ejecuta WordPress y que no aportan nada en el día a día.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ayudawp.com/optimizar-update-core/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>¿Tiene mapas del sitio multilenguaje Weglot? ¿se pueden integrar con plugins SEO</title>
		<link>https://ayudawp.com/sitemap-weglot/</link>
					<comments>https://ayudawp.com/sitemap-weglot/#respond</comments>
		
		<dc:creator><![CDATA[Fernando Tellado]]></dc:creator>
		<pubDate>Tue, 26 May 2026 06:28:00 +0000</pubDate>
				<category><![CDATA[Programación + WordPress]]></category>
		<category><![CDATA[SEO / AEO / GEO / LLMO]]></category>
		<category><![CDATA[Tutoriales - Trucos]]></category>
		<category><![CDATA[WordPress.com]]></category>
		<category><![CDATA[WordPress.org]]></category>
		<category><![CDATA[Avanzado]]></category>
		<category><![CDATA[Experto]]></category>
		<category><![CDATA[Weglot]]></category>
		<guid isPermaLink="false">https://ayudawp.com/?p=159308</guid>

					<description><![CDATA[Si has montado una web multilenguaje con Weglot y luego revisas el sitemap.xml, te llevas una sorpresa. Las URLs traducidas no están.]]></description>
										<content:encoded><![CDATA[<p>Si has montado una <a href="https://ayudawp.com/weglot/" target="_blank" rel="noopener"><strong>web multilenguaje con Weglot</strong></a> y luego revisas el mapa del sitio te llevas una sorpresa. Las URLs traducidas no están. Solo aparecen las del idioma original. Y eso, para alguien que viene de WPML o Polylang, choca bastante.</p>
<p>Lo que pasa es que <strong>Weglot maneja el SEO multilenguaje de una forma distinta a otros plugins de traducción</strong>, y los sitemaps son el ejemplo más claro de esa diferencia.</p>
<p>Antes de meterte a cambiar nada, conviene <strong>saber qué hace Weglot por defecto, qué no hace, y si de verdad necesitas tocar algo o no</strong>. Y si vas a tocarlo, hay que saber <strong>con qué plugin de SEO lo combinas</strong>, porque cada uno se comporta diferente.</p>
<h2>Cómo gestiona Weglot el SEO multilenguaje en WordPress</h2>
<p>Lo primero es entender la lógica, porque <strong>Weglot no guarda las traducciones en tu base de datos</strong> como hacen WPML, Polylang o TranslatePress, las guarda en sus propios servidores y las inyecta en el código fuente de cada página cuando alguien visita la versión traducida.</p>
<p>En lo que <strong>afecta al SEO</strong>, hace cuatro cosas:</p>
<ul>
<li>Crea una URL única para cada idioma con un subdirectorio del tipo <code>/es/</code>, <code>/fr/</code>, <code>/de/</code> a partir del código de dos letras del idioma.</li>
<li>Añade automáticamente las etiquetas <code>hreflang</code> en el <code>&lt;head&gt;</code> del HTML, declarando todas las versiones de cada página.</li>
<li>Traduce los meta tags (<code>title</code>, <code>meta description</code>, atributos <code>alt</code>) en el código fuente, no por JavaScript, así que Google los ve perfectamente.</li>
<li>Marca las URLs traducidas con sus canonical correctos, apuntando a sí mismas.</li>
</ul>
<p>Hasta aquí, todo bien, <strong>el <em>problema</em> viene con el mapa del sitio</strong>.</p>
<h2>La realidad sobre los sitemaps multilenguaje de Weglot</h2>
<p>Esto es lo que dice la documentación técnica oficial de Weglot, palabras textuales del centro de ayuda en su <a href="https://support.weglot.com/article/287-how-can-i-edit-my-sitemap" target="_blank" rel="nofollow noopener">artículo sobre cómo editar el sitemap</a>:</p>
<blockquote><p>«Si quieres un sitemap multilenguaje igualmente, Weglot lo gestiona por ti. Sin embargo, esta funcionalidad solo está disponible bajo la integración por subdirectorios. Esta opción no está disponible para proyectos WordPress.»</p></blockquote>
<p>O sea, que en WordPress, Weglot no genera un mapa del sitio multilenguaje propio ni añade las URLs traducidas a ningún <code>sitemap</code> existente. Su integración multilingüe de mapas del sitio existe, pero solo para webs con su proxy en subdirectorios, que es <strong>como funciona en otras plataformas, no la integración nativa de WordPress</strong>.</p>
<p>Llama la atención que el blog de marketing de Weglot diga lo contrario, hablando de que «<strong>genera sitemaps XML multilenguaje</strong>» como si fuera una característica más del plugin de WordPress. La documentación técnica desmiente esto, así que me lo explique, oiga.</p>
<h2>La diferencia con WPML, Polylang y TranslatePress</h2>
<p>La razón por la que Weglot tiene este problema y los demás no es importante para entenderlo antes de seguir.</p>
<p>WPML, Polylang y TranslatePress guardan las traducciones en la base de datos de tu WordPress como contenido real, y cada traducción es un post o un campo dentro del post, según el plugin. Eso significa que cualquier plugin de SEO que recorra los posts del sistema (que es como funcionan todos) las ve sin problema y las incluye en el mapa del sitio automáticamente.</p>
<p>Weglot guarda las traducciones en sus servidores remotos, y  cuando un visitante pide la versión en francés Weglot intercepta la respuesta y <strong>traduce el HTML al vuelo</strong>. Las traducciones nunca tocan tu base de datos, por tanto, cuando Yoast o Rank Math generan el mapa del sitio recorriendo tus posts no encuentran las versiones traducidas porque, técnicamente, no existen como contenido propio.</p>
<p>Esto tiene una ventaja, que es la que te cuenta Weglot, y es que <strong>tu base de datos no engorda con las traducciones</strong>. Pero también un inconveniente, el que estamos viendo, que las herramientas de tu web, de WordPress, no saben que existen.</p>
<h2>¿Necesitas un sitemap multilenguaje si ya tienes hreflang?</h2>
<p>Aquí es donde Weglot tiene su argumento, y no es un locurón, tiene lógica, pues <a href="https://developers.google.com/search/docs/specialty/international/localized-versions" target="_blank" rel="nofollow noopener">Google permite declarar las versiones de idioma de una página de tres formas</a>, y con cualquiera basta:</p>
<ul>
<li>Etiquetas hreflang en el <code>&lt;head&gt;</code> de cada página HTML.</li>
<li>Cabecera HTTP <code>hreflang</code>.</li>
<li>Etiquetas <code>xhtml:link</code> dentro del sitemap XML.</li>
</ul>
<p>Como Weglot ya inyecta los <code>hreflang</code> en el <code>head</code>, <strong>técnicamente con eso le vale a Google</strong>. Así que según eso tendrían razón en argumentar que el mapa del sitio multilenguaje es redundante, si tu objetivo es Google.</p>
<p>El problema es que <strong>no todo el mundo posiciona solo en Google</strong>, y aunque sea tu caso, hay matices que conviene tener en mente:</p>
<ul>
<li>Bing tira más de mapas del sitio que Google y le viene bien tener todas las URLs declaradas, traducidas incluidas.</li>
<li>Baidu, si te interesa el mercado chino, prácticamente exige sitemap dedicado por idioma para indexar bien.</li>
<li>Si algo tengo comprobado es que <a href="https://ayudawp.com/ai-overviews-google/" target="_blank" rel="noopener">los bots de IA lo primero que miran y devoran es el mapa del sitio</a>.</li>
<li>En sitios grandes con muchas URLs, un mapa del sitio completo acelera el descubrimiento de páginas nuevas. Si dejas las traducciones fuera Google las encontrará por enlaces internos pero más lento.</li>
<li>Search Console permite seguir métricas por mapa del sitio, así que tener uno por idioma facilita ver problemas de indexación específicos de un mercado. Esto se puede compensar dando de alta cada subcarpeta como propiedad, pero implica más trabajo.</li>
</ul>
<p>Vamos, por no enrollarme más, que si solo te importa Google y la web no es enorme, los <code>hreflang</code> en el <code>head</code> te valen. Ahora bien, si vendes en mercados con buscadores diferentes, si la web es grande, si te importa el tráfico que te pueda llegar de IAs, o si quieres tener Search Console limpio por idioma, <strong>te conviene tener las URLs traducidas en el mapa del sitio</strong>.</p>
<h2>Qué hace Weglot con cada plugin de SEO o de sitemaps XML</h2>
<p>El comportamiento por defecto, sin tocar código, es siempre el mismo, el sitemap solo lleva URLs del idioma original. Pero hay diferencias importantes según el plugin que uses, sobre todo a la hora de poder añadir las traducidas, así que vamos uno a uno.</p>
<h3>Sitemap nativo de WordPress (wp-sitemap.xml)</h3>
<p>Desde WordPress 5.5 hay un <a href="https://ayudawp.com/plugin-personalizar-sitemap-nativo-wordpress/" target="_blank" rel="noopener">mapa del sitio nativo</a> por defecto en <code>wp-sitemap.xml</code>. Si no usas ningún plugin de SEO ni de sitemaps XML y dejas que WordPress lo genere solo Weglot no lo toca.</p>
<p>El mapa del sitio nativo te muestra esto:</p>
<pre>&lt;urlset&gt;
  &lt;url&gt;
    &lt;loc&gt;https://midominio.com/articulo-ejemplo/&lt;/loc&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;https://midominio.com/contacto/&lt;/loc&gt;
  &lt;/url&gt;
&lt;/urlset&gt;</pre>
<p>Las versiones <code>/fr/articulo-ejemplo/</code>, <code>/de/articulo-ejemplo/</code> y demás traducciones no aparecen y no hay forma sencilla de añadirlas sin código. Tendrías que pelearte con los filtros <code>wp_sitemaps_posts_entry</code> o <code>wp_sitemaps_index_entry</code> de WordPress, y <strong>Weglot no proporciona snippet oficial</strong> para esto.</p>
<h3>Yoast SEO</h3>
<p>Yoast desactiva el mapa del sitio nativo de WordPress y genera el suyo propio en <code>/sitemap_index.xml</code>. Por defecto, instalando Weglot encima, el sitemap de Yoast sigue mostrando solo las URLs del idioma original. Si no haces nada al respecto las traducidas no aparecen.</p>
<p>Existe un apaño validado por un usuario del propio plugin Weglot en el foro oficial, y es usando el filtro <code>wpseo_sitemap_url</code>. No es solución oficial de Weglot pero funciona. Te lo enseño más abajo.</p>
<h3>Rank Math</h3>
<p>Aquí es donde Weglot sí ha hecho los deberes. Tiene una <a href="https://developers.weglot.com/wordpress/use-cases/add-translated-url-to-rankmath-sitemap-index" target="_blank" rel="nofollow noopener">página específica en developers.weglot.com</a> con un código oficial usando el filtro <code>rank_math/sitemap/index</code>. La pega es que ese snippet <strong>obliga a listar a mano las URLs de los sub-mapas</strong> que quieres traducir y <strong>desactiva el caché del sitemap</strong>, lo que en webs grandes puede pesar en rendimiento.</p>
<h3>All in One SEO (AIOSEO)</h3>
<p>AIOSEO menciona compatibilidad con Weglot pero no hay tutorial específico ni código oficial. El comportamiento es el mismo que con Yoast, solo URLs del idioma original. La buena noticia es que <strong>tiene dos filtros</strong> bien documentados que sirven para añadir URLs traducidas:</p>
<ul>
<li><code>aioseo_sitemap_additional_pages</code> permite añadir URLs personalizadas a un sub-sitemap dedicado en <code>addl-sitemap.xml</code>. Para que funcione hay que activar la opción «Additional Pages» en los ajustes de AIOSEO.</li>
<li><code>aioseo_sitemap_indexes</code> permite añadir sub-mapas al índice general.</li>
</ul>
<p>Hay que combinar uno de los dos con la API de Weglot para inyectar las traducciones. Enseguida lo vemos.</p>
<h3>SEOPress</h3>
<p>En octubre de 2025 Weglot y SEOPress anunciaron una colaboración comercial. A nivel de marketing está vendido como «la solución completa para SEO multilenguaje en WordPress»</p>
<p>Pero a nivel técnico, lo que SEOPress detecta automáticamente para sitemaps multilenguaje es WPML, Polylang, TranslatePress y Weglot, pero solo a efectos de NO duplicar URLs entre idiomas, no para incluir las traducidas que Weglot guarda en sus servidores.</p>
<p>El mapa del sitio de SEOPress (<code>sitemaps.xml</code>) <strong>sigue sin ver las URLs traducidas de Weglot</strong>.</p>
<p>SEOPress tiene varios filtros oficiales que se pueden aprovechar, sobre todo:</p>
<ul>
<li><code>seopress_sitemaps_single_url</code> que filtra cada URL del sitemap de un post type concreto (existe desde SEOPress 5.3).</li>
<li><code>seopress_sitemaps_urlset</code> que filtra el bloque urlset entero antes de imprimirse.</li>
</ul>
<p>El primero es el equivalente al <code>wpseo_sitemap_url</code> de Yoast, así que la lógica del código del foro de Weglot <strong>se puede adaptar a SEOPress</strong> sin demasiado problema. De nuevo, lo vemos en un momentito.</p>
<h3>Google XML Sitemaps</h3>
<p>Este es uno de los plugins de maps del sitio más veteranos y aún muy instalado. Por defecto genera su sitemap en <code>/sitemap.xml</code> sin tocar el de WordPress, y como el resto, <strong>no ve las URLs traducidas de Weglot</strong>.</p>
<p>La buena noticia es que <strong>tiene una API oficial documentada para que otros plugins añadan URLs al sitemap</strong>. Se basa en el hook <code>sm_buildmap</code> y el método <code>AddUrl</code> de la clase <code>GoogleSitemapGenerator</code>. Esto es lo más limpio que vas a encontrar en cuanto a integración.</p>
<p>Además tiene una opción de «Páginas adicionales» para añadir URLs sueltas a mano, que puede ser inabarcable cuando tienes que meter cientos de URLs traducidas, pero tenerlo lo tienes.</p>
<h3>XML Sitemap &amp; Google News</h3>
<p>Otro plugin muy popular, sobre todo entre quien quiere control fino del mapa del sitio sin la mochila de un plugin SEO completo. <a href="https://es.wordpress.org/plugins/xml-sitemap-feed/" target="_blank" rel="nofollow noopener">El plugin de RavanH</a> tiene la peculiaridad de ser uno de los más completos para añadir URLs personalizadas sin código.</p>
<p>Desde la versión 5.4 incluye una opción nativa en sus ajustes para «URLs personalizadas en el mapa del sitio» (añadir URLs sueltas a mano) y otra para «mapas del sitio externos». Esto último es útil si Weglot generase un sitemap externo para WordPress, pero ya hemos visto que no lo hace, así que esa segunda opción no nos sirve directamente para Weglot.</p>
<p>El plugin tiene un filtro pensado exactamente para este uso, <code>xmlsf_custom_urls</code>, que filtra el array de URLs personalizadas antes de que el mapa del sitio las procese. Es la forma limpia de añadir URLs traducidas sin tocar la opción almacenada en la base de datos. Lo veremos abajo, porque tiene un par de detalles importantes.</p>
<h2>Soluciones con código para añadir las URLs traducidas</h2>
<p>Aquí tienes todos los snippets ordenados por plugin. Pruébalos en staging antes de meterlos en producción, sobre todo los que no son oficiales de Weglot. Donde el snippet venga de fuente oficial te lo indico claramente.</p>
<h3>Para Yoast SEO</h3>
<p>Este viene del <a href="https://wordpress.org/support/topic/how-to-add-the-translated-urls-to-your-yoast-sitemap/" target="_blank" rel="nofollow noopener">foro oficial del plugin Weglot en WordPress.org</a>, escrito por un usuario y bendecido por un desarrollador del plugin. <strong>Funciona en versiones recientes de Yoast y Weglot</strong> pero <strong>no es código oficial</strong> de Weglot, tenlo en cuenta.</p>
<p>Usa el filtro <code>wpseo_sitemap_url</code> de Yoast para inyectar las URLs traducidas dentro del sitemap original, en lugar de duplicar sitemaps. Es el enfoque correcto según las directrices de Google:</p>
<pre>add_filter( 'wpseo_sitemap_url', 'weglot_sitemap', 10, 2 );

function weglot_sitemap( $output, $url ) {

    $date = null;

    if ( ! empty( $url['mod'] ) ) {
        $date = date( 'c', strtotime( $url['mod'] ) );
    }

    $languages = weglot_get_destination_languages();
    $language_services = weglot_get_service( 'Language_Service_Weglot' );
    $request_url_services = weglot_get_service( 'Request_Url_Service_Weglot' );

    foreach ( $languages as $language ) {

        $wg_url = $request_url_services-&gt;create_url_object( $url['loc'] );
        $lang = $language_services-&gt;get_language_from_internal( $language['language_to'] );

        $output .= "\t&lt;url&gt;\n";
        $output .= "\t\t&lt;loc&gt;" . $wg_url-&gt;getForLanguage( $lang ) . "&lt;/loc&gt;\n";
        $output .= empty( $date ) ? '' : "\t\t&lt;lastmod&gt;" . htmlspecialchars( $date ) . "&lt;/lastmod&gt;\n";

        $images = '';
        if ( isset( $url['images'] ) &amp;&amp; is_array( $url['images'] ) ) {
            foreach ( $url['images'] as $image ) {
                $images .= "&lt;image:image&gt;&lt;image:loc&gt;{$image['src']}&lt;/image:loc&gt;&lt;/image:image&gt;\n";
            }
            $output .= $images;
        }

        $output .= "\t&lt;/url&gt;\n";

    }

    return $output;

}</pre>
<p>Cómo queda el sitemap después de aplicar este snippet:</p>
<pre>&lt;urlset&gt;
  &lt;url&gt;
    &lt;loc&gt;https://midominio.com/articulo-ejemplo/&lt;/loc&gt;
    &lt;lastmod&gt;2026-04-15T10:30:00+00:00&lt;/lastmod&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;https://midominio.com/fr/articulo-ejemplo/&lt;/loc&gt;
    &lt;lastmod&gt;2026-04-15T10:30:00+00:00&lt;/lastmod&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;https://midominio.com/de/articulo-ejemplo/&lt;/loc&gt;
    &lt;lastmod&gt;2026-04-15T10:30:00+00:00&lt;/lastmod&gt;
  &lt;/url&gt;
&lt;/urlset&gt;</pre>
<p>Cada URL original aparece seguida de sus versiones traducidas, todo dentro del mismo sitemap. Si quieres excluir alguna URL de la traducción, el desarrollador de Weglot recomienda cambiar <code>getForLanguage( $lang )</code> por <code>getForLanguage( $lang, true )</code>.</p>
<h3>Para Rank Math</h3>
<p>Sacado de la documentación para desarrolladores de Weglot. Dos códigos, uno para desactivar el caché del sitemap (necesario para que se actualice con tus cambios) y otro para añadir las URLs traducidas al índice del sitemap.</p>
<p>El primero, para desactivar el caché:</p>
<pre>/**
 * Filter if XML sitemap transient cache is enabled.
 *
 * @param boolean $unsigned Enable cache or not, defaults to true
 */
add_filter( 'rank_math/sitemap/enable_caching', '__return_false' );</pre>
<p>El segundo, para añadir las URLs traducidas. Tienes que editar el array <code>$urls</code> con las URLs reales de tus sub-sitemaps:</p>
<pre>add_filter( 'rank_math/sitemap/index', function( $xml ) {
    // Fetch Weglot services
    $language_services = weglot_get_service( 'Language_Service_Weglot' );
    $request_url_services = weglot_get_service( 'Request_Url_Service_Weglot' );

    // List of URLs to be translated
    $urls = [
        "https://midominio.com/post-sitemap.xml",
        "https://midominio.com/page-sitemap.xml",
        "https://midominio.com/category-sitemap.xml",
    ];

    $destination_languages = weglot_get_destination_languages();
    foreach ( $urls as $url ) {
        $wg_url = $request_url_services-&gt;create_url_object( $url );

        // Iterate over each destination language
        foreach ( $destination_languages as $language_code ) {
            $language = $language_services-&gt;get_language_from_internal( $language_code['language_to'] );

            $translated_url = $wg_url-&gt;getForLanguage( $language );

            $xml .= '
                &lt;sitemap&gt;
                    &lt;loc&gt;' . esc_url( $translated_url ) . '&lt;/loc&gt;
                    &lt;lastmod&gt;' . esc_html( date( 'c' ) ) . '&lt;/lastmod&gt;
                &lt;/sitemap&gt;';
        }
    }

    return $xml;
}, 999 );</pre>
<p>Cómo queda el índice de sitemaps de Rank Math después:</p>
<pre>&lt;sitemapindex&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;https://midominio.com/post-sitemap.xml&lt;/loc&gt;
    &lt;lastmod&gt;2026-04-15T10:30:00+00:00&lt;/lastmod&gt;
  &lt;/sitemap&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;https://midominio.com/page-sitemap.xml&lt;/loc&gt;
    &lt;lastmod&gt;2026-04-15T10:30:00+00:00&lt;/lastmod&gt;
  &lt;/sitemap&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;https://midominio.com/fr/post-sitemap.xml&lt;/loc&gt;
    &lt;lastmod&gt;2026-04-15T10:30:00+00:00&lt;/lastmod&gt;
  &lt;/sitemap&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;https://midominio.com/fr/page-sitemap.xml&lt;/loc&gt;
    &lt;lastmod&gt;2026-04-15T10:30:00+00:00&lt;/lastmod&gt;
  &lt;/sitemap&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;https://midominio.com/de/post-sitemap.xml&lt;/loc&gt;
    &lt;lastmod&gt;2026-04-15T10:30:00+00:00&lt;/lastmod&gt;
  &lt;/sitemap&gt;
&lt;/sitemapindex&gt;</pre>
<p>Limitación importante: hay que listar las URLs a mano. Si añades un nuevo tipo de contenido o Rank Math empieza a generar otro sub-sitemap, tendrás que actualizar el array. No es automático del todo.</p>
<h3>Para SEOPress</h3>
<p>Este código no viene de Weglot ni de SEOPress, lo he montado yo mismo adaptando la lógica del código de Yoast al filtro <code>seopress_sitemaps_single_url</code> de SEOPress, que está documentado oficialmente. Pruébalo bien antes de pasarlo a producción:</p>
<pre>add_filter( 'seopress_sitemaps_single_url', 'ayudawp_seopress_weglot_sitemap', 10, 2 );

function ayudawp_seopress_weglot_sitemap( $url_xml, $url_data ) {

    if ( ! function_exists( 'weglot_get_destination_languages' ) ) {
        return $url_xml;
    }

    $original_url = isset( $url_data['loc'] ) ? $url_data['loc'] : '';
    $lastmod = isset( $url_data['lastmod'] ) ? $url_data['lastmod'] : '';

    if ( empty( $original_url ) ) {
        return $url_xml;
    }

    $languages = weglot_get_destination_languages();
    $language_services = weglot_get_service( 'Language_Service_Weglot' );
    $request_url_services = weglot_get_service( 'Request_Url_Service_Weglot' );

    foreach ( $languages as $language ) {

        $wg_url = $request_url_services-&gt;create_url_object( $original_url );
        $lang = $language_services-&gt;get_language_from_internal( $language['language_to'] );
        $translated_url = $wg_url-&gt;getForLanguage( $lang );

        $url_xml .= "&lt;url&gt;\n";
        $url_xml .= "\t&lt;loc&gt;" . esc_url( $translated_url ) . "&lt;/loc&gt;\n";
        if ( ! empty( $lastmod ) ) {
            $url_xml .= "\t&lt;lastmod&gt;" . esc_html( $lastmod ) . "&lt;/lastmod&gt;\n";
        }
        $url_xml .= "&lt;/url&gt;\n";
    }

    return $url_xml;
}</pre>
<p>El resultado es equivalente al de Yoast, las URLs traducidas se inyectan dentro del mismo sitemap, justo después de cada URL original. Si SEOPress te genera por ejemplo <code>post-sitemap.xml</code>, este filtro afecta a todas sus entradas.</p>
<h3>Para AIOSEO</h3>
<p>AIOSEO permite dos caminos, pero te dejo el más limpio, que usa el filtro <code>aioseo_sitemap_additional_pages</code> para crear un sub-mapa específico para las traducciones.</p>
<p>Primero, <strong>requisito imprescindible</strong>: ve a <code>AIOSEO &gt; Mapas del sitio &gt; Páginas adicionales</code> y activa la funcionalidad. Sin ese paso el filtro no se ejecuta, te pasa una página vacía con error si no lo activas.</p>
<p>Después añades este código usando tu método favorito:</p>
<pre>add_filter( 'aioseo_sitemap_additional_pages', 'ayudawp_aioseo_weglot_additional_pages' );

function ayudawp_aioseo_weglot_additional_pages( $pages ) {

    if ( ! function_exists( 'weglot_get_destination_languages' ) ) {
        return $pages;
    }

    // URLs base que quieres traducir. Adáptalo a tus sub-sitemaps.
    $base_urls = array(
        home_url( '/' ),
        // Añade aquí las URLs que quieras incluir
    );

    $languages = weglot_get_destination_languages();
    $language_services = weglot_get_service( 'Language_Service_Weglot' );
    $request_url_services = weglot_get_service( 'Request_Url_Service_Weglot' );

    foreach ( $base_urls as $base_url ) {

        $wg_url = $request_url_services-&gt;create_url_object( $base_url );

        foreach ( $languages as $language ) {

            $lang = $language_services-&gt;get_language_from_internal( $language['language_to'] );
            $translated_url = $wg_url-&gt;getForLanguage( $lang );

            $pages[] = (object) array(
                'loc'        =&gt; $translated_url,
                'lastmod'    =&gt; gmdate( 'c' ),
                'changefreq' =&gt; 'weekly',
                'priority'   =&gt; 0.8,
            );
        }
    }

    return $pages;
}</pre>
<p>Cómo queda el índice de AIOSEO:</p>
<pre>&lt;sitemapindex&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;https://midominio.com/post-sitemap.xml&lt;/loc&gt;
  &lt;/sitemap&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;https://midominio.com/page-sitemap.xml&lt;/loc&gt;
  &lt;/sitemap&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;https://midominio.com/addl-sitemap.xml&lt;/loc&gt;
  &lt;/sitemap&gt;
&lt;/sitemapindex&gt;</pre>
<p>Y dentro de <code>addl-sitemap.xml</code> aparecen las URLs traducidas que has añadido con el filtro. La pega es que es un sub-mapa aparte, no se mezclan con las originales como en Yoast o SEOPress, pero a ojos de Google es perfectamente válido.</p>
<h3>Para Google XML Sitemaps</h3>
<p>El plugin tiene una API oficial documentada en el archivo de documentación que se incluye con el plugin, basada en el hook <code>sm_buildmap</code>.</p>
<p>Esta es la forma correcta de añadir URLs externas:</p>
<pre>add_action( 'sm_buildmap', 'ayudawp_google_xml_sitemaps_weglot' );

function ayudawp_google_xml_sitemaps_weglot() {

    if ( ! class_exists( 'GoogleSitemapGenerator' ) ) {
        return;
    }

    if ( ! function_exists( 'weglot_get_destination_languages' ) ) {
        return;
    }

    $generator = GoogleSitemapGenerator::GetInstance();

    if ( $generator === null ) {
        return;
    }

    // URLs originales que quieres traducir
    $original_urls = array(
        home_url( '/' ),
        // Añade aquí las URLs que quieras incluir
    );

    $languages = weglot_get_destination_languages();
    $language_services = weglot_get_service( 'Language_Service_Weglot' );
    $request_url_services = weglot_get_service( 'Request_Url_Service_Weglot' );

    foreach ( $original_urls as $original_url ) {

        $wg_url = $request_url_services-&gt;create_url_object( $original_url );

        foreach ( $languages as $language ) {

            $lang = $language_services-&gt;get_language_from_internal( $language['language_to'] );
            $translated_url = $wg_url-&gt;getForLanguage( $lang );

            // Parámetros: URL, lastmod (timestamp), changefreq, priority
            $generator-&gt;AddUrl( $translated_url, time(), 'weekly', 0.8 );
        }
    }
}</pre>
<p>Cómo queda el sitemap, todo en un único archivo <code>sitemap.xml</code> mezclando originales y traducidas:</p>
<pre>&lt;urlset&gt;
  &lt;url&gt;
    &lt;loc&gt;https://midominio.com/&lt;/loc&gt;
    &lt;changefreq&gt;daily&lt;/changefreq&gt;
    &lt;priority&gt;1.0&lt;/priority&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;https://midominio.com/articulo-ejemplo/&lt;/loc&gt;
    &lt;changefreq&gt;weekly&lt;/changefreq&gt;
    &lt;priority&gt;0.6&lt;/priority&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;https://midominio.com/fr/&lt;/loc&gt;
    &lt;changefreq&gt;weekly&lt;/changefreq&gt;
    &lt;priority&gt;0.8&lt;/priority&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;https://midominio.com/de/&lt;/loc&gt;
    &lt;changefreq&gt;weekly&lt;/changefreq&gt;
    &lt;priority&gt;0.8&lt;/priority&gt;
  &lt;/url&gt;
&lt;/urlset&gt;</pre>
<p>Tiene una limitación parecida a la de Rank Math, y es que hay que listar las URLs originales que quieres traducir. Si tienes pocas URLs base de las que cuelga todo, esto te vale, si tienes cientos de entradas y quieres todos traducidos, vas a tener que iterar dinámicamente sobre tus posts publicados, cosa que requiere más código.</p>
<h3>XML Sitemap &amp; Google News</h3>
<p>El plugin tiene un filtro específico para esto, <code>xmlsf_custom_urls</code>, que es la forma documentada de inyectar URLs personalizadas sin tocar la base de datos. He revisado el código actual del plugin (versión 5.7.4) y este es el filtro correcto.</p>
<p>Hay dos peculiaridades que conviene saber antes:</p>
<ul>
<li>El plugin tiene dos modos de servidor de sitemap:
<ol>
<li>«WordPress core» (delega en el sitemap nativo de WP) y «Plugin» (genera el sitemap propio). Se elige en <code>Ajustes &gt; XML Sitemap</code>.</li>
<li>El filtro <code>xmlsf_custom_urls</code> funciona en ambos, pero con un matiz que veremos.</li>
</ol>
</li>
<li>El formato del <code>array</code> que espera el plugin no son claves asociativas tipo Yoast/AIOSEO. Cada entrada es un <code>array</code> indexado donde el primer elemento es la URL: <code>array( $url )</code> o <code>array( $url, $priority )</code>. El plugin lee <code>$data[0]</code> directamente.</li>
</ul>
<p>Aquí va el snippet:</p>
<pre>add_filter( 'xmlsf_custom_urls', 'ayudawp_xmlsf_weglot_custom_urls' );

function ayudawp_xmlsf_weglot_custom_urls( $custom_urls ) {

    if ( ! function_exists( 'weglot_get_destination_languages' ) ) {
        return $custom_urls;
    }

    if ( ! is_array( $custom_urls ) ) {
        $custom_urls = array();
    }

    // URLs originales que quieres traducir
    $original_urls = array(
        home_url( '/' ),
        // Añadir aquí más URLs base
    );

    $languages = weglot_get_destination_languages();
    $language_services = weglot_get_service( 'Language_Service_Weglot' );
    $request_url_services = weglot_get_service( 'Request_Url_Service_Weglot' );

    foreach ( $original_urls as $original_url ) {

        $wg_url = $request_url_services-&gt;create_url_object( $original_url );

        foreach ( $languages as $language ) {

            $lang = $language_services-&gt;get_language_from_internal( $language['language_to'] );
            $translated_url = $wg_url-&gt;getForLanguage( $lang );

            // Formato: array indexado, no asociativo. El plugin lee $data[0].
            $custom_urls[] = array( $translated_url );
        }
    }

    return $custom_urls;
}</pre>
<p>Ahora viene el matiz importante, porque en modo «Plugin» del servidor de mapa del sitio este filtro se ejecuta directamente. Pero en modo «WordPress core» el <code>provider</code> de URLs personalizadas solo se registra si la opción <code>xmlsf_urls</code> ya tiene algún valor guardado en la base de datos. Es una comprobación que hace el plugin internamente:</p>
<pre>// Línea 193 de inc/class-sitemap-core.php (versión 5.7.4)
if ( get_option( 'xmlsf_urls' ) ) {
    wp_register_sitemap_provider( 'custom', new Sitemaps_Provider_Custom() );
}</pre>
<p>Dicho de otro modo, si nunca has añadido una URL manualmente desde el admin del plugin, en modo WordPress core el snippet de arriba no se va a ejecutar nunca, porque el <code>provider</code> ni siquiera está registrado.</p>
<p>Hay dos formas de solucionarlo:</p>
<ul>
<li><strong>Manual</strong>: ve a <code>Ajustes &gt; XML Sitemap</code>, sección de URLs pesonalizadas, y añade cualquier URL como <code>placeholder</code> (la página de inicio, por ejemplo). Con eso la opción ya tiene valor en la base de datos, el <code>provider</code> se registra y tu filtro empieza a funcionar.</li>
<li><strong>Automática</strong>: añade también un filtro a <code>option_xmlsf_urls</code> que devuelva un <code>array</code> no vacío cuando la opción esté vacía, así forzamos el registro. Hay que tener cuidado con la pantalla de admin para que no se contamine con URLs falsas.</li>
</ul>
<p>La forma manual es perfectamente razonable, porque con una sola URL real añadida una vez resuelves el problema sin código adicional.</p>
<p>Cómo queda el índice del sitemap después de aplicar el código:</p>
<pre>&lt;sitemapindex&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;https://midominio.com/wp-sitemap-posts-post-1.xml&lt;/loc&gt;
  &lt;/sitemap&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;https://midominio.com/wp-sitemap-posts-page-1.xml&lt;/loc&gt;
  &lt;/sitemap&gt;
  &lt;sitemap&gt;
    &lt;loc&gt;https://midominio.com/wp-sitemap-custom-1.xml&lt;/loc&gt;
  &lt;/sitemap&gt;
&lt;/sitemapindex&gt;</pre>
<p>Las URLs traducidas aparecen dentro del sub-mapa <code>wp-sitemap-custom-1.xml</code> (en modo WordPress core) o <code>sitemap-custom.xml</code> (en modo Plugin). Si prefieres que las traducidas se mezclen con las originales en su sub-mapa correspondiente no es viable con este plugin sin reescribir bastante de la lógica de generación.</p>
<h2>Cuándo merece la pena este jaleo y cuándo no</h2>
<p>Después de probarlo en varios sitios a las conclusiones que llego, por si te ahorra algún dolor de cabeza, son estas:</p>
<ul>
<li>Si tu web es pequeña, posicionas solo en Google y los <code>hreflang</code> están bien puestos, mejor no tocar nada. Weglot tiene razón en que es redundante.</li>
<li>Si tu web es grande, con cientos o miles de URLs traducidas, mete el código que se ajuste a tu instalación. La indexación de las versiones nuevas será más rápida y tendrás Search Console más limpio.</li>
<li>Si te interesa Bing, cualquier buscador que no sea Google o posicionar en IAs, mete el código sin pensarlo. Estos bots leen menos los <code>hreflang</code> y más los mapas del sitio.</li>
<li>Si vas a dar de alta cada subcarpeta como propiedad separada en Search Console (que es buena práctica para multilenguaje), mete el código también, porque cada propiedad querrá su mapa del sitio específico.</li>
</ul>
<p>Antes de pelearte con código, dale unas semanas a Search Console y mira si las URLs traducidas se están indexando bien por sí solas. Si lo están haciendo gracias a los <code>hreflang</code> y los enlaces internos del cambiador de idioma, no necesitas hacer nada.</p>
<p>Si ves que Google ignora los idiomas secundarios o que tarda mucho en encontrarlos, ahí es donde el mapa del sitio traducido empieza a justificarse por sí solo.</p>
<p>¿Que no te llega, que usas plugins SEO distintos a los seis que he cubierto aquí?, abre un ticket directamente al soporte de Weglot pidiendo el código adaptado, hasta donde he comprobado, suelen responder bastante bien. Y, por supuesto, aquí me tienes, pregunta lo que quieras abajo en los comentarios, como siempre.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ayudawp.com/sitemap-weglot/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>¿Entonces en qué quedamos, me hago un plugin con IA o no? Pues depende…</title>
		<link>https://ayudawp.com/dudas-hacer-plugin-ia/</link>
					<comments>https://ayudawp.com/dudas-hacer-plugin-ia/#respond</comments>
		
		<dc:creator><![CDATA[Fernando Tellado]]></dc:creator>
		<pubDate>Mon, 25 May 2026 06:28:00 +0000</pubDate>
				<category><![CDATA[IA + WordPress]]></category>
		<category><![CDATA[Plugins WordPress]]></category>
		<category><![CDATA[Programación + WordPress]]></category>
		<category><![CDATA[Tutoriales - Trucos]]></category>
		<category><![CDATA[WordPress.com]]></category>
		<category><![CDATA[WordPress.org]]></category>
		<category><![CDATA[Avanzado]]></category>
		<category><![CDATA[Principiante]]></category>
		<guid isPermaLink="false">https://ayudawp.com/?p=159188</guid>

					<description><![CDATA[La pregunta no es si una IA puede crearte un plugin de WordPress, eso ya sabes que sí, lo he contado en varios artículos anteriores y muy probablemente lo hayas probado por tu cuenta. La pregunta de verdad es otra, es la de que ¿deberías hacerte tú ese plugin con IA, en tu caso concreto, para lo que tú quieres?]]></description>
										<content:encoded><![CDATA[<p>La pregunta no es si <strong>una IA puede crearte un plugin  WordPress</strong>, eso ya sabes que sí, lo he contado en varios artículos anteriores y muy probablemente lo hayas probado por tu cuenta.</p>
<p>La pregunta es otra, es una que no se hacen muchos y deberían, y es esa de … <strong>¿deberías hacer tú ese plugin con IA, en tu caso concreto, para lo que tú quieres?</strong></p>
<p>Y la respuesta no es la misma para todo el mundo. <strong>Depende de tres cosas</strong> que puedes responder en treinta segundos, y son qué tipo de plugin necesitas, qué nivel de programación tienes y qué riesgo asumes si algo falla.</p>
<p>A partir de ahí <strong>vamos a ver dónde tiene sentido tirar de IA y dónde es una mala idea</strong>.</p>
<h2>Cuándo sí tiene sentido hacerte un plugin con IA</h2>
<p>Hay un tipo de plugin que <strong>es justo lo que la IA hace mejor</strong>, y casualmente es el que más necesita la mayoría de la gente que tiene un WordPress.</p>
<p>Me refiero a los <strong>plugins pequeños y específicos</strong>, esos que <strong>resuelven un problema concreto tuyo</strong> y que jamás vas a publicar en wordpress.org porque <strong>a (casi) nadie más le sirven</strong>.</p>
<p>Por ejemplo:</p>
<ul>
<li>Un <code>mu-plugin</code> que añade una columna en el listado de entradas con un dato concreto que te interesa.</li>
<li>Un fragmento que cambia el comportamiento del editor para tu flujo de trabajo.</li>
<li>Una automatización interna que cruza datos entre dos plugins que ya tienes instalados.</li>
<li>Un widget de escritorio personalizado para vigilar algo de tu web.</li>
<li>Un shortcode a medida que te ahorra copiar y pegar el mismo HTML en cien lugares.</li>
</ul>
<p>En estos casos <strong>la IA te resuelve en quince minutos lo que antes te costaba un día buscando</strong> códigos en internet y adaptándolos. Y como el código va a vivir en tu web y lo usas tú y nadie más, <strong>los riesgos son muy controlables</strong>.</p>
<p>También <strong>funciona bien para prototipos y proyectos</strong>. Si tienes una idea de plugin pero no sabes si va a funcionar comercialmente, <strong>montar una versión mínima con IA te permite validarla antes de invertir tiempo o dinero</strong> en hacer la versión seria.</p>
<p>La probabilidad de que ese prototipo acabe siendo el plugin definitivo es baja, pero <strong>como herramienta para validar el concepto es de lo mejor que hemos tenido en décadas</strong>.</p>
<p>Y queda otro caso del que poco he leído por ahí, que es <strong>aprender a programar con la IA</strong>. Le pides que te genere algo sencillo, le preguntas el porqué de cada decisión, y vas entendiendo el código.</p>
<p>La IA no te hace el trabajo, te lo explica mientras lo hace, y es una forma de las más rápidas de aprender desarrollo WordPress que he visto, <a href="https://campus.ayudawp.com/curso-avanzado-de-programacion-profesional-para-wordpress/" target="_blank" rel="noopener">no es un curso avanzado de programación profesional</a>, pero ayuda mucho.</p>
<h2>Cuándo no te lo recomiendo en absoluto</h2>
<p>Hay otras <strong>situaciones donde tirar de IA para crear un plugin es la antesala del desastre</strong>. No porque la IA sea mala, sino porque el contexto pide cosas que la IA hoy en día no garantiza.</p>
<ol>
<li>Cualquier plugin que vayas a <strong>publicar en el repositorio de wordpress.org sin revisar el código</strong> línea a línea. La gente que descarga un plugin desde el directorio oficial confía en que <strong>detrás hay alguien que entiende lo que hace</strong>. Si lo único que hay detrás eres tú con un prompt bien afinado, no tienes ese conocimiento, y <strong>un fallo de seguridad tuyo lo van a sufrir miles de instalaciones</strong>.</li>
<li>Cualquier plugin que toque pagos, <strong>datos bancarios, datos personales sensibles</strong><span style="font-size: 16px;"><strong> o el proceso de pago</strong> de una tienda. El riesto de ataque es enorme, las consecuencias de un fallo son legales además de técnicas, y aquí no vale el «</span><em style="font-size: 16px;">lo he probado y va</em><span style="font-size: 16px;">«.</span></li>
<li>El tercero es irónico pero pasa, y mucho, y me refiero a los <strong style="font-size: 16px;">plugins de seguridad</strong><span style="font-size: 16px;">. Si pides a una IA que te haga un plugin para mejorar la seguridad de tu WordPress, es probable que el propio plugin sea inseguro. He visto código generado con IA que añadía cabeceras de seguridad, sí, pero por el camino dejaba un <code>endpoint</code> REST sin </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">permission_callback</code><span style="font-size: 16px;"> que daba acceso a cualquiera. Lo bueno tapado por lo malo. </span>Y sí, <a style="font-size: 16px; background-color: #ffffff;" href="https://vigilante.works/es/" target="_blank" rel="noopener nofollow">yo tengo un plugin de seguridad</a><span style="font-size: 16px;">, pero le pongo el doble o triple de revisión que a cualquier otra cosa que hago, me hago responsable, y llevo años desarrollando código para WordPress.</span></li>
<li>Plugins que vayas a usar en <strong>webs de clientes o vender como producto comercial</strong> sin auditar. El día que falle no estás solo tú en juego, está tu reputación profesional. Y si el cliente pregunta por qué pasó algo y la respuesta es «<em>es que ese trozo lo hizo Claude</em>«, la conversación va a ser jodida.</li>
</ol>
<h2>Lo que la IA hace mal cuando crea plugins</h2>
<p>Pues sí, no todo son alegrías, porque hay <strong>problemas técnicos que aparecen una y otra vez en código generado por IA</strong>, sin importar cuál uses, ni siquiera cómo la uses.</p>
<p>Te lo cuento desde la experiencia de revisar mucho código generado y desde lo que recogen las propias guías de seguridad y rendimiento de WordPress.</p>
<ul>
<li><strong>Saneado a medias:</strong> Las IAs casi siempre se acuerdan de sanitizar lo que reciben, pero a veces eligen la función equivocada. Te ponen <code>sanitize_text_field()</code> cuando deberían usar <code>sanitize_email()</code>, o <code>esc_html()</code> donde tocaba <code>wp_kses_post()</code>. El plugin no falla, pero o se carga datos legítimos o deja pasar cosas que no debería.</li>
<li><strong>Se te olvidan los nonces:</strong> En formularios sencillos suelen estar. En endpoints AJAX a veces sí. En endpoints REST casi nunca. Y si el plugin tiene una función de borrado o de modificación masiva, ahí tienes una vulnerabilidad CSRF de manual.</li>
<li><strong>Queries sin preparar:</strong> Cuando el código mete consultas SQL directas con <code>$wpdb</code>, hay una probabilidad muy alta de que falte el <code>$wpdb-&gt;prepare()</code>. Le has dicho a la IA que haga una consulta y la ha hecho, pero la concatenación de variables es la receta clásica de la inyección SQL.</li>
<li><strong>Capacidades sin comprobar:</strong> Los <code>current_user_can()</code> que faltan son uno de los fallos más típicos. La IA pone la lógica de la funcionalidad, pero se olvida de comprobar si el usuario que la está ejecutando tiene permiso. Y el resultado es que cualquier suscriptor llamando al endpoint puede hacer cosas que solo un administrador debería poder hacer.</li>
<li><strong>Faltan escapes:</strong> Imprimir variables sin <code>esc_html()</code>, sin <code>esc_attr()</code> o sin <code>esc_url()</code> es la base del XSS. La IA escapa los datos obvios pero se le escapan los menos obvios, sobre todo los que vienen de la base de datos, que ella asume seguros pero pueden no serlo si llegaron ahí desde otro sitio.</li>
<li><strong>Rendimiento desastroso:</strong> Queries dentro de bucles, <code>posts_per_page</code> a <code>-1</code> sin pensárselo, transients dinámicos que llenan la tabla de opciones, scripts cargados en todas las páginas en lugar de solo donde hacen falta. Funciona en una web vacía, pero en una con tráfico real tumba el servidor.</li>
</ul>
<p>Todo esto es solucionable, pero <strong>arreglarlo requiere saber identificarlo, y para identificarlo hace falta saber programar</strong>.</p>
<p>Si no sabes qué es un <code>nonce</code> o por qué es importante el <code>permission_callback</code> <strong>la IA puede generarte un plugin que parece perfecto y que en realidad es un coladero</strong>.</p>
<p>Y luego está el otro problema, el del día de mañana.</p>
<p>¿Quién mantiene ese plugin que has generado hoy cuando la IA del momento ya no esté disponible o haya cambiado tanto que ni entienda su propio código de hace dos años?</p>
<p>Si te interesa el lado del mantenimiento a largo plazo <a href="https://ayudawp.com/mantenimiento-web-vibe-coding/" target="_blank" rel="noopener ugc">echa un vistazo a este artículo</a>, ahí lo desarrollo a fondo.</p>
<h2>Cinco preguntas para saber si TÚ deberías hacer plugins con IA</h2>
<p>Antes de pedirle a la IA que te genere ese plugin, <strong>contéstate estas preguntas</strong> con sinceridad. Si la mayoría son positivas adelante, si tienes varias que como que no, pues mejor pensártelo dos veces.</p>
<h4>¿Sabrías interpretar este código si la IA desapareciera mañana?</h4>
<p>Si la respuesta es que no <strong>lo que tienes no es un plugin sino una caja negra</strong> que vas a tener que mantener a ciegas.</p>
<p>Para uso personal puede ir bien y aceptas el coste.  Ahora bien, para cualquier otro escenario es un problema, porque el día que algo falle y la IA del momento no sirva o cueste tres veces más, te toca <strong>pelear con código que nunca fue tuyo del todo</strong>.</p>
<h4>¿Lo va a usar alguien más que tú?</h4>
<p>Si la respuesta es no el riesgo está controlado, si te peta te jodes tú y punto.</p>
<p>Pero si la respuesta es que sí (clientes, usuarios del repositorio, miembros de tu comunidad), <strong>las consecuencias de un fallo se multiplican</strong> y la exigencia de calidad sube mucho.</p>
<p>La IA por sí sola no llega a ese nivel,  hace <strong>falta alguien que SEPA</strong> revisar y mejorar el código.</p>
<h4>¿Toca dinero, datos personales o identificaciones?</h4>
<p>Si la respuesta es sí ya tienes media decisión tomada. O lo escribes tú con la IA como copiloto y revisas cada línea entendiendo lo que hace, o se lo encargas a alguien que sepa.</p>
<p>Generar y publicar sin más es buscarse problemas serios.</p>
<h4>¿Tendrías capacidad de auditar el código antes de ponerlo en producción?</h4>
<p>Auditar no es leer y decir «parece que funciona», es <strong>comprobar punto por punto</strong> que la sanitización es correcta, los <code>nonces</code> están en su sitio, las capacidades se verifican, las queries están preparadas y los escapes de salida son los adecuados.</p>
<p>Si tienes ese conocimiento <strong>la IA es un turbo acojonante</strong>, pero si no lo tienes mejor empieza por aprender lo básico antes de montar cosas que no entiendes.</p>
<h4>¿Aceptas la idea de que el plugin sea desechable?</h4>
<p>A veces la respuesta correcta es que sí, que lo montas rápido, lo usas unos meses y cuando deje de servirte lo tiras y haces otro. En ese caso la IA encaja como anillo al dedo.</p>
<p>Pero si lo que quieres es montar algo que dure años y que evolucione la cosa es muy distinta.</p>
<h2>Por dónde seguir</h2>
<p>Si después de leer todo esto sigues teniendo claro que quieres hacerte tu plugin con IA, en el blog tienes casi de todo para hacerlo bien.</p>
<p>Empezando por la base teórica y subiendo nivel:</p>
<ul>
<li><a href="https://ayudawp.com/crear-plugins-con-ia/" target="_blank" rel="noopener ugc">Crear plugins con IA</a> y <a href="https://ayudawp.com/crear-plugin-ia/" target="_blank" rel="noopener ugc">crear un plugin con IA</a> para los fundamentos.</li>
<li><a href="https://ayudawp.com/vibe-coding-plugin-wordpress/" target="_blank" rel="noopener ugc">Vibe coding aplicado a plugins WordPress</a> y <a href="https://ayudawp.com/vibe-agentic-coding/" target="_blank" rel="noopener ugc">vibe coding y agentic coding</a> para entender los dos enfoques principales.</li>
<li><a href="https://ayudawp.com/ia-crear-contenidos-codigo/" target="_blank" rel="noopener ugc">IA para crear contenidos y código</a> y <a href="https://ayudawp.com/inteligencia-artificial-crear-contenidos-codigo/" target="_blank" rel="noopener ugc">el original sobre inteligencia artificial para crear contenidos y código</a> para el contexto más amplio.</li>
</ul>
<p>Yo mismo he hecho varios plugins con este tipo de procesos. <a href="https://es.wordpress.org/plugins/vigia/" target="_blank" rel="noopener ugc">VigIA</a>, ese plugin de control y analítica de bots de IA del que tanto hablo, salió de horas de trabajo con asistencia de IA, igual que <a href="https://profiles.wordpress.org/fernandot/#content-plugins" target="_blank" rel="noopener ugc nofollow">algunos de mis otros plugins</a>.</p>
<p>La diferencia entre que esos plugins funcionen bien y los que ves circulando con fallos de seguridad básicos está exactamente en lo que te he contado en el apartado de los problemas técnicos.</p>
<p>Hay que <strong>revisar cada línea</strong> entendiendo lo que hace, no tragar con lo primero que sale y <strong>aplicar de manera sistemática las prácticas de seguridad y rendimiento</strong> que WordPress lleva años publicando.</p>
<p>La IA es una herramienta excelente para esto, pero como cualquier herramienta <strong>el resultado depende mucho menos de la herramienta y mucho más de quien la usa</strong>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ayudawp.com/dudas-hacer-plugin-ia/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>