Si llevas tiempo con WordPress hay bastantes posibilidades de que tu base de datos esté llena de restos de plugins y temas que ya no existen. Hoy me refiero en concreto a todos esos shortcodes que un día hacían algo útil y ahora no sirven absolutamente para nada, pero siguen ahí, impresos en el contenido de tus páginas, entradas, widgets y opciones del tema, como fantasmas.
Algunos se ven directamente en pantalla tal cual, con sus corchetes y todo, otros no se ven porque WordPress los ignora en silencio, pero siguen ocupando espacio, ensuciando el HTML que ve Google y ralentizando las consultas a la base de datos. En los casos más extremos, como cuando usabas un maquetador visual basado en shortcodes, pueden dejar páginas enteras completamente ilegibles.
Este artículo cubre absolutamente todo lo que necesitas saber sobre los shortcodes huérfanos, qué son exactamente, por qué aparecen, dónde se esconden (en más sitios de los que crees), cómo encontrarlos con diferentes herramientas y todos los métodos disponibles para eliminarlos o neutralizarlos, desde el más sencillo hasta el más avanzado, para todos los niveles, con código listo para usar.
Qué es un shortcode y por qué se queda huérfano
Un shortcode es ese fragmento entre corchetes que WordPress interpreta y convierte en algo concreto, ya sea un formulario de contacto, un slider, una galería de imágenes, un botón, un bloque de contenido, una restricción de acceso. Lo registra el plugin o el tema que lo utiliza a través de la función add_shortcode(), y mientras ese plugin o tema está activo, WordPress sabe exactamente qué hacer con él.
El problema llega cuando ese plugin ya no está. Si desinstalaste el plugin, cambiaste de tema o simplemente lo desactivaste sin limpiar el contenido antes, los shortcodes se quedan ahí, huérfanos, sin nadie que los interprete. A partir de ese momento pueden pasar dos cosas:
- Que WordPress los ignore y no muestre nada, que es el comportamiento más habitual en versiones modernas de WordPress. El shortcode está en la base de datos pero no se procesa, así que en pantalla no aparece nada donde antes había algo.
- Que WordPress los muestre literalmente en pantalla, tal cual, con sus corchetes. Esto depende de cómo está configurado WordPress y de si hay algún filtro interviniendo.
Lo segundo es lo peor porque tus visitantes ven algo así en medio del texto de una página que debería tener un formulario de contacto:
[contact-form-7 id="48" title="Formulario de contacto"]
O algo bastante más caótico si era de un maquetador visual:
[et_pb_section fb_built="1" _builder_version="4.16.0"][et_pb_row _builder_version="4.16.0"][et_pb_column type="4_4" _builder_version="4.16.0"][et_pb_text _builder_version="4.16.0"]Tu contenido de aquí[/et_pb_text][/et_pb_column][/et_pb_row][/et_pb_section]
Ninguna de las dos situaciones es buena. Aunque no se vean hay razones suficientes para limpiarlos.
Nota: Revisa manualmente los códigos de ejemplos para Contact Form 7, no los apliques directamente porque como yo lo uso y se rompería la página al tratar de mostrarlos les he añadido un espacio entre el primer corchete (
[) y el identificadorcontact-form-7).
El impacto en SEO, rendimiento y base de datos de los shortcodes huérfanos
El argumento de que los shortcodes no afectan al SEO porque Google solo ve el HTML es verdad a medias.
Si el plugin está activo y el shortcode se procesa correctamente Google ve el HTML resultante, hasta aquí bien. Pero si el shortcode está huérfano y aparece en pantalla como texto en crudo, Google lo ve exactamente igual que un visitante humano, como texto basura que no aporta nada al contenido de la página.
Incluso cuando no se muestran en pantalla, los shortcodes de maquetadores generan un volumen de código en la base de datos que puede ser enorme. Una sola página construida con el viejo Visual Composer puede tener KBs de shortcodes almacenados en post_content.
Ese contenido tiene que viajar de la base de datos al servidor con cada carga de página, procesarse y descartarse. En webs con muchas páginas eso es un montón.
En cuanto al rendimiento, cada shortcode que aparece en el contenido pasa por el sistema de procesado de shortcodes de WordPress aunque devuelva vacío. No es dramático en una web pequeña pero en una instalación grande con cientos de entradas heredadas puede tener un impacto alto en los tiempos de respuesta.
Y luego está la cuestión de mantenimiento, porque una base de datos limpia es más fácil de auditar, de migrar y de depurar cuando algo falla.
¿De quién es la culpa de que haya shortcodes huérfanos?
Antes de ponerse a buscar y eliminar, conviene tener claro de dónde viene el problema en tu caso concreto. Los orígenes más habituales son los que te cuento a continuación, habrá más (o no) pero estos están seguro.
Maquetadores visuales
Son los mayores generadores de shortcodes huérfanos por volumen. Divi, WP Bakery (antes Visual Composer), Beaver Builder, King Composer, SiteOrigin y otros. Todos ellos (o al menos sus versiones más antiguas) almacenan el contenido de tus páginas como shortcodes en la base de datos. Cuando cambias de maquetador o de tema sin migrar el contenido primero, lo que queda puede ser cientos o miles de líneas de shortcodes en cada página.
Plugins de formularios
Este es el caso que más se pasa por alto y que más shortcodes huérfanos genera en todo tipo de webs. Contact Form 7 es el plugin de WordPress más instalado del mundo, y su shortcode [ contact-form-7 id="X" ] aparece literalmente en millones de páginas de contacto, páginas de presupuesto, pies de página y widgets.
Cuando alguien migra a otro plugin de formularios (Gravity Forms, WPForms, Fluent Forms) o simplemente desinstala CF7, esos shortcodes se quedan ahí donde estuvieran, sin uso ni beneficio.
Gravity Forms ([gravityform id="1"]), WPForms ([wpforms id="52"]), Ninja Forms ([ninja_form id="1"]), Formidable Forms ([formidable id="2"]) y prácticamente cualquier plugin de formularios tiene exactamente el mismo problema.
Plugins de sliders y galerías
Elige tu diversión. Revolution Slider, Meta Slider, Soliloquy, Smart Slider 3, Nivo Slider, Envira Gallery, FooGallery, y prácticamente todos los carruseles de contenido o imágenes usan shortcodes para incrustar su contenido en páginas y entradas. Si en algún momento cambiaste de plugin de sliders o galerías sin limpiar el contenido, esos shortcodes siguen ahí.
Plugins de membresía y restricción de contenido
MemberPress, Restrict Content Pro, s2Member, LearnDash y similares usan shortcodes para proteger fragmentos de contenido o mostrar mensajes según el nivel de acceso del usuario. Son especialmente delicados cuando quedan huérfanos porque pueden ocultar partes del contenido o mostrar mensajes de acceso denegado sin sentido.
Plugins de redes sociales, botones de compartir, incrustados
Muchos plugins de generaciones anteriores insertaban sus botones de compartir mediante shortcodes en el contenido, y si usaste shortcodes para insertar vídeos seguro que también viste Mazinger Z o cera le andas. Si los usaste hace años y luego los cambiaste por soluciones más modernas esos shortcodes pueden seguir en tus entradas más antiguas.
WooCommerce y sus extensiones
WooCommerce incluye sus propios shortcodes nativos ([products], [woocommerce_cart], [woocommerce_checkout], [woocommerce_my_account]) que se registran mientras el plugin esté activo. Pero el problema mayor viene de las extensiones de WooCommerce, como filtros de productos, comparadores, listas de deseos, tabs de producto con contenido personalizado.
Si desinstalaste alguna de estas extensiones sin limpiar el contenido sus shortcodes siguen en páginas y en las descripciones de productos.
Temas mutipropósito premium con shortcodes propios
Muchos temas de sitios como ThemeForest y otros marketplaces llevan incorporados sus propios shortcodes para crear elementos de diseño como cajas de información, iconos, columnas, botones, secciones destacadas.
Cuando cambias de tema o plugin todo eso se convierte en texto inútil.
Dónde se esconden
El error más habitual cuando alguien busca y elimina shortcodes es hacerlo solo en el contenido de entradas y páginas. Eso es quedarse muy a medias. Los shortcodes pueden estar en muchos más sitios, y si no los limpias todos, el problema persiste.
- Contenido de entradas y páginas: la tabla
wp_posts, columnapost_content. Es el sitio más obvio. - Extractos: la columna
post_excerptde la misma tabla y se olvida con mucha frecuencia. Si pusiste un shortcode en el extracto de una entrada para mostrar algo en las páginas de archivo o en el feed ese shortcode puede seguir ahí. - Tipos de contenido personalizados: también en
wp_postspero con diferentes valores enpost_type. Si usas CPTs (productos de WooCommerce, portfolios, cursos, propiedades), sus contenidos también pueden tener shortcodes. - Metadatos de entradas y campos personalizados: la tabla
wp_postmeta. Muchos plugins guardan contenido con shortcodes en campos personalizados. ACF en particular puede tener campos de tipotextareao contenido WYSIWYG llenos de ellos. Las descripciones cortas de productos de WooCommerce (_woocommerce_short_description) también van aquí. - Widgets: los widgets de texto clásicos, los widgets del editor de bloques y los de cualquier plugin de widgets personalizados se guardan en
wp_optionscon la clavewidget_texto similares. Si usabas un shortcode de formulario en la barra lateral y desinstalaste el plugin, ese widget sigue teniendo el shortcode. - Opciones del tema y del personalizador: también en
wp_options. Algunos temas permiten insertar shortcodes en campos de texto del personalizador, en el pie de página, cabeceras fijas, avisos legales, textos de copyright, etc. Estos son especialmente difíciles de encontrar porque no hay una clave estándar para buscarlos. - Menús de navegación: algunos plugins añaden shortcodes en los títulos o en el CSS de los elementos de menú. Raro, pero existe.
- Plantillas de correo electrónico personalizadas: si usas WooCommerce o algún plugin de email marketing con plantillas editables desde el escritorio, esas plantillas pueden tener shortcodes. Al cambiar de plugin, los shortcodes quedan en las plantillas guardadas.
- Bloques del editor de bloques (Gutenberg): cuando importas o migras contenido clásico a Gutenberg, los shortcodes a veces acaban dentro de bloques de párrafo o bloques clásicos. Gutenberg tiene un bloque específico llamado shortcode para procesarlos, pero si están dentro de un bloque de párrafo normal no se procesan.
- Opciones de SEO y meta descriptions: plugins como Yoast SEO o Rank Math permiten personalizar los snippets de SEO, y algunos usuarios han introducido shortcodes ahí. Si el plugin que generaba ese shortcode ya no está el texto puede aparecer raro en las etiquetas meta.
¿Donde puedes encontrar los shortcodes huérfanos?
Antes de eliminar nada hay que saber qué tienes y dónde están. Lanzarse a borrar sin localizar primero puede dejarte con contenido roto o con shortcodes que crees que has eliminado pero siguen apareciendo en algún widget olvidado, y como acabamos de ver antes pueden estar en montones de sitios, algunos nada obvios.
Con un shortcode de diagnóstico temporal
Una técnica elegante para localizar shortcodes concretos en el contenido de entradas y páginas sin instalar ningún plugin es crear un shortcode de diagnóstico temporal, sí otro. ¿A que es algo casi poético empezar la eliminación de shortcodes creando uno nuevo?
Lo añades al functions.php del tema hijo o a un plugin de snippets, usas el shortcode en una página nueva para obtener el listado y luego lo eliminas.
/* Shortcode de diagnóstico temporal: encuentra entradas que contienen un shortcode concreto */
// Uso: [ayudawp_buscar_shortcode find="nombre-del-shortcode"]
// Ejemplo: [ayudawp_buscar_shortcode find="contact-form-7"]
// IMPORTANTE: elimina este código una vez usado
add_shortcode( 'ayudawp_buscar_shortcode', 'ayudawp_shortcode_finder' );
function ayudawp_shortcode_finder( $atts ) {
// Solo accesible para administradores
if ( ! current_user_can( 'manage_options' ) ) {
return '';
}
$atts = shortcode_atts( array(
'find' => '',
), $atts );
if ( empty( $atts['find'] ) ) {
return '<p>Indica el shortcode a buscar con el atributo find="nombre".</p>';
}
// Busca en entradas, páginas y cualquier CPT publicado
$args = array(
's' => $atts['find'],
'posts_per_page' => -1,
'post_type' => 'any',
'post_status' => array( 'publish', 'draft', 'private' ),
);
$query = new WP_Query( $args );
if ( ! $query->have_posts() ) {
return '<p>No se han encontrado entradas con ese shortcode.</p>';
}
$output = '<p><strong>Entradas que contienen [' . esc_html( $atts['find'] ) . ']:</strong></p>';
$output .= '<ul>';
while ( $query->have_posts() ) {
$query->the_post();
$output .= '<li><a href="' . esc_url( get_edit_post_link() ) . '">';
$output .= esc_html( get_the_title() ) . ' (' . esc_html( get_post_type() ) . ')</a></li>';
}
$output .= '</ul>';
wp_reset_postdata();
return $output;
}
Una vez añadido el código, crea una página nueva (en borrador, no hace falta que la publiques) y escribe en su contenido:
[ayudawp_buscar_shortcode find="contact-form-7"]
Al previsualizar la página verás un listado de todas las entradas y páginas donde aparece ese shortcode, con enlace directo al editor (Dato curioso: también verás la actual).
Cuando termines elimina el código del functions.php.
Ten en cuenta que este método busca en el contenido (post_content) mediante la búsqueda nativa de WordPress, que no siempre indexa todo perfectamente.
Con un plugin
La opción más mencionada históricamente para localizar shortcodes ha sido el plugin Shortcodes in Use. Y sí, permite buscar shortcodes concretos en el contenido de tus entradas filtrando por tipo de contenido (páginas, entradas, CPTs, widgets), pero tiene un problema, que lleva varios años sin actualizarse y su compatibilidad con versiones modernas de WordPress no está garantizada. Puedes probarlo pero ojito.
Para sustituir Shortcodes in Use he encontrado Shortcodes Finder, que hace lo mismo y mucho más porque encuentra todos los shortcodes por tipo de contenido, muestra los huérfanos sin usar y permite desactivarlos con un clic desde el panel. Es el sustituto natural.
Con consultas SQL
Esta es la manera más directa y completa de saber qué shortcodes tienes y dónde. Accede a tu base de datos desde phpMyAdmin (en tu panel de hosting), Adminer o cualquier cliente SQL que uses.
Antes de ejecutar nada, un par de notas importantes:
- Sustituye siempre
wp_por el prefijo real de tu base de datos (puedes verlo en tu archivowp-config.php) - Ejecuta primero las consultas
SELECTpara ver los resultados antes de hacer ningúnUPDATE.
Para buscar un shortcode concreto en el contenido de entradas y páginas:
SELECT ID, post_title, post_type, post_status FROM wp_posts WHERE post_content LIKE '%Error: Formulario de contacto no encontrado.
+)[^\]]*\]/', $post->post_content, $matches ); if ( empty( $matches[1] ) ) { return; } foreach ( array_unique( $matches[1] ) as $tag ) { if ( ! array_key_exists( $tag, $shortcode_tags ) ) { add_shortcode( $tag, '__return_empty_string' ); } } }
Función strip_shortcodes() y filtros sobre the_content
WordPress incluye de serie la función strip_shortcodes() que elimina los shortcodes del texto que procesa. La diferencia con el método anterior es que actúa en el filtro de salida del contenido, así que los shortcodes no se procesan, simplemente se eliminan del HTML antes de mostrarlo al visitante.
La forma más sencilla de usarlo es aplicarlo al filtro the_content:
// Elimina todos los shortcodes no reconocidos del contenido antes de mostrarlo.
// Atención: strip_shortcodes() elimina TODOS los shortcodes no registrados.
add_filter( 'the_content', 'ayudawp_strip_orphan_shortcodes', 5 );
function ayudawp_strip_orphan_shortcodes( $content ) {
global $shortcode_tags;
$registered = array_keys( $shortcode_tags );
// Buscamos shortcodes en el contenido
preg_match_all( '/\[([a-zA-Z_\-]+)[^\]]*\]/', $content, $matches );
if ( empty( $matches[1] ) ) {
return $content;
}
foreach ( array_unique( $matches[1] ) as $tag ) {
// Solo eliminamos los que no están registrados
if ( ! in_array( $tag, $registered, true ) ) {
// Elimina la etiqueta de apertura con cualquier combinación de atributos
$content = preg_replace(
'/\[' . preg_quote( $tag, '/' ) . '(?:\s[^\]]*)?\/?\]/',
'',
$content
);
// Elimina la etiqueta de cierre si existe
$content = preg_replace(
'/\[\/' . preg_quote( $tag, '/' ) . '\]/',
'',
$content
);
}
}
return $content;
}
La prioridad 5 hace que se ejecute antes que el procesado normal de shortcodes de WordPress (prioridad 11), lo correcto en este tipo de acciones.
La limitación importante de este enfoque con expresiones regulares básicas es que no maneja bien los shortcodes con etiqueta de cierre que envuelven contenido, como [shortcode]contenido envuelto[/shortcode]. Si eliminas solo las etiquetas el contenido interior puede quedar suelto en el HTML de forma inesperada.
Eliminar shortcodes con etiqueta de cierre y contenido anidado
Los shortcodes más complejos, especialmente los de maquetadores visuales como Divi o WP Bakery, tienen estructura de apertura y cierre, y además se anidan unos dentro de otros:
[et_pb_section][et_pb_row][et_pb_column]contenido[/et_pb_column][/et_pb_row][/et_pb_section]
Para estos casos necesitas una estrategia diferente que elimine tanto las etiquetas como el contenido que envuelven.
El siguiente código hace justamente eso:
// Elimina shortcodes con estructura de apertura y cierre (y su contenido interior).
// Especialmente útil para maquetadores como Divi o WP Bakery.
add_filter( 'the_content', 'ayudawp_strip_wrapping_orphan_shortcodes', 5 );
function ayudawp_strip_wrapping_orphan_shortcodes( $content ) {
global $shortcode_tags;
// Patrón que captura shortcodes con cierre: [tag atributos]...contenido...[/tag]
// El modificador 's' hace que el punto también coincida con saltos de línea
$pattern = '/\[([a-zA-Z_\-]+)(?:\s[^\]]*)?\](.*?)\[\/\1\]/s';
preg_match_all( $pattern, $content, $matches, PREG_SET_ORDER );
if ( empty( $matches ) ) {
return $content;
}
foreach ( $matches as $match ) {
$tag = $match[1];
// Solo actúa si el shortcode no está registrado
if ( ! array_key_exists( $tag, $shortcode_tags ) ) {
// Elimina el bloque completo: apertura + contenido + cierre
$content = str_replace( $match[0], '', $content );
}
}
return $content;
}
Si en lugar de eliminar el contenido interior prefieres conservarlo (quitando solo las etiquetas del shortcode pero manteniendo lo que había dentro) cambia str_replace( $match[0], '', $content ) por str_replace( $match[0], $match[2], $content ).
Para una limpieza completa de maquetadores como Divi o WP Bakery lo más efectivo es combinar este código con el método de buscar y reemplazar en base de datos que veremos enseguida. El código resuelve el problema de visualización de forma inmediata, el reemplazo en base de datos lo resuelve de forma permanente.
Buscar y reemplazar en la base de datos
Este es el método que elimina los shortcodes de verdad, borrándolos de la base de datos. Los resultados son permanentes así que la regla de oro es siempre hacer una copia de seguridad completa antes de ejecutar nada, sin excepciones.
Con Better Search Replace
Better Search Replace es el plugin de referencia para esto. Gratuito, bien mantenido y con una interfaz clara. Permite buscar un texto concreto en cualquier tabla de la base de datos y reemplazarlo por otro (o por nada).
El flujo de trabajo correcto es este: instala y activa el plugin, ve a Herramientas › Better Search Replace, introduce el shortcode exacto en el campo «Buscar», deja «Reemplazar por» vacío para eliminarlo, selecciona las tablas donde quieres buscar (wp_posts y wp_options como mínimo, todas si no estás seguro) y marca «Ejecutar como prueba» la primera vez. Eso te mostrará cuántos reemplazos haría sin ejecutarlos realmente. Si el número tiene sentido, repite sin la opción de prueba.
El límite de este plugin es que trabaja con texto literal. Si el shortcode tiene atributos variables (como un ID diferente en cada uso), necesitas hacerlo con expresiones regulares, lo que requiere WP-CLI o SQL directo.
Con WP-CLI
WP-CLI incluye el comando search-replace que admite expresiones regulares, lo que lo convierte en la herramienta perfecta para shortcodes con atributos variables. Siempre empieza con --dry-run para ver qué va a pasar antes de ejecutar de verdad:
Para un shortcode con texto exacto:
wp search-replace '[ contact-form-7 id="48" title="Formulario de contacto 1"]' '' wp_posts --dry-run
Para un shortcode con cualquier combinación de atributos (usando regex):
wp search-replace '\[ contact-form-7[^\]]*\]' '' wp_posts --regex --dry-run
Para limpiar shortcodes de Divi (prefijo et_pb_) en todo el contenido:
wp search-replace '\[/?et_pb_[^\]]*\]' '' wp_posts --regex --dry-run
Para WP Bakery (prefijo vc_):
wp search-replace '\[/?vc_[^\]]*\]' '' wp_posts --regex --dry-run
Cuando el resultado del dry-run es el esperado, ejecuta sin esa opción. El flujo completo recomendado, que hace copia de seguridad, ejecuta el reemplazo y limpia la caché:
wp db export backup-antes-limpieza-shortcodes.sql && wp search-replace '\Error: Formulario de contacto no encontrado.
]*\]' '' wp_posts --regex --report-changed-only && wp cache flush
Si el shortcode afecta también a metadatos o a opciones, añade las tablas correspondientes al comando:
wp search-replace '\[ contact-form-7[^\]]*\]' '' wp_posts wp_postmeta wp_options --regex --report-changed-only
Con SQL directo en phpMyAdmin
Para shortcodes con texto exacto e idéntico en todos los usos, la función REPLACE de MySQL es directa:
-- Primero comprueba cuántas filas afectaría
SELECT COUNT(*) FROM wp_posts
WHERE post_content LIKE '%[ contact-form-7 id="48" title="Formulario de contacto 1"]%';
-- Si el resultado es el esperado, ejecuta el reemplazo
UPDATE wp_posts
SET post_content = REPLACE(
post_content,
'[ contact-form-7 id="48" title="Formulario de contacto 1"]',
''
)
WHERE post_content LIKE '%contact-form-7%';
Para shortcodes con atributos variables necesitas REGEXP_REPLACE, disponible desde MySQL 8.x o MariaDB 10.x (comprueba la versión de tu servidor antes de usarlo):
-- Elimina todas las variantes del shortcode independientemente de sus atributos
UPDATE wp_posts
SET post_content = REGEXP_REPLACE(
post_content,
'\\[ contact-form-7[^\\]]*\\]',
''
)
WHERE post_content REGEXP '\\Error: Formulario de contacto no encontrado.
+[^\\]]*\\].*?\\[/et_pb_[a-z_]+\\]',
''
)
WHERE post_content REGEXP '\\[et_pb_';
Con este último mucho cuidado, si el contenido que está dentro de las etiquetas del maquetador lo quieres conservar de alguna forma esta consulta lo borra todo. Úsalo cuando las páginas van a ser reconstruidas de cero con el nuevo tema o maquetador.
Ejecutar un script PHP de limpieza masiva con WP_Query
Una alternativa intermedia entre editar a mano y hacer un reemplazo global en la base de datos es usar un script PHP que recorra todas las entradas afectadas y limpie cada una individualmente, con más control sobre qué se elimina. Esto es muy útil cuando quieres eliminar shortcodes de apertura y cierre pero conservar el contenido interior.
El siguiente script busca todas las entradas que contienen un shortcode concreto y elimina solo las etiquetas (apertura y cierre), conservando el texto que había dentro:
<?php
/**
* Plugin Name: Limpiador de shortcodes huérfanos (ejecutar una vez)
* Description: Elimina las etiquetas de shortcodes huérfanos conservando el contenido interior.
* IMPORTANTE: desactivar y eliminar este plugin después de usarlo.
* Version: 1.0
* Author: Fernando Tellado
*/
// Solo se ejecuta cuando un administrador accede al escritorio
add_action( 'admin_init', 'ayudawp_limpiar_shortcodes_una_vez' );
function ayudawp_limpiar_shortcodes_una_vez() {
// Comprobación de seguridad: solo administradores
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// Evita ejecuciones múltiples usando una opción de control
if ( get_option( 'ayudawp_limpieza_shortcodes_ejecutada' ) ) {
return;
}
// Lista de shortcodes a limpiar (solo las etiquetas, el contenido interior se conserva)
$shortcodes_a_limpiar = array(
'custom_shortcode',
'clima_semanal',
'privado',
// Añade aquí los shortcodes que quieras limpiar
);
// Construye el patrón regex para todos los shortcodes de la lista
$tags_patron = implode( '|', array_map( 'preg_quote', $shortcodes_a_limpiar ) );
// Patrón para etiqueta de apertura (con o sin atributos) y etiqueta de cierre
$patron_apertura = '/\[(?:' . $tags_patron . ')(?:\s[^\]]*)?\]/';
$patron_cierre = '/\[\/(?:' . $tags_patron . ')\]/';
// Obtiene todas las entradas publicadas (y borradores) de cualquier tipo
$args = array(
'posts_per_page' => -1,
'post_type' => 'any',
'post_status' => array( 'publish', 'draft', 'private' ),
'fields' => 'ids',
);
$posts = get_posts( $args );
$contador = 0;
foreach ( $posts as $post_id ) {
$post = get_post( $post_id );
$contenido_original = $post->post_content;
// Aplica la limpieza
$contenido_limpio = preg_replace( $patron_apertura, '', $contenido_original );
$contenido_limpio = preg_replace( $patron_cierre, '', $contenido_limpio );
// Solo actualiza si ha habido cambios reales
if ( $contenido_limpio !== $contenido_original ) {
wp_update_post( array(
'ID' => $post_id,
'post_content' => $contenido_limpio,
) );
$contador++;
}
}
// Marca la limpieza como ejecutada para no repetirla
update_option( 'ayudawp_limpieza_shortcodes_ejecutada', true );
// Registra un mensaje en el log para confirmación
error_log( 'AyudaWP: Limpieza de shortcodes completada. Entradas modificadas: ' . $contador );
}
Este script se ejecuta una sola vez (gracias al control mediante get_option). Después de que se ejecute desactívalo y elimínalo. Si quieres reiniciarlo para volver a ejecutarlo (por ejemplo, con una lista diferente de shortcodes), elimina la opción de control desde el escritorio de WordPress o con WP-CLI:
wp option delete ayudawp_limpieza_shortcodes_ejecutada
Casos especiales: maquetadores y plugins populares
Los shortcodes huérfanos más habituales no vienen de plugins raros, vienen de herramientas muy extendidas que la gente usa durante años y luego cambia o abandona. Aquí están los casos que más aparecen y cómo tratarlos.
Contact Form 7
Conviene dedicarle un apartado propio porque el volumen de webs afectadas es enorme. Contact Form 7 es el plugin de WordPress más instalado del mundo y su shortcode aparece en prácticamente cualquier página de contacto, y más sitios.
Cuando alguien migra a otro plugin de formularios o simplemente desinstala CF7 sin limpiar, ese shortcode queda por todas partes.
La particularidad de CF7 es que el ID del formulario varía en cada instalación y en cada uso, así que no sirve hacer un reemplazo de texto literal. Necesitas regex:
-- Busca todas las entradas con shortcodes de CF7
SELECT ID, post_title FROM wp_posts
WHERE post_content REGEXP '\\[ contact-form-7[^\\]]*\\]'
AND post_status NOT IN ('auto-draft', 'trash');
Con WP-CLI para eliminarlos:
wp search-replace '\[ contact-form-7[^\]]*\]' '' wp_posts --regex --dry-run
Si migras a WPForms, Gravity Forms u otro plugin, el proceso es el mismo: localiza los shortcodes del plugin anterior, elimínalos de la base de datos e inserta manualmente los nuevos shortcodes en las páginas correspondientes.
Gravity Forms, WPForms, Ninja Forms y Formidable
Mismo patrón que CF7 pero con diferentes nombres de shortcode. Para localizarlos y eliminarlos:
-- Gravity Forms: [gravityform id="X" title="false"]
SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%[gravityform%' AND post_status NOT IN ('auto-draft','trash');
-- WPForms: [wpforms id="X"]
SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%[wpforms%' AND post_status NOT IN ('auto-draft','trash');
-- Ninja Forms: [ninja_form id="X"]
SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%[ninja_form%' AND post_status NOT IN ('auto-draft','trash');
Divi (versiones anteriores a Divi 5)
Divi es el caso más extremo de dependencia de shortcodes y el que más desastres causa cuando alguien cambia de tema sin preparar el terreno. Hasta la versión 4.x, absolutamente todo el contenido generado con su maquetador visual estaba almacenado como shortcodes anidados en la base de datos. Una sola página podía tener cientos de líneas así:
[et_pb_section fb_built="1" _builder_version="4.21.0" _module_preset="default"] [et_pb_row _builder_version="4.21.0" _module_preset="default"] [et_pb_column type="4_4" _builder_version="4.21.0" _module_preset="default"] [et_pb_text _builder_version="4.21.0" _module_preset="default"] Aquí el contenido real de la página [/et_pb_text] [/et_pb_column] [/et_pb_row] [/et_pb_section]
Si cambias de tema sin convertir ese contenido primero lo que te queda en las entradas es un muro de shortcodes absolutamente ilegible. Y no es un problema pequeño pues en algunas instalaciones con Divi el volumen de shortcodes en la base de datos es tal que la diferencia de tamaño de wp_posts antes y después de la limpieza puede ser de varios megas.
Opción 1: migrar a Divi 5
Divi 5 ya está disponible y ha reescrito el framework desde cero. Lo más importante para el asunto del que estamos hablando es que Divi 5 abandona completamente los shortcodes y pasa a un sistema basado en bloques, similar en concepto a Gutenberg, pero con su propio motor.
El contenido de Divi ya no se almacena como shortcodes en post_content, sino en una estructura mucho más limpia y portable.
Al migrar de Divi 4.x a Divi 5 el propio proceso de migración convierte el contenido al nuevo formato, eliminando los shortcodes de la base de datos. Puedes leer los detalles en el esta guía de migración a Divi 5. Migrar a Divi 5 es la solución más limpia y con más futuro a largo plazo.
Opción 2: limpiar los shortcodes con buscar y reemplazar
Si cambias a otro tema o maquetador y necesitas limpiar los shortcodes de Divi, el prefijo et_pb_ es tu punto de partida. Con WP-CLI:
-- Primero, comprueba cuántas entradas tienen shortcodes de Divi
wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_content LIKE '%[et_pb_%' AND post_status NOT IN ('auto-draft','trash');"
-- Dry-run para ver qué pasaría
wp search-replace '\[/?et_pb_[^\]]*\]' '' wp_posts --regex --dry-run
-- Si el resultado es correcto, ejecuta sin dry-run
wp search-replace '\[/?et_pb_[^\]]*\]' '' wp_posts --regex --report-changed-only
Ten en cuenta que esto elimina todo el contenido que estaba dentro de los módulos de Divi. Si las páginas tienen texto real que quieres conservar, necesitas extraerlo antes de limpiar los shortcodes. Una estrategia posible es exportar el contenido de cada página a un archivo de texto, copiar solo el texto relevante y reconstruir la página con el nuevo maquetador.
Opción 3: neutralizar temporalmente mientras reconstruyes
Si el volumen de páginas es grande y necesitas tiempo para reconstruirlas, el código de neutralización es lo más recomendable. Lo activas, los shortcodes dejan de mostrarse en pantalla y puedes reconstruir las páginas a tu ritmo sin que los visitantes vean el caos.
WP Bakery Page Builder (Visual Composer)
El otro gran generador de shortcodes masivos. WP Bakery usa prefijos como vc_row, vc_column, vc_column_text, vc_section y muchos más. El impacto es similar a Divi, o sea, páginas construidas íntegramente con WP Bakery pueden tener cientos de shortcodes anidados.
Para localizar el alcance del problema:
SELECT ID, post_title, post_type
FROM wp_posts
WHERE post_content LIKE '%[vc_%'
AND post_status NOT IN ('auto-draft', 'trash')
ORDER BY post_type, ID;
Para limpiar con WP-CLI:
-- Elimina todas las etiquetas de WP Bakery (apertura y cierre) wp search-replace '\[/?vc_[a-z_]+[^\]]*\]' '' wp_posts --regex --dry-run
Un detalle importante de WP Bakery es que también puede dejar rastro en el metadato _wpb_vc_js_status y en algunas opciones propias. Después de limpiar wp_posts, vale la pena hacer una búsqueda en wp_postmeta:
SELECT post_id, meta_key FROM wp_postmeta WHERE meta_key LIKE '_wpb_%' OR meta_key LIKE '_vc_%';
Beaver Builder
Beaver Builder gestiona el contenido de forma diferente a Divi y WP Bakery, lo que hace que el impacto de dejarlo sea menor en cuanto a shortcodes visibles. En lugar de guardar todo como shortcodes en post_content, guarda la configuración de los módulos en metadatos (wp_postmeta) y el shortcode que aparece en post_content suele ser simplemente [fl_builder_insert_layout] o similar.
Esto significa que si dejas de usar Beaver Builder, el impacto visible en el contenido es mucho menor. Pero sigue quedando basura en la base de datos en forma de metadatos. Para limpiarla tienes esta consulta SQL:
-- Encuentra los metadatos de Beaver Builder SELECT post_id, meta_key FROM wp_postmeta WHERE meta_key LIKE '_fl_builder%' ORDER BY post_id; -- Si quieres eliminarlos (con cuidado) DELETE FROM wp_postmeta WHERE meta_key LIKE '_fl_builder%';
Y para el shortcode que puede quedar en el contenido, con WP-CLI:
wp search-replace '\[fl_builder_insert_layout[^\]]*\]' '' wp_posts --regex --dry-run
Elementor y sus extensiones
Elementor en sí mismo no usa shortcodes para almacenar el contenido, guarda todo en JSON en wp_postmeta con la clave _elementor_data. Esto es una ventaja importante en términos de portabilidad respecto a maquetadores más antiguos.
Sin embargo, muchas extensiones de Elementor (packs de widgets de terceros como Essential Addons, Ultimate Addons, JetElements, etc.) sí generan shortcodes que se insertan dentro de los widgets de texto de Elementor o en otras partes del contenido. Si usaste alguna de estas extensiones y lo desinstalaste esos shortcodes pueden aparecer dentro del contenido JSON de Elementor o en los widgets de texto de la barra lateral.
Para buscarlos, la consulta en wp_postmeta con el shortcode concreto es el punto de partida:
SELECT pm.post_id, p.post_title, LEFT(pm.meta_value, 300) AS fragmento FROM wp_postmeta pm JOIN wp_posts p ON p.ID = pm.post_id WHERE pm.meta_key = '_elementor_data' AND pm.meta_value LIKE '%nombre-del-shortcode%';
Revolution Slider, Meta Slider y similares
Revolution Slider es uno de los más habituales. Su shortcode tiene la forma [rev_slider alias="nombre-del-slider"]. Si cambias de plugin de sliders sin limpiar primero, ese shortcode queda en todas las páginas donde tenías un slider.
Para localizarlos:
SELECT ID, post_title FROM wp_posts
WHERE post_content LIKE '%[rev_slider%'
AND post_status NOT IN ('auto-draft', 'trash');
Y para limpiarlos con WP-CLI:
wp search-replace '\[rev_slider[^\]]*\]' '' wp_posts --regex --dry-run
Meta Slider ([metaslider id="X"]), Smart Slider 3 ([smartslider3 slider="X"]) y el resto siguen el mismo patrón.
Plugins de membresía, cursos y restricción de contenido
MemberPress, Restrict Content Pro, s2Member, LearnDash, Sensei y similares usan shortcodes para proteger fragmentos de contenido o mostrar elementos propios. Son especialmente problemáticos cuando quedan huérfanos porque pueden ocultar partes del contenido que deberían ser visibles o mostrar mensajes de acceso denegado sin sentido. Hay que localizarlos y limpiarlos con especial cuidado porque el contenido interior puede ser relevante.
En estos casos el método que conserva el contenido interior (eliminar solo las etiquetas del shortcode, no lo que hay dentro) suele ser el más apropiado.
Eliminar shortcodes de los resultados de la API REST
Si tu WordPress alimenta una aplicación externa, un marketplace, una app móvil o cualquier integración que consuma datos vía API REST de WordPress, los shortcodes huérfanos son un problema específicamente feo: en lugar de procesar el shortcode, la API devuelve el texto tal cual, y el sistema receptor lo muestra literalmente al usuario final.
Este problema es muy habitual cuando se conecta una tienda WooCommerce con marketplaces como Amazon, Miravia u otros, o cuando se construye una app sobre un WordPress que tiene contenido generado con maquetadores. El plugin del marketplace usa la API REST para leer las descripciones de los productos, y si esas descripciones están llenas de shortcodes de Divi o de WP Bakery, eso es exactamente lo que recibe y muestra.
La solución es filtrar la salida de la API para procesar o eliminar los shortcodes antes de que se envíen en la respuesta. Para el contenido general de entradas y páginas mediante la API REST estándar de WordPress:
// Elimina shortcodes no registrados del contenido que devuelve la API REST de entradas.
add_filter( 'rest_prepare_post', 'ayudawp_clean_shortcodes_rest_api', 10, 3 );
function ayudawp_clean_shortcodes_rest_api( $response, $post, $request ) {
if ( isset( $response->data['content']['raw'] ) ) {
// Procesamos el raw: eliminamos shortcodes no registrados y procesamos los registrados
$content = strip_shortcodes( $response->data['content']['raw'] );
$response->data['content']['rendered'] = apply_filters( 'the_content', $content );
}
return $response;
}
Para páginas, el hook es rest_prepare_page, para tipos de contenido personalizados, rest_prepare_{post_type}.
Para el caso específico de productos de WooCommerce en su API REST:
// Elimina shortcodes de la descripción de productos en la API REST de WooCommerce.
add_filter( 'woocommerce_rest_prepare_product_object', 'ayudawp_clean_shortcodes_woo_rest', 10, 3 );
function ayudawp_clean_shortcodes_woo_rest( $response, $product, $request ) {
$post_object = get_post( $product->get_id() );
if ( ! $post_object ) {
return $response;
}
// Elimina todos los shortcodes del contenido (descripción larga)
$content = preg_replace( '/\[[^\]]+\]/', '', $post_object->post_content );
$response->data['description'] = apply_filters( 'the_content', $content );
// Hace lo mismo con la descripción corta si la hay
if ( ! empty( $response->data['short_description'] ) ) {
$short = preg_replace( '/\[[^\]]+\]/', '', $product->get_short_description() );
$response->data['short_description'] = apply_filters( 'the_content', $short );
}
return $response;
}
Si en lugar de eliminar los shortcodes quieres que se procesen (para que la API devuelva el HTML resultante en lugar del shortcode en crudo), sustituye el preg_replace que elimina shortcodes por do_shortcode. Tiene sentido cuando el plugin que genera el shortcode sigue activo pero la API no lo procesa por defecto.
Eliminar shortcodes del feed RSS
El feed RSS de WordPress es otro lugar donde los shortcodes huérfanos causan problemas. Los lectores de feeds y los agregadores de contenido muestran el shortcode en texto plano, lo que da muy mala imagen.
Y si tienes configurada alguna integración de email marketing que consume el feed automáticamente para enviar newsletters (algo habitual en plataformas como Mailchimp, MailerLite o ConvertKit con la opción RSS-to-email), ese shortcode en crudo llega directamente a la bandeja de entrada de tus suscriptores.
La solución es limpiar el contenido antes de que se vuelque en el feed. El código actúa sobre dos filtros: the_content_feed para el contenido completo de las entradas en el feed, y the_excerpt_rss para los extractos:
// Elimina shortcodes del contenido que aparece en el feed RSS.
// Afecta solo al feed, no al contenido visible en la web.
add_filter( 'the_content_feed', 'ayudawp_strip_shortcodes_rss' );
add_filter( 'the_excerpt_rss', 'ayudawp_strip_shortcodes_rss' );
function ayudawp_strip_shortcodes_rss( $content ) {
// strip_shortcodes() elimina todos los shortcodes no registrados
// do_shortcode() procesa los que sí están registrados antes de eliminar los huérfanos
return strip_shortcodes( do_shortcode( $content ) );
}
El orden importa aquí: primero se procesan con do_shortcode los que tienen un plugin activo que los gestiona (convirtiendo esos shortcodes en HTML correcto), y luego strip_shortcodes elimina los que han quedado sin procesar porque no tienen plugin que los gestione.
Si prefieres eliminar absolutamente todos los shortcodes del feed, incluyendo los que sí están registrados, simplifica el snippet a solo return strip_shortcodes( $content );.
Cómo no volver a estar en esta situación
Arreglar el problema está bien. No volver a tenerlo está mucho mejor. Estas son las costumbres que marcan la diferencia a largo plazo.
- Limpia antes de desinstalar: Cuando vayas a eliminar un plugin que usa shortcodes, búscalos primero (con cualquiera de los métodos de localización que hemos visto) y reemplázalos por el contenido real o elimínalos antes de desactivar el plugin. Una vez desactivado, pierdes la referencia de qué shortcodes usaba, así que es mucho más difícil localizarlos.
- Instala el plugin sustituto antes de desinstalar el anterior: Si cambias de plugin de formularios, de sliders o de cualquier otra herramienta que use shortcodes, activa el nuevo plugin antes de desactivar el anterior. Así tienes tiempo de migrar el contenido con ambos activos y puedes verificar que todo funciona antes de hacer la transición definitiva.
- Documenta qué shortcodes usa cada plugin activo: En webs con mucho contenido o en webs de clientes, tener un registro de qué plugin gestiona cada shortcode que aparece en el contenido puede ahorrarte horas de investigación en el futuro. No hace falta nada sofisticado: una nota en el bloc o un documento compartido con el cliente es suficiente.
- Haz auditorías periódicas: Una vez al año, o cada vez que hagas una ronda de limpieza de plugins, ejecuta las consultas SQL de localización para ver si hay shortcodes sin procesar en el contenido. Con la práctica tarda cinco minutos y puede evitar problemas mayores.
- Prioriza herramientas que no creen dependencia de shortcodes: El ecosistema WordPress ha evolucionado mucho en este sentido. Gutenberg y los maquetadores modernos guardan el contenido en formatos más portátiles. Elementor guarda en JSON, Gutenberg en HTML con atributos de bloque, Divi 5 también ha abandonado los shortcodes. Si vas a empezar un proyecto nuevo o a rediseñar uno existente, ten en cuenta la portabilidad del contenido: qué pasa con lo que has construido si dentro de tres años cambias de herramienta.
- Para formularios, usa bloques Gutenberg cuando puedas: Plugins modernos como WPForms, Gravity Forms o Fluent Forms ofrecen tanto shortcode como bloque de Gutenberg. Si usas el bloque de Gutenberg para insertar el formulario, el contenido es mucho más portable y no te deja shortcodes huérfanos si cambias de plugin: simplemente el bloque dejará de procesarse.
¿Qué método deberías usar según el caso?
Con tantas opciones, puede ser útil tener claro cuándo usar cada una. La elección depende del volumen de shortcodes afectados, de si quieres eliminarlos de la base de datos o solo evitar que se muestren, y de tu nivel de comodidad técnica.
- Si tienes pocos shortcodes en pocas páginas, edítalas a mano. Es lo más limpio, tienes control total y no hay riesgo de romper nada.
- Si tienes muchos shortcodes y necesitas una solución inmediata sin tocar la base de datos, el códigio de neutralización con
add_shortcodedevolviendo vacío es tu primer paso. Los shortcodes desaparecen de pantalla en segundos. Después planifica la limpieza definitiva. - Si los shortcodes son todos exactamente iguales (mismo texto en todos los usos), Better Search Replace o un
UPDATEdirecto de SQL lo resuelve en minutos. - Si los shortcodes tienen atributos variables (IDs diferentes en cada uso, como es el caso de todos los formularios y sliders), necesitas expresiones regulares. WP-CLI con
--regexoREGEXP_REPLACEde MySQL son las herramientas adecuadas. - Si los shortcodes tienen etiqueta de cierre y envuelven contenido que quieres conservar, el script PHP de limpieza masiva (método 6) es el más apropiado: elimina las etiquetas pero mantiene el texto interior.
- Si el problema está en la API REST o en el feed RSS, los filtros específicos de esos apartados resuelven el problema de forma aislada sin tocar el contenido de la base de datos ni afectar a la web visible.
- Si el origen del problema es Divi 4.x la migración a Divi 5 es la solución que resuelve el problema de los shortcodes de raíz y de paso te da acceso a un maquetador más moderno y más rápido.
Si aún te quedan dudas me preguntas en los comentarios.
¿Te gustó este artículo? ¡Ni te imaginas lo que te estás perdiendo en YouTube!









He migrado algunas webs de Divi a Elementor y a Bricks builder y el método que mejor me ha funcionado ha sido cambiar el theme Divi por el plug-in Divi e instalar el nuevo constructor ya que se pueden tener varios constructores en una misma web (no en una misma página) y luego ir cambiando página a página sin prisas.
Pues ese es también muy buen apaño 🙂