Pulsa ESC para cerrar

Curso WordpressCurso Wordpress

Haz tu WordPress privado sin plugins: un mu-plugin que cierra todas las puertas

Hay muchos motivos para tener un WordPress que solo vean usuarios conectados: una intranet para tu equipo, un sitio en construcción que el cliente debe revisar, una copia de prueba que no quieres que Google encuentre o un área de alumnos.

La solución típica es redirigir al inicio de sesión a quien no esté conectado. El problema es que eso solo cierra las páginas. Tu contenido sigue saliendo por otras puertas que casi nadie revisa:

  • La API REST (/wp-json/wp/v2/posts) entrega todas tus entradas, con título y contenido completo.
  • Los feeds (/feed/) muestran las últimas entradas.
  • El mapa del sitio (/wp-sitemap.xml) lista todas tus URLs.

Vamos a construir un mu-plugin que cierre todas, paso a paso. Es también un buen ejercicio para entender cómo WordPress procesa cada tipo de petición.

El mu-plugin completo

Crea wp-content/mu-plugins/cwp-sitio-privado.php:

<?php
/**
 * Plugin Name: Sitio privado (Curso WordPress)
 * Description: Solo usuarios conectados pueden ver el sitio, la API, los feeds y el mapa del sitio.
 */

// Páginas públicas aunque el sitio sea privado (slugs).
const CWP_PAGINAS_PUBLICAS = array( 'contacto', 'politica-de-privacidad' );

// IPs que pueden ver todo sin iniciar sesión (por ejemplo, tu oficina).
const CWP_IPS_PERMITIDAS = array( '200.0.0.1' );

function cwp_es_visita_permitida() {
	if ( is_user_logged_in() ) {
		return true;
	}
	$ip = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
	return in_array( $ip, CWP_IPS_PERMITIDAS, true );
}

// 1. Páginas: quien no esté conectado va al inicio de sesión.
add_action( 'template_redirect', function () {
	if ( cwp_es_visita_permitida() ) {
		return;
	}
	if ( ! empty( CWP_PAGINAS_PUBLICAS ) && is_page( CWP_PAGINAS_PUBLICAS ) ) {
		return;
	}
	auth_redirect(); // Redirige al login y vuelve a esta página después.
} );

// 2. API REST: solo para usuarios conectados.
add_filter( 'rest_authentication_errors', function ( $resultado ) {
	if ( ! empty( $resultado ) || cwp_es_visita_permitida() ) {
		return $resultado;
	}
	return new WP_Error( 'cwp_privado', 'Este sitio es privado.', array( 'status' => 401 ) );
} );

// 3. Feeds: fuera.
foreach ( array( 'do_feed', 'do_feed_rdf', 'do_feed_rss', 'do_feed_rss2', 'do_feed_atom' ) as $feed ) {
	add_action( $feed, function () {
		if ( ! cwp_es_visita_permitida() ) {
			wp_die( 'Este sitio es privado.', '', array( 'response' => 403 ) );
		}
	}, 1 );
}

// 4. Mapa del sitio de WordPress: desactivado.
add_filter( 'wp_sitemaps_enabled', '__return_false' );

// 5. Que los buscadores no indexen nada.
add_filter( 'wp_robots', function ( $robots ) {
	$robots['noindex']  = true;
	$robots['nofollow'] = true;
	return $robots;
} );

Qué hace cada parte

  1. template_redirect se ejecuta justo antes de mostrar cualquier página del sitio. Ahí comprobamos si la visita está permitida; si no, auth_redirect() la manda al login y, después de entrar, la devuelve a la página que quería ver. Las páginas de la lista CWP_PAGINAS_PUBLICAS quedan abiertas.
  2. rest_authentication_errors es el filtro que WordPress consulta antes de responder cualquier petición a la API. Devolver un error ahí la bloquea por completo para visitantes anónimos. El editor de bloques sigue funcionando porque los usuarios conectados sí pasan.
  3. do_feed_* son las acciones que generan cada tipo de feed. Las interceptamos con prioridad 1, antes de que se genere nada.
  4. wp_sitemaps_enabled apaga el mapa del sitio nativo. Si usas un plugin de SEO que tiene su propio mapa, desactívalo también en sus ajustes.
  5. wp_robots agrega noindex, nofollow a todas las páginas, por si algún buscador llegó a verlas antes.

Lo que sigue funcionando: la pantalla de inicio de sesión, la recuperación de contraseña, admin-ajax.php y wp-cron.php no pasan por template_redirect, así que no hace falta hacerles excepciones.

Las imágenes y archivos subidos siguen siendo públicos. El servidor los entrega directamente, sin pasar por WordPress. Si alguien tiene la URL de un PDF en wp-content/uploads, lo podrá abrir. Para documentos realmente confidenciales necesitas guardarlos fuera de uploads o usar un plugin de descargas protegidas.

Alternativa rápida para copias de prueba

Si solo quieres esconder una copia de prueba (por ejemplo, prueba.tudominio.cl), cPanel ofrece Privacidad de directorios: proteges la carpeta con usuario y contraseña a nivel del servidor, y nadie ve nada, ni siquiera la pantalla de login de WordPress.

Su punto débil es que también bloquea las peticiones que WordPress se hace a sí mismo (las usan WP-Cron y Salud del sitio). Si ves avisos de «la petición de bucle invertido falló», es por eso, y puedes ignorarlos mientras sea una copia de prueba.

Para probar que todo quedó cerrado

Desde una ventana de incógnito o una terminal:

curl -I https://tudominio.cl/
curl https://tudominio.cl/wp-json/wp/v2/posts
curl -I https://tudominio.cl/feed/
curl -I https://tudominio.cl/wp-sitemap.xml

Lo esperado: la portada redirige (302) al login, la API responde 401, el feed 403 y el mapa del sitio también te manda al login, porque ya no existe y cae en la regla de las páginas.

¿Necesitas que algunos roles vean solo ciertas secciones? Plantea tu caso en el foro y vemos cómo extender este mu-plugin.