
Los mu-plugins (plugins de uso obligatorio) son perfectos para el código propio de un sitio: se cargan siempre, antes que los plugins normales, y nadie los puede desactivar por error desde el escritorio. En esta serie de guías los usamos mucho.
Pero cuando juntas varios, aparece una pregunta: ¿puedo ordenarlos en carpetas?
La regla: solo la primera altura
WordPress carga únicamente los archivos .php que están directamente en wp-content/mu-plugins/. Si pones una carpeta adentro, ignora todo lo que contiene.
wp-content/mu-plugins/
├── cwp-seguridad.php ← se carga
├── cwp-rendimiento.php ← se carga
└── cwp-sitio/
└── correos.php ← NO se carga
Por eso, para un mu-plugin con varios archivos, se usa un archivo cargador.
El archivo cargador
La estructura:
wp-content/mu-plugins/
├── cwp-sitio.php ← el cargador
└── cwp-sitio/
├── seguridad.php
├── rendimiento.php
├── correos.php
└── assets/
├── admin.css
└── editor.js
Y el cargador, cwp-sitio.php:
<?php
/**
* Plugin Name: Funciones del sitio (Curso WordPress)
* Description: Carga los módulos de la carpeta cwp-sitio.
*/
defined( 'ABSPATH' ) || exit;
define( 'CWP_SITIO_DIR', __DIR__ . '/cwp-sitio/' );
define( 'CWP_SITIO_URL', plugins_url( 'cwp-sitio/', __FILE__ ) );
foreach ( array( 'seguridad', 'rendimiento', 'correos' ) as $modulo ) {
$archivo = CWP_SITIO_DIR . $modulo . '.php';
if ( is_readable( $archivo ) ) {
require_once $archivo;
}
}
Ventajas de una lista explícita (en vez de cargar «todo lo que haya en la carpeta»):
- Controlas el orden de carga.
- Para desactivar un módulo, lo quitas de la lista.
- Un archivo de prueba o una copia de respaldo que dejes en la carpeta no se ejecuta por accidente.
Si prefieres cargar todos los archivos de la carpeta, puedes usar glob( CWP_SITIO_DIR . '*.php' ), pero entonces cualquier archivo .php que alguien suba ahí se ejecutará en cada visita. La lista explícita es más segura.
Scripts y estilos
Para enlazar archivos de la carpeta assets, usa la constante de URL que definimos. plugins_url() detecta que el archivo está en mu-plugins y arma la dirección correcta:
<?php
// Dentro de cwp-sitio/rendimiento.php, por ejemplo.
add_action( 'admin_enqueue_scripts', function () {
wp_enqueue_style(
'cwp-sitio-admin',
CWP_SITIO_URL . 'assets/admin.css',
array(),
filemtime( CWP_SITIO_DIR . 'assets/admin.css' )
);
} );
Usar filemtime() como versión hace que el navegador descargue el archivo nuevo cada vez que lo modificas.
Lo que no funciona en un mu-plugin
- Activación y desactivación:
register_activation_hook()yregister_deactivation_hook()no se ejecutan nunca, porque un mu-plugin no se «activa». Si necesitas crear una tabla o una opción la primera vez, compruébalo tú: si la opción no existe, créala. - Actualizaciones automáticas: WordPress no actualiza mu-plugins. Los actualizas tú.
- Aparecer en la lista de plugins normal: aparecen en Plugins → Imprescindibles, y solo el archivo cargador.
- Traducciones automáticas del directorio: si tu mu-plugin tiene textos traducibles, cárgalos tú con
load_muplugin_textdomain().
¿Cuándo pasar a un plugin normal?
Si tu código crece mucho, necesita activación, una pantalla de ajustes con su propio menú, o lo vas a usar en varios sitios, conviértelo en un plugin normal. Un mu-plugin es ideal para código propio del sitio, pequeño y que siempre debe estar activo.
Como los mu-plugins no se pueden desactivar desde el escritorio, un error de PHP en uno de ellos deja el sitio caído hasta que lo corrijas por FTP o terminal. Prueba cada cambio en una copia del sitio antes.
¿Cuántos mu-plugins tiene tu sitio? Cuéntanos en el foro cómo los organizas.





