La “famosa instalación en 5 minutos” es un eslogan de marketing, no un estándar profesional. Una instalación predeterminada de WordPress es “habladora”, poco optimizada y a menudo insegura.
Como desarrolladores no nos limitamos a “instalar” WordPress. Lo preparamos para funcionar en un entorno concreto. Esta guía cubre las constantes de configuración clave y las técnicas de refuerzo de seguridad que deberían formar parte de la base de cualquier proyecto de cliente en 2026.
Configurar wp-config.php después de instalar WordPress
Es el cerebro de tu instalación. Deja de tenerlo con la configuración predeterminada.
Definir WP_ENVIRONMENT_TYPE en wp-config.php
Desde WordPress 5.5, WP_ENVIRONMENT_TYPE es el estándar. Úsalo para evitar que los errores de desarrollo lleguen a producción.
// En wp-config.php
define( 'WP_ENVIRONMENT_TYPE', 'production' ); // 'local', 'development', 'staging', 'production'Después, en tu código:
if ( wp_get_environment_type() === 'production' ) {
// Activar caché, desactivar errores
}Desactivar el editor de archivos y forzar SSL en wp-config.php
Evita que los clientes (o los atacantes) rompan el sitio desde el panel de administración.
// Desactivar el editor de archivos (editor de temas/plugins)
define( 'DISALLOW_FILE_EDIT', true );
// Bloquear la instalación/actualización de plugins (útil en despliegues inmutables)
define( 'DISALLOW_FILE_MODS', true );
// Forzar SSL en la administración
define( 'FORCE_SSL_ADMIN', true );Cómo limitar las revisiones de entradas en WordPress
El asesino de la base de datos. ¿De verdad necesitas 100 versiones de la página “Sobre nosotros”?
define( 'WP_POST_REVISIONS', 10 ); // Conservar las últimas 10
// O
define( 'WP_POST_REVISIONS', false ); // Desactivar por completo (no recomendado)Cómo activar WP_DEBUG_LOG sin mostrar errores
Nunca muestres errores en el frontend. Regístralos.
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', '/tmp/wp-errors.log' ); // ¡Saca el registro del directorio público!
define( 'WP_DEBUG_DISPLAY', false );
// Registrar consultas SQL para depurar el rendimiento (¡desactívalo en producción!)
define( 'SAVEQUERIES', false );Cómo desactivar los emoji y XML-RPC en WordPress
WordPress trae funciones que el 90% de los sitios empresariales no necesita: emoji, oEmbeds y XML-RPC.
No instales un plugin para desactivarlas. Crea un plugin must-use (wp-content/mu-plugins/lean-core.php).
<?php
/* Plugin Name: Lean Core */
// 1. Desactivar los emoji (ahorra una petición HTTP)
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
// 2. Desactivar XML-RPC (seguridad)
add_filter( 'xmlrpc_enabled', '__return_false' );
// 3. Eliminar la versión de WP (seguridad por oscuridad)
remove_action( 'wp_head', 'wp_generator' );
// 4. Desactivar los feeds RSS (si construyes una web corporativa)
// function wppoland_disable_feed() {
// wp_die( 'No hay feed, ¡visita nuestra página de inicio!' );
// }
// add_action('do_feed', 'wppoland_disable_feed', 1);Cambiar las claves salt en wp-config.php
Ya conoces las claves de autenticación de wp-config.php.
define('AUTH_KEY', 'escribe aquí tu frase única');
// ...Dato: cambiarlas cierra al instante la sesión de todos los usuarios. Es la “opción nuclear” si el sitio ha sido hackeado. Consejo profesional: automatiza su rotación con un script de CLI o con Vault si gestionas sitios enterprise.
Qué configurar en wp-config.php antes de lanzar el sitio
Antes del lanzamiento:
- Establece
WP_ENVIRONMENT_TYPEen production. - Establece
DISALLOW_FILE_EDITen true. - Limita
WP_POST_REVISIONS. - Mueve
WP_DEBUG_LOGa una carpeta privada. - Desactiva los emoji/XML-RPC por código.
Una instancia de WordPress bien configurada es silenciosa, segura y rápida.
Qué enlaces permanentes configurar en WordPress
La estructura de enlaces permanentes óptima
Ajustes > Enlaces permanentes > Nombre de la entradaPor qué:
- URL más cortas y legibles
- Mejor para el SEO (palabras clave en la URL)
- Más fáciles de recordar y compartir
Configuración en wp-config.php:
// Forzar la estructura de enlaces permanentes (opcional)
define('WP_POST_REVISIONS', 10);Estructura de categorías y etiquetas
- Crea las categorías básicas (3-7 principales)
- Define la jerarquía (categorías superiores y subcategorías)
- Establece una categoría predeterminada (no “Sin categoría”)
- Planifica una estrategia de etiquetas (opcional, 5-10 etiquetas por entrada)
Ajustes de medios y tamaños de imagen en WordPress
Tamaños de imagen
Ajustes > MediosAjustes recomendados:
- Miniatura: 150x150px (marca “Recortar la miniatura a las dimensiones exactas”)
- Mediano: 300x300px
- Grande: 1024x1024px
- Tamaño completo: siempre disponible
Optimización automática de imágenes
// En un mu-plugin: wp-content/mu-plugins/image-optimization.php
<?php
/* Plugin Name: Image Optimization */
// No generar tamaños adicionales que no usas
function remove_unused_image_sizes() {
remove_image_size('1536x1536'); // WordPress 5.3+
remove_image_size('2048x2048'); // WordPress 5.3+
}
add_action('init', 'remove_unused_image_sizes');
// Activar la carga diferida (lazy loading) de imágenes
add_filter('wp_lazy_loading_enabled', '__return_true');
// Optimizar las imágenes al subirlas
add_filter('wp_image_editors', function($editors) {
return ['WP_Image_Editor_Imagick', 'WP_Image_Editor_GD'];
});Cómo activar la subida en WebP en WordPress
// Activar WebP (requiere soporte del servidor)
add_filter('wp_upload_image_mime_transforms', function($transforms) {
$transforms['image/jpeg'] = ['image/webp', 'image/jpeg'];
$transforms['image/png'] = ['image/webp', 'image/png'];
return $transforms;
});Cómo proteger WordPress después de la instalación
Cambiar el prefijo predeterminado de las tablas
Durante la instalación: Introduce un prefijo personalizado en lugar de wp_ (por ejemplo x7k9_, mysite_, proj23_)
Después de la instalación (avanzado):
-- Cambiar el prefijo en phpMyAdmin (¡haz antes una copia de seguridad!)
RENAME TABLE wp_posts TO x7k9_posts;
RENAME TABLE wp_options TO x7k9_options;
-- ... repetir para todas las tablas
-- Actualizar wp-config.php
define('DB_PREFIX', 'x7k9_');Proteger el archivo wp-config.php
# En .htaccess (directorio raíz de WordPress)
<files wp-config.php>
order allow,deny
deny from all
</files>
# Protección adicional
<files ~ "^.*\.([Hh][Tt][Aa])">
order allow,deny
deny from all
</files>Bloquear el acceso a directorios
# En wp-content/uploads/.htaccess
Options -Indexes
# En wp-includes/.htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>Limitar los intentos de inicio de sesión
// En un mu-plugin: wp-content/mu-plugins/login-security.php
<?php
/* Plugin Name: Login Security */
// Limitar los intentos de inicio de sesión (sin plugin)
add_action('wp_login_failed', function($username) {
$ip = $_SERVER['REMOTE_ADDR'];
$attempts = get_transient('login_attempts_' . $ip);
if ($attempts === false) {
set_transient('login_attempts_' . $ip, 1, HOUR_IN_SECONDS);
} else {
set_transient('login_attempts_' . $ip, $attempts + 1, HOUR_IN_SECONDS);
if ($attempts >= 5) {
wp_die('Demasiados intentos de inicio de sesión. Inténtalo de nuevo dentro de una hora.');
}
}
});
// Ocultar la versión de WordPress
remove_action('wp_head', 'wp_generator');
add_filter('the_generator', '__return_empty_string');Autenticación de dos factores (2FA)
// Exigir contraseñas seguras
add_action('user_profile_update_errors', function($errors) {
$password = $_POST['pass1'];
if (!empty($password) && strlen($password) < 12) {
$errors->add('password_error', 'La contraseña debe tener al menos 12 caracteres.');
}
});Cómo acelerar WordPress después de la instalación
Optimización básica de la base de datos
// En wp-config.php
// Limitar las revisiones de entradas
define('WP_POST_REVISIONS', 10);
// Vaciar la papelera automáticamente a los 7 días
define('EMPTY_TRASH_DAYS', 7);
// Desactivar el editor de archivos (seguridad + rendimiento)
define('DISALLOW_FILE_EDIT', true);Limpieza de la base de datos
// En un mu-plugin: wp-content/mu-plugins/db-cleanup.php
<?php
/* Plugin Name: DB Cleanup */
// Eliminar revisiones antiguas (conservar las últimas 10)
add_action('wp_scheduled_auto_draft_delete', function() {
global $wpdb;
$wpdb->query("DELETE FROM {$wpdb->posts} WHERE post_type = 'revision' AND post_date < DATE_SUB(NOW(), INTERVAL 30 DAY)");
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_%' AND option_value < UNIX_TIMESTAMP()");
$wpdb->query("OPTIMIZE TABLE {$wpdb->posts}, {$wpdb->options}, {$wpdb->postmeta}");
});Optimización de la Heartbeat API
// Reducir la frecuencia de Heartbeat
add_filter('heartbeat_settings', function($settings) {
$settings['interval'] = 60; // segundos (por defecto 15)
return $settings;
});
// Desactivar Heartbeat en el frontend
add_action('init', function() {
if (!is_admin()) {
wp_deregister_script('heartbeat');
}
}, 1);Quitar los enlaces RSD, shortlink y REST API de wp_head
// En un mu-plugin: wp-content/mu-plugins/disable-bloat.php
<?php
/* Plugin Name: Disable Bloat */
// Desactivar los emoji
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');
// Desactivar XML-RPC
add_filter('xmlrpc_enabled', '__return_false');
// Desactivar el enlace RSD
remove_action('wp_head', 'rsd_link');
// Desactivar el enlace de Windows Live Writer
remove_action('wp_head', 'wlwmanifest_link');
// Desactivar el enlace corto
remove_action('wp_head', 'wp_shortlink_wp_head');
// Desactivar el enlace de la REST API (si no la usas)
remove_action('wp_head', 'rest_output_link_wp_head');Ajustes básicos de SEO en WordPress
Título del sitio, descripción corta y visibilidad en buscadores
Ajustes > Generales:
- Título del sitio: [Tu marca] | [Descripción breve]
- Descripción corta: 2-3 frases sobre el sitio (meta description)
- Visibilidad en los motores de búsqueda: DESMARCA "Disuade a los motores de búsqueda de indexar este sitio"Estructura de enlaces permanentes amigable para el SEO
Ajustes > Enlaces permanentes > Nombre de la entradaMapa del sitio (XML Sitemap)
// Activar el mapa del sitio predeterminado de WordPress
add_filter('wp_sitemaps_enabled', '__return_true');
// Añadir tipos de contenido personalizados al mapa
add_filter('wp_sitemaps_post_types', function($post_types) {
$post_types[] = 'portfolio';
return $post_types;
});Optimización básica de imágenes para SEO
// Atributos ALT automáticos (alternativa)
add_filter('wp_get_attachment_image_attributes', function($attributes, $attachment) {
if (empty($attributes['alt'])) {
$attributes['alt'] = get_the_title($attachment->ID);
}
return $attributes;
}, 10, 2);
// Títulos de las imágenes
add_filter('wp_insert_attachment_data', function($data, $postarr) {
if (empty($data['post_title'])) {
$filename = basename($data['guid']);
$data['post_title'] = sanitize_title($filename);
}
return $data;
}, 10, 2);Cómo hacer copias de seguridad de WordPress
Copias automáticas de la base de datos
// En un mu-plugin: wp-content/mu-plugins/backup-scheduler.php
<?php
/* Plugin Name: Backup Scheduler */
// Copia diaria de la base de datos
add_action('daily_database_backup', function() {
$upload_dir = wp_upload_dir();
$backup_dir = $upload_dir['basedir'] . '/backups';
if (!file_exists($backup_dir)) {
wp_mkdir_p($backup_dir);
}
$filename = $backup_dir . '/db-backup-' . date('Y-m-d-H-i-s') . '.sql';
// Usa WP-CLI o mysqldump
exec('mysqldump --user=' . DB_USER . ' --password=' . DB_PASSWORD . ' --host=' . DB_HOST . ' ' . DB_NAME . ' > ' . $filename);
// Eliminar copias antiguas (conservar las últimas 7)
$files = glob($backup_dir . '/db-backup-*.sql');
if (count($files) > 7) {
array_multisort(array_map('filemtime', $files), SORT_ASC, $files);
for ($i = 0; $i < count($files) - 7; $i++) {
unlink($files[$i]);
}
}
});
// Programar la tarea
if (!wp_next_scheduled('daily_database_backup')) {
wp_schedule_event(time(), 'daily', 'daily_database_backup');
}Configurar copias externas
Soluciones recomendadas:
- UpdraftPlus: la versión gratuita basta para sitios pequeños
- BlogVault: de pago, pero muy fiable
- WPvivid: una buena alternativa gratuita
Configuración:
- Copia diaria (base de datos)
- Copia semanal (completa)
- Retención: mínimo 30 días
- Ubicación externa: Google Drive, Dropbox, S3
Configurar SMTP en WordPress
SMTP en lugar de mail()
// En wp-config.php o en un mu-plugin
define('SMTP_USER', '[email protected]');
define('SMTP_PASS', 'your-password');
define('SMTP_HOST', 'smtp.gmail.com');
define('SMTP_PORT', '587');
define('SMTP_SECURE', 'tls');
define('SMTP_FROM', '[email protected]');
define('SMTP_NAME', 'Tu sitio');
// En el mu-plugin
add_action('phpmailer_init', function($phpmailer) {
$phpmailer->isSMTP();
$phpmailer->Host = SMTP_HOST;
$phpmailer->SMTPAuth = true;
$phpmailer->Username = SMTP_USER;
$phpmailer->Password = SMTP_PASS;
$phpmailer->SMTPSecure = SMTP_SECURE;
$phpmailer->Port = SMTP_PORT;
$phpmailer->From = SMTP_FROM;
$phpmailer->FromName = SMTP_NAME;
});Cómo enviar un correo de prueba desde WordPress
// Función de prueba (ejecútala una vez)
add_action('admin_init', function() {
if (isset($_GET['test_email'])) {
wp_mail(get_option('admin_email'), 'Test Email', 'Este es un mensaje de prueba enviado desde WordPress.');
wp_die('¡Correo de prueba enviado!');
}
});Monitorización de WordPress y registro de errores
Registro de errores
// En wp-config.php
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', '/var/log/wp-errors.log'); // Fuera del directorio público
define('WP_DEBUG_DISPLAY', false);
// Registro de consultas SQL (solo en modo de depuración)
// define('SAVEQUERIES', true);Monitorización del tiempo de actividad
Herramientas recomendadas:
- UptimeRobot (plan gratuito: 50 monitores, intervalos de 5 minutos)
- Pingdom (de pago, pero más avanzado)
- Better Uptime (alternativa moderna)
Registrar inicios de sesión y cambios en entradas
// En un mu-plugin: wp-content/mu-plugins/activity-log.php
<?php
/* Plugin Name: Activity Log */
add_action('wp_login', function($user_login, $user) {
error_log('User logged in: ' . $user_login . ' from IP: ' . $_SERVER['REMOTE_ADDR']);
}, 10, 2);
add_action('save_post', function($post_id, $post, $update) {
if ($update && $post->post_status === 'publish') {
error_log('Post updated: ' . $post->post_title . ' by user: ' . get_current_user_id());
}
}, 10, 3);Qué revisar antes de lanzar un sitio WordPress
Lista de verificación antes del lanzamiento
- Todos los ajustes de SEO correctos
- Mapa del sitio generado
- Google Search Console configurado
- Google Analytics instalado
- Política de privacidad y términos añadidos
- Formularios de contacto probados
- El correo funciona
- SSL funciona (HTTPS)
- Redirecciones entre www y sin www configuradas
- Copia de seguridad realizada
- Plugins de caché configurados (opcional)
- CDN configurada (opcional)
- Pruebas de rendimiento realizadas (PageSpeed Insights)
- Pruebas de adaptación a móviles
- Pruebas en distintos navegadores
- Página 404 configurada
- Favicon añadido
Probar checksums y acceso a archivos con WP-CLI y curl
# Pruebas con WP-CLI
wp core verify-checksums
wp plugin verify-checksums --all
wp db check
# Pruebas de seguridad
curl -I https://tu-sitio.es/wp-admin/install.php # Debería devolver 404
curl -I https://tu-sitio.es/wp-config.php # Debería devolver 403Resumen: la lista de verificación de 50 puntos
Seguridad (1-15)
- ✅ Cambia el prefijo de las tablas de la base de datos
- ✅ Usa contraseñas seguras (mín. 12 caracteres)
- ✅ Cambia el nombre de usuario admin
- ✅ Desactiva el editor de archivos en el panel
- ✅ Bloquea la instalación de plugins (opcional)
- ✅ Fuerza SSL en el panel de administración
- ✅ Protege wp-config.php con .htaccess
- ✅ Bloquea el acceso a directorios (no indexes)
- ✅ Desactiva XML-RPC
- ✅ Oculta la versión de WordPress
- ✅ Limita los intentos de inicio de sesión
- ✅ Configura la autenticación de dos factores (2FA)
- ✅ Rota periódicamente las claves de autenticación (salts)
- ✅ Instala un certificado SSL
- ✅ Configura protecciones a nivel de servidor (cortafuegos, límite de intentos de inicio de sesión)
Rendimiento (16-25)
- ✅ Limita las revisiones de entradas (5-10)
- ✅ Desactiva los emoji
- ✅ Desactiva las funciones que no usas (XML-RPC, RSD)
- ✅ Optimiza los tamaños de imagen
- ✅ Activa la carga diferida (lazy loading)
- ✅ Configura la caché (de objetos, de página)
- ✅ Optimiza la Heartbeat API
- ✅ Activa la compresión GZIP/Brotli
- ✅ Configura una CDN (opcional)
- ✅ Optimiza la base de datos (limpieza automática)
SEO (26-35)
- ✅ Establece la estructura de enlaces permanentes “Nombre de la entrada”
- ✅ Configura el título y la descripción corta del sitio
- ✅ Activa el mapa del sitio XML
- ✅ Añade el sitio a Google Search Console
- ✅ Configura Google Analytics / GA4
- ✅ Optimiza las imágenes (atributos ALT)
- ✅ Crea la estructura de categorías
- ✅ Configura las migas de pan (breadcrumbs)
- ✅ Añade el schema Organization
- ✅ Prepara las metaetiquetas Open Graph
Copias de seguridad (36-42)
- ✅ Configura copias automáticas de la base de datos
- ✅ Configura copias completas semanales
- ✅ Guarda las copias en una ubicación externa
- ✅ Prueba la restauración desde una copia
- ✅ Fija la retención de las copias (mín. 30 días)
- ✅ Documenta el procedimiento de restauración
- ✅ Verifica con regularidad la integridad de las copias
Contenido y funcionalidad (43-50)
- ✅ Crea las páginas básicas (Sobre nosotros, Contacto, Política de privacidad)
- ✅ Configura los menús de navegación
- ✅ Prueba todos los formularios
- ✅ Configura SMTP para el correo
- ✅ Crea una página 404 personalizada
- ✅ Añade el favicon y los iconos PWA
- ✅ Realiza pruebas de rendimiento
- ✅ Documenta la configuración
Consulta nuestra auditoría de seguridad WordPress si quieres poner orden en los riesgos y las protecciones del sitio.
Diferencias en wp-config.php entre staging y producción
Esta guía define WP_DEBUG dos veces con valores distintos: true en la sección de depuración y false en la de monitorización. Las dos versiones son correctas, cada una para un entorno diferente, y por eso la misma copia de wp-config.php no debería llegar sin cambios a staging y a producción. Antes de mover el archivo entre servidores, repasa estas constantes:
WP_ENVIRONMENT_TYPE:stagingen la copia de pruebas,productionen el sitio en vivo. Cualquier condición basada enwp_get_environment_type()depende de este valor.WP_DEBUG: activado en staging, desactivado en producción.WP_DEBUG_DISPLAYse queda enfalseen ambos sitios y el registro va fuera del directorio público.SAVEQUERIES: solo mientras buscas consultas lentas, nunca en producción.DISALLOW_FILE_MODS: en producción bloquea la instalación y actualización de plugins desde el panel, así que las actualizaciones pasan por el despliegue o por WP-CLI. Acuérdalo con el cliente antes de que note que falta el botón de actualizar.- Claves de autenticación: generadas aleatoriamente en cada instalación. Una copia de producción en staging recibe su propio juego, y las claves de producción no se tocan, porque cambiarlas cierra la sesión de todos los usuarios.
Después de mover el archivo, ejecuta wp core verify-checksums y wp plugin verify-checksums --all de la sección de pruebas antes del lanzamiento.







