Pasar un sitio WordPress del entorno de desarrollo al dominio de producción, cambiar el nombre de la marca o instalar un certificado SSL (pasar de HTTP a HTTPS) son tareas rutinarias de cualquier administrador o desarrollador. A primera vista la operación parece trivial: exportar la base de datos a un archivo .sql, abrirlo en un editor de código, usar “Buscar y reemplazar” y volver a importarlo. O lanzar una consulta SQL rápida en phpMyAdmin.
Hacer eso sobre la base de datos de producción de WordPress termina con el sitio roto al instante. Desaparecen los widgets del pie y de las barras laterales, los ajustes de colores y tipografía del tema vuelven a sus valores por defecto, los campos avanzados (ACF) devuelven valores vacíos y la configuración de los plugins de comercio electrónico se pierde.
La causa está en la forma en que WordPress y sus plugins guardan estructuras de datos complejas en una base relacional MySQL o MariaDB. La documentación de WordPress lo advierte sin rodeos: una búsqueda y sustitución simple sobre toda la base de datos puede dañar la serialización, porque algunos temas y widgets guardan la URL junto con su longitud (Migrating WordPress, developer.wordpress.org). Entender la serialización de PHP y dominar las herramientas adecuadas, sobre todo la interfaz de línea de comandos WP-CLI, es la base para gestionar sitios WordPress con seguridad.
Qué son los datos serializados en la base de datos de WordPress
Las bases de datos SQL relacionales están pensadas para guardar datos escalares: números, cadenas cortas o fechas. WordPress, sin embargo, necesita a menudo guardar en una sola fila una estructura compleja y multidimensional, por ejemplo un array con la lista de widgets activos, su orden, los iconos asignados y sus parámetros.
En lugar de crear decenas de tablas normalizadas adicionales para cada plugin, los creadores de WordPress optaron por una función nativa de PHP: serialize().
// Array de configuración de un tema en PHP
$theme_settings = array(
'site_name' => 'Mi Tienda WordPress',
'logo_url' => 'https://dominio-antiguo.es/logo.png',
'contact_mail' => '[email protected]'
);
// Resultado de serialize($theme_settings)
// Guardado tal cual en la columna option_value de la tabla wp_options:
a:3:{s:9:"site_name";s:19:"Mi Tienda WordPress";s:8:"logo_url";s:35:"https://dominio-antiguo.es/logo.png";s:12:"contact_mail";s:26:"[email protected]";}Cómo leer una cadena serializada
Fijémonos en el fragmento que representa la URL del logotipo:
s:35:"https://dominio-antiguo.es/logo.png";Cada elemento del formato serializado de PHP tiene una estructura estricta:
sindica el tipo de dato: string (cadena de caracteres).35es la longitud declarada de la cadena (el número exacto de bytes)."https://dominio-antiguo.es/logo.png"es el valor, entre comillas.
La pieza clave es el número 35. Al leer los datos de la base, PHP llama a la función unserialize(). El analizador de PHP no busca la comilla de cierre a ciegas, sino que cuenta exactamente 35 bytes desde el punto indicado.
Por qué REPLACE de SQL rompe los datos serializados
Ejemplo: el sitio se migra a un dominio nuevo y más largo, https://dominio-nuevo-bastante-mas-largo-empresa.es. La nueva URL del logotipo es https://dominio-nuevo-bastante-mas-largo-empresa.es/logo.png, con 60 caracteres.
Si ejecutas la consulta SQL tradicional:
-- ERROR CRÍTICO: esta consulta destruye la serialización
UPDATE wp_options
SET option_value = REPLACE(option_value, 'https://dominio-antiguo.es', 'https://dominio-nuevo-bastante-mas-largo-empresa.es');En la base de datos quedará esta cadena:
s:35:"https://dominio-nuevo-bastante-mas-largo-empresa.es/logo.png";Qué ocurre cuando un visitante abre la página:
- WordPress lee la fila de la base de datos y la pasa a
unserialize(). - La función lee la cabecera
s:35y espera exactamente 35 caracteres. - PHP corta los primeros 35 caracteres de la nueva URL y, en lugar de la comilla de cierre y el punto y coma obligatorios, encuentra letras sueltas dentro de la cadena.
- El analizador de PHP lanza el aviso
PHP Notice: unserialize(): Error at offset...y devuelvefalse. - El tema o el plugin recibe
falseen lugar del array de opciones que esperaba. - WordPress da por hecho que los datos no existen y restablece los ajustes de fábrica del tema o elimina los widgets.
Dónde guarda WordPress las URLs en la base de datos
Las URLs de una instalación de WordPress no están en un solo sitio. Están repartidas entre varias tablas del sistema y las tablas que crean los plugins:
graph TD
A["Base de datos de WordPress"] --> B["wp_options"]
A --> C["wp_posts"]
A --> D["wp_postmeta"]
A --> E["wp_comments / wp_commentmeta"]
A --> F["wp_usermeta"]
A --> G["Tablas de plugins (WooCommerce, formularios, SEO)"]
B --> B1["siteurl y home (direcciones principales)"]
B --> B2["sidebars_widgets (serializado)"]
B --> B3["theme_mods (serializado)"]
C --> C1["post_content (enlaces, imágenes)"]
C --> C2["post_excerpt"]
C --> C3["guid (NO cambiar en entradas publicadas)"]
D --> D1["_elementor_data (JSON escapado)"]
D --> D2["_wp_attached_file (rutas de medios)"]
D --> D3["Campos ACF (serializados)"]1. La tabla wp_options
Es la tabla de configuración más importante. Contiene dos registros críticos para que el sitio funcione:
siteurl: indica dónde están físicamente los archivos de WordPress (la ruta del núcleo).home: indica la dirección principal del sitio que los usuarios escriben en el navegador.
Además guarda en forma serializada:
sidebars_widgets: la disposición de los widgets en barras laterales y pies de página.theme_mods_[nombre_del_tema]: colores, cabecera, tipografía y logotipo del personalizador (Customizer).- Los ajustes de plugins de SEO (Rank Math, Yoast), de seguridad y de caché.
2. La tabla wp_posts
Guarda el contenido de entradas, páginas, productos de WooCommerce y elementos de la biblioteca de medios.
- La columna
post_content: contiene enlaces absolutos a otras páginas, etiquetas<img>incrustadas, enlaces a archivos PDF y los comentarios de bloque de Gutenberg. - La columna
guid(Globally Unique Identifier): el identificador único de la entrada. Regla importante: la documentación de WordPress dice: “Nunca, jamás, cambie el contenido de la columna GUID, bajo ninguna circunstancia” (Migrating WordPress, developer.wordpress.org, traducción propia). La columnaguidno debe cambiarse porque los lectores de feeds RSS la usan para detectar artículos nuevos. Si cambias elguid, los lectores tratarán todas las entradas antiguas como nuevas y volverán a avisar a los suscriptores.
3. La tabla wp_postmeta
Contiene atributos adicionales de entradas y páginas. Aquí van los arrays serializados de los campos de Advanced Custom Fields (ACF), los metadatos de los adjuntos (_wp_attachment_metadata) y la configuración de constructores de páginas como Elementor o Divi.
4. Las tablas wp_usermeta y wp_comments
Contienen las URLs de las webs de los autores de comentarios y los metadatos de los perfiles de usuario.
Cambiar el dominio de WordPress con WP-CLI search-replace
La interfaz de línea de comandos de WordPress (WP-CLI) es la herramienta más rápida, fiable y automatizable para migrar URLs. Trabaja directamente en el servidor a través de SSH y opera sobre las estructuras de PHP en memoria.
Paso 1: copia de seguridad
Antes de tocar la base de datos de producción, vuélcala a un archivo SQL:
# Volcado de la base de datos con sentencias DROP TABLE
wp db export copia-antes-de-migrar-$(date +%Y%m%d_%H%M%S).sql --add-drop-tableSi la base de datos se daña, recuperarás el estado original en segundos:
wp db import copia-antes-de-migrar-*.sqlPaso 2: prueba en seco con —dry-run
La opción --dry-run muestra cuántas filas y en qué tablas se modificarían, sin cambiar nada en la base de datos:
wp search-replace 'https://dominio-antiguo.es' 'https://dominio-nuevo.es' --all-tables --dry-runEl resultado es una tabla con las columnas Table, Column, Replacements y Type, más una línea de resumen con el total de sustituciones. La columna Type indica si esa columna se procesó en SQL o en PHP. Si el número de sustituciones es cero, lo más habitual es que la URL antigua no coincida con la guardada en la base de datos por el protocolo, por el prefijo www o por una barra final.
Paso 3: sustitución real en producción
Para la sustitución definitiva recomendamos este conjunto de opciones:
wp search-replace 'https://dominio-antiguo.es' 'https://dominio-nuevo.es' \
--all-tables \
--precise \
--recurse-objects \
--skip-columns=guid \
--report-changed-onlyQué hace cada opción:
--all-tables: recorre todas las tablas de la base de datos, incluidas las tablas propias de plugins de comercio electrónico, formularios y registros.--precise: fuerza el uso de PHP en lugar de SQL en todas las columnas. La documentación de WP-CLI lo describe como más lento pero más fiable con datos serializados complejos.--recurse-objects: entra de forma recursiva en los objetos serializados. Está activada por defecto y se desactiva con--no-recurse-objects, así que escribirla solo deja clara la intención.--skip-columns=guid: omite la columnaguiddewp_postsy evita que los lectores RSS dupliquen entradas.--report-changed-only: muestra en el resumen solo las tablas en las que hubo cambios y oculta cientos de filas vacías.
Si necesitas una búsqueda sin distinguir mayúsculas, --regex por sí sola no basta: hay que añadir --regex-flags='i', y la documentación advierte que el modo regex es mucho más lento.
Cambiar el dominio en una red WordPress Multisite
En las instalaciones Multisite (una red de varios sitios) las tablas tienen prefijos propios (por ejemplo wp_2_posts, wp_3_options) y los dominios de cada subsitio se guardan en las tablas wp_blogs y wp_site.
En ese entorno se usa la opción --network, que abarca todas las tablas registradas en $wpdb para toda la red, o --all-tables-with-prefix, que añade las tablas con el prefijo que no están registradas en $wpdb:
wp search-replace 'https://red-antigua.es' 'https://red-nueva.es' --network --all-tables-with-prefixLa documentación de WordPress reconoce que, al cambiar el dominio de una red Multisite, este paso “todavía no se puede automatizar fácilmente” (Migrating WordPress, developer.wordpress.org, traducción propia). Después de la sustitución revisa a mano:
- las tablas
wp_siteywp_blogs, donde están el dominio y la ruta de la red y de cada subsitio, - las opciones
home,siteurlyfileupload_urlen la tabla de opciones de cada sitio (wp_options,wp_2_optionsy siguientes), - al pasar de subdominios a subdirectorios o al revés: el archivo
.htaccessy la constanteSUBDOMAIN_INSTALLdewp-config.php.
Cómo exportar la base de datos con las nuevas URLs a un archivo SQL
Si preparas una migración de base de datos para un cliente en tu entorno local y quieres generar un archivo SQL con los dominios ya sustituidos sin tocar tu base de datos de trabajo:
wp search-replace 'https://localhost/proyecto' 'https://dominio-cliente.es' \
--all-tables \
--export=base-lista-para-importar.sqlPasar de www al dominio sin www
Cuando solo cambia el prefijo, sustituye la dirección completa con su protocolo, para no tocar cadenas ajenas que contengan el mismo nombre:
wp search-replace 'https://www.sudominio.es' 'https://sudominio.es' --all-tables --precise --skip-columns=guid --dry-runCambiar las URLs con el plugin Better Search Replace
Si gestionas el sitio en un hosting compartido sin acceso SSH o prefieres trabajar desde el escritorio de WordPress, la opción más segura es el plugin Better Search Replace, mantenido por el equipo de WP Engine (antes Delicious Brains).
Paso a paso en el escritorio de WordPress:
1. Vaya a Plugins > Añadir plugin y busque "Better Search Replace".
2. Instale y active el plugin.
3. Vaya a Herramientas > Better Search Replace.
4. En el campo "Search for" escriba la URL antigua (por ejemplo https://dominio-antiguo.es).
5. En el campo "Replace with" escriba la URL nueva (por ejemplo https://dominio-nuevo.es).
6. Seleccione todas las tablas de la lista (Ctrl+A en Windows o Cmd+A en macOS).
7. Compruebe que la opción "Run as dry run?" está MARCADA.
8. Pulse "Run Search/Replace" y analice el informe.
9. Desmarque "Run as dry run?" y repita el proceso para guardar los cambios.Errores de Better Search Replace y cómo solucionarlos
- Error 504 Gateway Timeout o límite de tiempo de ejecución superado (
max_execution_time): Con bases de datos grandes el servidor web puede cortar la petición HTTP. En la pestaña Settings del plugin reduce el parámetro Max Page Size. El valor por defecto es de 20000 filas por lote, como se ve en el código fuente del plugin (class-bsr-admin.php); un lote más pequeño significa más peticiones, pero más cortas.
Cómo cambiar siteurl y home en WordPress
Dos opciones de la tabla wp_options deciden si podrás siquiera iniciar sesión en el sitio con la nueva dirección. La documentación de WordPress (Changing The Site URL) las distingue así: home es la “Dirección del sitio (URL)”, la dirección que los visitantes escriben en el navegador, y siteurl es la “Dirección de WordPress (URL)”, el lugar donde están los archivos del núcleo. Ambos campos de Ajustes > Generales deben incluir https:// y no deben terminar en barra.
La documentación describe varias formas de cambiar estos valores. Cada una sirve para una situación distinta:
| Método | Cuándo usarlo | Precauciones |
|---|---|---|
wp option update en WP-CLI | Tienes SSH y el escritorio no funciona | Solo cambia dos opciones, el resto de URLs las corrige wp search-replace |
Constantes WP_HOME y WP_SITEURL en wp-config.php | Hay que levantar el sitio rápido | Los valores quedan fijados en el código y los campos de Ajustes > Generales no se pueden editar |
update_option() en el functions.php del tema | Solo tienes acceso FTP | Hay que borrar el código en cuanto cargue el escritorio |
Constante RELOCATE | El sitio se ha movido a otro directorio u otro servidor | Solo corrige las direcciones del panel y dejarla en el archivo es inseguro |
Edición de wp_options en phpMyAdmin | Solo tienes el panel del hosting | Primero una copia de la base de datos; el prefijo puede no ser wp_ |
Cambiar siteurl y home con WP-CLI
La documentación propone dos comandos y, después, la sustitución de URLs en el contenido sin tocar la columna guid:
wp option update home 'https://dominio-nuevo.es'
wp option update siteurl 'https://dominio-nuevo.es'
wp search-replace 'https://dominio-antiguo.es' 'https://dominio-nuevo.es' --skip-columns=guidCambiar la dirección desde functions.php
Si solo tienes acceso por FTP, la documentación indica añadir al archivo functions.php del tema activo, justo después de la etiqueta de apertura <?php, dos líneas:
update_option( 'siteurl', 'https://dominio-nuevo.es' );
update_option( 'home', 'https://dominio-nuevo.es' );Tras subir el archivo, abre varias veces la pantalla de acceso o el escritorio y después borra esas líneas. La documentación es tajante: “No deje este código en el archivo functions.php” (Migrating WordPress, developer.wordpress.org, traducción propia). El código se ejecutaría en cada petición y sobrescribiría las opciones que más adelante quieras cambiar desde el escritorio.
La constante RELOCATE tras mover el directorio
La constante define( 'RELOCATE', true );, añadida en wp-config.php antes del comentario That's all, stop editing!, hace que WordPress adopte la dirección actual cuando inicias sesión mediante wp-login.php en el nuevo servidor. Según la documentación solo corrige las direcciones del panel, no el resto de la configuración, y dejarla puesta es inseguro: “Elimine siempre la línea RELOCATE de wp-config.php cuando termine” (Migrating WordPress, developer.wordpress.org, traducción propia).
Transients en la base de datos migrada
La documentación llama la atención sobre los transients, valores temporales guardados en wp_options. Pueden contener URLs antiguas y, como WordPress los vuelve a generar por sí mismo, se pueden borrar sin riesgo en la copia migrada de la base de datos. Con WP-CLI lo hace wp transient delete --all, y la documentación da también una consulta para phpMyAdmin, siempre con una copia previa:
DELETE FROM `wp_options` WHERE `option_name` LIKE '%\_transient\_%';Cómo cambiar las URLs cuando el escritorio de WordPress no funciona
En una emergencia, sin acceso SSH y con el escritorio de WordPress caído por un bucle de redirecciones o por archivos dañados, la herramienta probada desde hace más de una década sigue siendo el script Search-Replace-DB (conocido como SRDB) de la agencia Interconnect/it.
Cómo usar el script SRDB:
1. Descargue el archivo zip oficial desde interconnectit.com.
2. Descomprima los archivos y cambie el nombre de la carpeta por una cadena única y difícil de adivinar (por ejemplo srdb-sec-8f7a9d/).
3. Suba la carpeta al servidor por FTP o SFTP al directorio principal de WordPress (donde está wp-config.php).
4. Abra en el navegador https://su-dominio.es/srdb-sec-8f7a9d/.
5. El script lee automáticamente los datos de acceso a la base de datos de wp-config.php.
6. Rellene los campos "replace" (valor antiguo) y "with" (valor nuevo).
7. Pulse "dry run" y, tras comprobarlo, "live run".
8. CRÍTICO: al terminar, pulse el botón rojo "delete me" para que el script se borre del servidor.[!CAUTION] Dejar el script
Search-Replace-DBen un servidor en funcionamiento es un agujero de seguridad crítico. Cualquiera que adivine la ruta del script obtiene acceso completo de lectura y escritura a la base de datos sin necesidad de contraseña. Compruebe siempre que la carpeta se ha borrado al terminar.
Cambiar las URLs en Elementor, Divi y Gutenberg
Los constructores de páginas y el editor de bloques Gutenberg añaden capas extra de codificación de datos que hay que tener en cuenta al actualizar las URLs.
graph LR
subgraph WP_CLI_Execution["Paso 1: base de datos"]
A["wp search-replace"] --> B["Tablas wp_posts, wp_postmeta, wp_options"]
end
subgraph Builder_Sync["Paso 2: constructores de páginas"]
B --> C["Elementor: Tools > Replace URL"]
B --> D["Elementor: regenerar archivos CSS"]
end
subgraph Edge_Purge["Paso 3: caché y borde"]
C & D --> F["wp cache flush (Redis/Memcached)"]
F --> G["Purgar la CDN (Cloudflare)"]
end1. URLs en los datos JSON de Elementor
Elementor guarda el diseño de las páginas en el campo meta _elementor_data en formato JSON, donde las barras de las URLs suelen ir precedidas de un carácter de escape (escaped slashes):
{\"url\":\"https:\/\/dominio-antiguo.es\/wp-content\/uploads\/2026\/08\/banner.jpg\"}Una búsqueda normal de https://dominio-antiguo.es puede no coincidir con la cadena escapada https:\/\/dominio-antiguo.es. Además, Elementor compila los estilos en archivos CSS estáticos dentro de wp-content/uploads/elementor/css/.
Procedimiento correcto tras migrar un sitio hecho con Elementor:
- Ejecuta el
wp search-replacehabitual en la base de datos. - Entra en el escritorio de WordPress y ve a Elementor > Tools > Replace URL.
- Escribe la URL antigua y la nueva y pulsa Replace URL.
- En la pestaña General pulsa el botón Regenerate Files & Data.
2. URLs en los bloques de Gutenberg
Los bloques de Gutenberg guardan los atributos de los medios directamente en comentarios HTML dentro de post_content:
<!-- wp:image {"id":412,"sizeSlug":"full","linkDestination":"custom"} -->
<figure class="wp-block-image size-full"><a href="https://dominio-antiguo.es/oferta/"><img src="https://dominio-antiguo.es/wp-content/uploads/2026/08/imagen.png" alt="Oferta"/></a></figure>
<!-- /wp:image -->Tanto wp search-replace como Better Search Replace lo resuelven sin problema, siempre que recorran la columna post_content de la tabla wp_posts.
3. URLs escritas directamente en los archivos del tema
Algunos temas y plugins llevan direcciones escritas directamente en archivos PHP, CSS o JavaScript. Ninguna sustitución en la base de datos las alcanza, pero es fácil encontrarlas en el servidor:
grep -rn "dominio-antiguo.es" wp-content/themes/su-tema/Cambiar el dominio de una tienda WooCommerce
Al cambiar el dominio de una tienda basada en WooCommerce hay que prestar especial atención a los módulos que se comunican con pasarelas de pago, transportistas y sistemas ERP:
- Pasarelas de pago:
- La mayoría de las pasarelas registran en su panel de desarrollador las direcciones de notificación de vuelta (webhooks o IPN). Si cambias el dominio, las notificaciones de pedidos pagados seguirán llegando a la dirección antigua y provocarán errores en las transacciones.
- Después de
wp search-replace, entra en el panel de cada proveedor de pagos y actualiza la dirección del webhook para que apunte al dominio nuevo.
- Claves y webhooks de la API REST de WooCommerce:
- En WooCommerce > Settings > Advanced > Webhooks comprueba que las direcciones de destino (Delivery URL) se han actualizado. Si los webhooks sincronizan el stock con un ERP, actualiza también las direcciones de los endpoints en el software de integración.
- Sesiones y carritos de los clientes:
- La tabla
wp_woocommerce_sessionsguarda datos temporales de los carritos. Tras el cambio de dominio conviene vaciar las sesiones en WooCommerce > Status > Tools > Clear customer sessions para evitar errores de tokens de sesión al cambiar las cookies del dominio.
- La tabla
Search-replace en una base de datos grande y en clústeres MySQL
Con bases de datos muy grandes, una sola pasada de wp search-replace por todas las tablas tarda mucho y durante todo ese tiempo escribe en tablas que el sitio en funcionamiento está usando. La documentación de WP-CLI menciona dos limitaciones que conviene conocer: las tablas sin clave primaria se omiten, y las opciones --regex y --log ralentizan el trabajo de forma notable.
1. Cómo dividir search-replace en partes más pequeñas
El comando wp search-replace no tiene opciones --batch-size ni --limit; la lista de opciones de la documentación de WP-CLI no las incluye y las instrucciones que las mencionan no funcionarán. El trabajo se divide de otra manera: indicando los nombres de las tablas como argumentos (se admiten comodines) o excluyendo tablas con --skip-tables:
# Primero el contenido, después los metadatos, en pasadas separadas
wp search-replace 'https://antiguo.es' 'https://nuevo.es' wp_posts --precise --skip-columns=guid
wp search-replace 'https://antiguo.es' 'https://nuevo.es' wp_postmeta --precise
# Todo salvo una tabla grande de registros de un plugin
wp search-replace 'https://antiguo.es' 'https://nuevo.es' --all-tables --skip-tables='wp_*_log' --precise --skip-columns=guidLa opción --include-columns limita la sustitución a las columnas indicadas, y con --export el número de filas por sentencia INSERT lo fija --export_insert_size (50 por defecto).
2. Search-replace con replicación (Amazon Aurora, MySQL Galera)
Si la base de datos funciona con una arquitectura maestro-réplica o en un clúster Galera, modificar millones de registros de golpe genera un gran volumen de datos en el registro binario (binlog). Eso puede provocar un retraso de replicación considerable en los nodos de lectura.
Estrategia recomendada para entornos de alta disponibilidad:
- Hacer la operación en una ventana de mantenimiento con el menor tráfico posible.
- Desconectar temporalmente los nodos de réplica mientras dura la escritura masiva.
- Usar la opción
--exportpara preparar el archivo SQL procesado en una máquina de trabajo aparte e importarlo durante la ventana de mantenimiento.
3. Modo de mantenimiento durante la migración
WP-CLI puede activar el modo de mantenimiento de WordPress, de modo que los visitantes no vean el sitio a medio pasar a la nueva dirección:
wp maintenance-mode activate
wp search-replace 'https://antiguo.es' 'https://nuevo.es' --all-tables --precise --skip-columns=guid
wp maintenance-mode deactivatePasar de HTTP a HTTPS y configurar el servidor web
Si el objetivo de la migración no es cambiar el dominio sino instalar un certificado SSL y pasar del protocolo sin cifrar al cifrado:
wp search-replace 'http://sudominio.es' 'https://sudominio.es' \
--all-tables \
--precise \
--skip-columns=guidAsí eliminas el problema del Mixed Content (contenido mixto), en el que el navegador bloquea scripts, fuentes o imágenes servidos por el antiguo protocolo http://. Limita siempre la sustitución a tu propio dominio: un reemplazo de http:// a secas cambiaría también los enlaces a sitios externos.
Redirecciones 301 en Nginx y Apache
Tras cambiar el dominio en la base de datos, el paso crítico para el SEO es redirigir todo el tráfico de la dirección antigua a la nueva con el código de estado HTTP 301 (Moved Permanently).
Configuración de Nginx (/etc/nginx/sites-available/dominio-antiguo.conf):
server {
listen 80;
listen 443 ssl http2;
server_name dominio-antiguo.es www.dominio-antiguo.es;
ssl_certificate /etc/letsencrypt/live/dominio-antiguo.es/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dominio-antiguo.es/privkey.pem;
return 301 https://dominio-nuevo.es$request_uri;
}Configuración de Apache (.htaccess en el servidor antiguo):
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(?:www\.)?dominio-antiguo\.es$ [NC]
RewriteRule ^(.*)$ https://dominio-nuevo.es/$1 [L,R=301]
</IfModule>Constantes de wp-config.php frente a los valores de la base de datos
Una práctica habitual durante una avería es forzar las URLs en el archivo wp-config.php:
define('WP_HOME', 'https://dominio-nuevo.es');
define('WP_SITEURL', 'https://dominio-nuevo.es');Conviene saber cómo funcionan estas constantes:
- Las constantes definidas en
wp-config.phpsustituyen los valores desiteurlyhomeque lee WordPress, pero no cambian los registros de la base de datos. La documentación lo llama directamente fijar los valores en el código y advierte que no es necesariamente la mejor solución. - Los campos de dirección de Ajustes > Generales dejan de poder editarse.
- Los enlaces del contenido de las entradas, los adjuntos y los widgets seguirán apuntando a la dirección antigua.
- Trata las constantes de
wp-config.phpcomo una solución temporal; al final la base de datos siempre debe actualizarse conwp search-replace.
Checklist para cambiar el dominio de WordPress
Antes de empezar conviene seguir un plan ordenado:
| Fase | Tarea | Herramienta o comando |
|---|---|---|
| Antes de migrar | Volcado completo de la base de datos | wp db export backup-$(date +%s).sql --add-drop-table |
| Antes de migrar | Copia de wp-content/uploads y .htaccess | rsync / tar -czf uploads.tar.gz |
| Antes de migrar | Desactivar plugins de caché y minificación | Escritorio / wp plugin deactivate |
| Durante | Prueba en seco de la sustitución | wp search-replace 'old' 'new' --all-tables --dry-run |
| Durante | Sustitución real en la base de datos | wp search-replace 'old' 'new' --all-tables --precise --skip-columns=guid |
| Durante | Cambio de dominio en constructores (Elementor) | Escritorio > Elementor > Tools > Replace URL |
| Después de migrar | Vaciar la caché de objetos y los transients | wp cache flush y wp transient delete --all |
| Después de migrar | Regenerar los enlaces permanentes | wp rewrite flush --hard |
Script Bash para sincronizar la base de datos de WordPress entre entornos
En flujos de trabajo con contenedores Docker o entornos de desarrollo y staging (DDEV, Lando, LocalWP), escribir los comandos a mano cada vez lleva tiempo y da pie a errores. Lo habitual en los equipos técnicos es preparar un script de Bash que descargue la base de datos de producción, la importe en local y sustituya los dominios.
#!/usr/bin/env bash
# Script de sincronización automática de la base de datos: sync-db.sh
set -eo pipefail
PROD_HOST="su-servidor-de-produccion.es"
PROD_USER="deploy"
PROD_PATH="/var/www/html"
PROD_URL="https://suempresa.es"
LOCAL_URL="https://proyecto.local"
BACKUP_DIR="./backups"
mkdir -p "${BACKUP_DIR}"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
DUMP_FILE="${BACKUP_DIR}/dump_prod_${TIMESTAMP}.sql"
echo "==> 1/4 Descargando la base de datos de producción por SSH..."
ssh "${PROD_USER}@${PROD_HOST}" "cd ${PROD_PATH} && wp db export - --add-drop-table" > "${DUMP_FILE}"
echo "==> 2/4 Importando la base de datos en el entorno local..."
wp db import "${DUMP_FILE}"
echo "==> 3/4 Sustituyendo dominios respetando la serialización..."
wp search-replace "${PROD_URL}" "${LOCAL_URL}" \
--all-tables \
--precise \
--recurse-objects \
--skip-columns=guid \
--report-changed-only
echo "==> 4/4 Vaciando cachés y regenerando enlaces..."
wp cache flush
wp rewrite flush --hard
echo "==> Listo. El entorno local está sincronizado en ${LOCAL_URL}."Comprobar la serialización al terminar
Si quieres asegurarte de que ningún campo de la base de datos se ha dañado durante la migración, puedes ejecutar un breve script PHP con WP-CLI que comprueba la integridad de todos los registros serializados de la tabla wp_options:
wp eval '
global $wpdb;
$options = $wpdb->get_results("SELECT option_name, option_value FROM {$wpdb->options} WHERE option_value LIKE \"a:%\" OR option_value LIKE \"O:%\"");
$broken = 0;
foreach ($options as $opt) {
if (is_serialized($opt->option_value) && @unserialize($opt->option_value) === false) {
echo "Opción dañada: " . $opt->option_name . PHP_EOL;
$broken++;
}
}
if ($broken === 0) {
echo "Todas las opciones serializadas de wp_options son correctas." . PHP_EOL;
} else {
echo "Se han encontrado " . $broken . " registros dañados." . PHP_EOL;
}
'Errores después de cambiar el dominio de WordPress y cómo solucionarlos
1. Bucle de redirecciones tras cambiar el dominio (ERR_TOO_MANY_REDIRECTS)
Si después del cambio el sitio entra en un bucle de redirecciones infinito:
- Comprueba que
siteurlyhomeenwp_optionstienen exactamente el mismo protocolo (https://) y el mismo prefijo (con o sinwww) que los configurados en el servidor web o en Cloudflare. - Revisa la configuración SSL de Cloudflare (el modo SSL/TLS debe estar en Full (Strict), no en Flexible).
- Restablece el archivo
.htaccessa la versión por defecto de WordPress con:wp rewrite flush --hard
2. Desaparecen los estilos CSS y los iconos tras la migración
- Si la página carga como texto sin estilos, la causa más habitual son hojas de estilo y scripts que siguen apuntando a la dirección antigua, el contenido mixto (Mixed Content) al pasar a HTTPS o el bloqueo de recursos por la política CORS.
- Abre la consola de desarrollo del navegador (tecla F12, pestaña Console) para localizar los archivos que se cargan desde la antigua dirección
http://.
3. Widgets o campos de ACF dañados
- Si se ignoraron las reglas de la serialización y se ejecutó un
REPLACEde SQL a pelo, la única salida segura es restaurar la base de datos desde el volcado previo (wp db import backup.sql) y repetir la operación conwp search-replace --precise.
4. Error Allowed memory size exhausted en WP-CLI
Al ejecutar search-replace sobre tablas con enormes cantidades de texto (por ejemplo, tablas de registros o revisiones de entradas muy grandes), el proceso PHP de la terminal puede agotar el límite de memoria asignado:
- Para aumentar temporalmente la memoria de un solo comando de WP-CLI, ejecútalo con la opción de PHP correspondiente:
php -d memory_limit=1024M /usr/local/bin/wp search-replace 'https://antiguo.es' 'https://nuevo.es' --all-tables - También conviene borrar antes de la migración las revisiones innecesarias y los transients caducados:
wp transient delete --all && wp post delete $(wp post list --post_type='revision' --format=ids) --force
5. Incompatibilidad de codificación y de collation
Al mover un volcado a un servidor de base de datos más antiguo o distinto, puede ocurrir que el servidor de destino no conozca la collation usada en el volcado, por ejemplo utf8mb4_unicode_520_ci:
- Si al importar el archivo
.sqlaparece el error#1273 - Unknown collation: 'utf8mb4_unicode_520_ci', sustituye esa cadena en el archivo SQL antes de importarlo:sed -i 's/utf8mb4_unicode_520_ci/utf8mb4_unicode_ci/g' backup.sql - Ambas collations usan
utf8mb4, así que las tildes y la eñe se conservan; solo cambia la regla de ordenación y comparación de texto.
Resumen
Actualizar correctamente las URLs en la base de datos de WordPress exige entender cómo funcionan PHP y las bases de datos relacionales. Las consultas SQL a pelo o la edición del volcado en un editor de texto son el camino más corto para desestabilizar el sitio y perder la configuración.
WP-CLI con las opciones --all-tables --precise --skip-columns=guid, precedido de una copia de seguridad y de una pasada --dry-run, conserva intactos los datos serializados y la columna guid.
¿Preparas una modernización completa del sitio, un cambio a arquitectura headless o una migración a plataformas de nueva generación? Consulta nuestros servicios de desarrollo WordPress, de migración de WordPress a Astro y Next.js y la auditoría de Core Web Vitals. Antes del corte DNS en producción, reserva una evaluación de migración WordPress para encontrar huecos de redirección y URLs serializadas sin tocar live.







