En serio, lo sé, hay muchos plugins del tipo de membresía o incluso especializados para hacer privada tu web, o pedir un login para poder verla, pero he probado todos los que hay y no hay ni uno al que no le sobre o le falte algo, todos pecan de algo:
- Demasiadas opciones cuando solo quieres forzar el login a WordPress para ver la web.
- Demasiado restrictivos, impidiendo el acceso de servicios como la API REST o incluso WP-CLI además del login normal de usuarios.
- Demasiado poco restrictivos, justo lo contrario de lo anterior, dejando ver la web por RSS o XML-RPC, por ejemplo.
- Con la mitad de las funcionalidades de pago.
Así que me harté, ¡ea!
Código de copiar y pegar para forzar el login
He preparado un trocito de código que montones de posibilidades para que cada cual lo personalice a su gusto y fuerce el acceso a WordPress como quiera, es este:
/**
* Easy Force Login Pro (porque es fácil y pro a la vez)
*
* Fuerza el inicio de sesión para todos los visitantes con múltiples
* excepciones configurables mediante constantes.
*
* @package AyudaWP_Force_Login_Snippet
* @author Fernando Tellado
* @license GPLv2 or later
* @link https://ayudawp.com/
*
* USO:
* 1. Copia este código en tu archivo functions.php del tema hijo
* 2. O créalo como plugin: añade la cabecera de plugin y súbelo a /wp-content/plugins/
* 3. Configura las constantes en wp-config.php según tus necesidades
*
* CONFIGURACIÓN:
* Define las constantes en wp-config.php ANTES de "That's all, stop editing!"
* Ejemplo: define( 'AYUDAWP_FORCE_LOGIN_ALLOW_FEEDS', true );
*/
defined( 'ABSPATH' ) || exit;
/*
* ==========================================================================
* CONSTANTES DE CONFIGURACIÓN
* ==========================================================================
*
* Define estas constantes en wp-config.php para personalizar el comportamiento.
* Si no se definen, se usarán los valores por defecto indicados.
*
* --------------------------------------------------------------------------
* EXCEPCIONES DE SISTEMA (por defecto activadas)
* --------------------------------------------------------------------------
*
* AYUDAWP_FORCE_LOGIN_ALLOW_REST_API (default: true)
* Permite acceso a la REST API. Necesario para el editor de bloques,
* plugins como Contact Form 7, configuraciones headless, etc.
*
* AYUDAWP_FORCE_LOGIN_ALLOW_CRON (default: true)
* Permite la ejecución de WP-Cron. Necesario para tareas programadas
* como publicaciones diferidas, copias de seguridad, emails, etc.
*
* AYUDAWP_FORCE_LOGIN_ALLOW_CLI (default: true)
* Permite comandos WP-CLI. Necesario para mantenimiento por terminal,
* despliegues automatizados y scripts de administración.
*
* AYUDAWP_FORCE_LOGIN_ALLOW_AJAX (default: true)
* Permite peticiones AJAX públicas (admin-ajax.php). Necesario para
* formularios de contacto, cargas dinámicas y funcionalidades AJAX.
*
* --------------------------------------------------------------------------
* EXCEPCIONES SEO (por defecto activadas)
* --------------------------------------------------------------------------
*
* AYUDAWP_FORCE_LOGIN_ALLOW_SITEMAP (default: true)
* Permite acceso a sitemaps XML. Necesario para que los buscadores
* puedan indexar tu sitio correctamente.
*
* AYUDAWP_FORCE_LOGIN_ALLOW_ROBOTS (default: true)
* Permite acceso a robots.txt. Necesario para que los buscadores
* conozcan las reglas de rastreo de tu sitio.
*
* AYUDAWP_FORCE_LOGIN_ALLOW_SEO_BOTS (default: true)
* Permite acceso a bots de buscadores (Google, Bing, etc.).
* Detecta los User-Agent más comunes de crawlers.
*
* --------------------------------------------------------------------------
* EXCEPCIONES OPCIONALES (por defecto desactivadas)
* --------------------------------------------------------------------------
*
* AYUDAWP_FORCE_LOGIN_ALLOW_XMLRPC (default: false)
* Permite acceso a XML-RPC. Solo actívalo si usas la app móvil de
* WordPress, Jetpack o herramientas que dependan de XML-RPC.
* ADVERTENCIA: XML-RPC es un vector de ataque común.
*
* AYUDAWP_FORCE_LOGIN_ALLOW_FEEDS (default: false)
* Permite acceso a feeds RSS/Atom. Actívalo si necesitas que los
* lectores RSS puedan acceder sin autenticación.
*
* AYUDAWP_FORCE_LOGIN_ALLOW_STATIC (default: false)
* Permite acceso a archivos estáticos (CSS, JS, imágenes, fuentes).
* Útil si la página de login necesita recursos del tema.
*
* AYUDAWP_FORCE_LOGIN_ALLOW_HEAD (default: true)
* Permite peticiones HTTP HEAD. Usado por servicios de monitorización
* de uptime para verificar que el sitio está activo.
*
* --------------------------------------------------------------------------
* CONFIGURACIÓN PERSONALIZADA
* --------------------------------------------------------------------------
*
* AYUDAWP_FORCE_LOGIN_ALLOWED_PATHS (default: '')
* Rutas públicas separadas por coma. Ejemplo: '/contacto/,/landing/,/tienda/'
* Usa rutas relativas desde la raíz del sitio.
*
* AYUDAWP_FORCE_LOGIN_ALLOWED_IPS (default: '')
* IPs permitidas separadas por coma. Ejemplo: '192.168.1.1,10.0.0.1'
* Útil para desarrollo, agencias o redes internas de confianza.
*
* AYUDAWP_FORCE_LOGIN_ALLOWED_USER_AGENTS (default: '')
* User-Agents permitidos separados por coma (coincidencia parcial).
* Ejemplo: 'UptimeRobot,Pingdom,StatusCake'
*
* --------------------------------------------------------------------------
* COMPORTAMIENTO DE BLOQUEO
* --------------------------------------------------------------------------
*
* AYUDAWP_FORCE_LOGIN_REDIRECT_TYPE (default: 'login')
* Tipo de respuesta al bloquear:
* - 'login': Redirige a wp-login.php (comportamiento por defecto)
* - '403': Muestra error 403 Forbidden
* - 'url': Redirige a URL personalizada (requiere definir la siguiente constante)
*
* AYUDAWP_FORCE_LOGIN_REDIRECT_URL (default: '')
* URL de redirección personalizada. Solo se usa si REDIRECT_TYPE es 'url'.
* Ejemplo: 'https://ejemplo.com/acceso-restringido/'
*
* ==========================================================================
*/
/**
* Función principal que fuerza el inicio de sesión.
*
* Se ejecuta en el hook 'template_redirect' para tener acceso a
* funciones condicionales como is_feed(), is_front_page(), etc.
*
* @since 1.0.0
* @return void
*/
function ayudawp_force_login_init() {
// ─────────────────────────────────────────────────────────────────────
// USUARIOS AUTENTICADOS: Siempre permitidos
// ─────────────────────────────────────────────────────────────────────
if ( is_user_logged_in() ) {
return;
}
// ─────────────────────────────────────────────────────────────────────
// ÁREA DE ADMINISTRACIÓN Y LOGIN: Siempre permitidos
// ─────────────────────────────────────────────────────────────────────
if ( is_admin() ) {
return;
}
$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
// Permitir página de login y registro.
if ( ayudawp_force_login_is_login_page( $request_uri ) ) {
return;
}
// ─────────────────────────────────────────────────────────────────────
// EXCEPCIONES DE SISTEMA
// ─────────────────────────────────────────────────────────────────────
// REST API (default: true).
$allow_rest = defined( 'AYUDAWP_FORCE_LOGIN_ALLOW_REST_API' ) ? AYUDAWP_FORCE_LOGIN_ALLOW_REST_API : true;
if ( $allow_rest && defined( 'REST_REQUEST' ) && REST_REQUEST ) {
return;
}
// WP-Cron (default: true).
$allow_cron = defined( 'AYUDAWP_FORCE_LOGIN_ALLOW_CRON' ) ? AYUDAWP_FORCE_LOGIN_ALLOW_CRON : true;
if ( $allow_cron && defined( 'DOING_CRON' ) && DOING_CRON ) {
return;
}
// WP-CLI (default: true).
$allow_cli = defined( 'AYUDAWP_FORCE_LOGIN_ALLOW_CLI' ) ? AYUDAWP_FORCE_LOGIN_ALLOW_CLI : true;
if ( $allow_cli && defined( 'WP_CLI' ) && WP_CLI ) {
return;
}
// AJAX (default: true).
$allow_ajax = defined( 'AYUDAWP_FORCE_LOGIN_ALLOW_AJAX' ) ? AYUDAWP_FORCE_LOGIN_ALLOW_AJAX : true;
if ( $allow_ajax && defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
// XML-RPC (default: false) - Vector de ataque común, desactivado por defecto.
$allow_xmlrpc = defined( 'AYUDAWP_FORCE_LOGIN_ALLOW_XMLRPC' ) ? AYUDAWP_FORCE_LOGIN_ALLOW_XMLRPC : false;
if ( $allow_xmlrpc && defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
return;
}
// ─────────────────────────────────────────────────────────────────────
// EXCEPCIONES SEO
// ─────────────────────────────────────────────────────────────────────
// Sitemaps (default: true).
$allow_sitemap = defined( 'AYUDAWP_FORCE_LOGIN_ALLOW_SITEMAP' ) ? AYUDAWP_FORCE_LOGIN_ALLOW_SITEMAP : true;
if ( $allow_sitemap && ayudawp_force_login_is_sitemap( $request_uri ) ) {
return;
}
// robots.txt (default: true).
$allow_robots = defined( 'AYUDAWP_FORCE_LOGIN_ALLOW_ROBOTS' ) ? AYUDAWP_FORCE_LOGIN_ALLOW_ROBOTS : true;
if ( $allow_robots && ayudawp_force_login_is_robots( $request_uri ) ) {
return;
}
// Feeds RSS/Atom (default: false).
$allow_feeds = defined( 'AYUDAWP_FORCE_LOGIN_ALLOW_FEEDS' ) ? AYUDAWP_FORCE_LOGIN_ALLOW_FEEDS : false;
if ( $allow_feeds && is_feed() ) {
return;
}
// Bots de buscadores (default: true).
$allow_seo_bots = defined( 'AYUDAWP_FORCE_LOGIN_ALLOW_SEO_BOTS' ) ? AYUDAWP_FORCE_LOGIN_ALLOW_SEO_BOTS : true;
if ( $allow_seo_bots && ayudawp_force_login_is_search_bot() ) {
return;
}
// ─────────────────────────────────────────────────────────────────────
// EXCEPCIONES TÉCNICAS
// ─────────────────────────────────────────────────────────────────────
// Peticiones HEAD para monitorización (default: true).
$allow_head = defined( 'AYUDAWP_FORCE_LOGIN_ALLOW_HEAD' ) ? AYUDAWP_FORCE_LOGIN_ALLOW_HEAD : true;
if ( $allow_head && ayudawp_force_login_is_head_request() ) {
return;
}
// Archivos estáticos (default: false).
$allow_static = defined( 'AYUDAWP_FORCE_LOGIN_ALLOW_STATIC' ) ? AYUDAWP_FORCE_LOGIN_ALLOW_STATIC : false;
if ( $allow_static && ayudawp_force_login_is_static_file( $request_uri ) ) {
return;
}
// ─────────────────────────────────────────────────────────────────────
// EXCEPCIONES PERSONALIZADAS
// ─────────────────────────────────────────────────────────────────────
// Rutas permitidas.
if ( ayudawp_force_login_is_allowed_path( $request_uri ) ) {
return;
}
// IPs permitidas.
if ( ayudawp_force_login_is_allowed_ip() ) {
return;
}
// User-Agents permitidos.
if ( ayudawp_force_login_is_allowed_user_agent() ) {
return;
}
// ─────────────────────────────────────────────────────────────────────
// BLOQUEAR ACCESO
// ─────────────────────────────────────────────────────────────────────
ayudawp_force_login_block_access( $request_uri );
}
add_action( 'template_redirect', 'ayudawp_force_login_init' );
/**
* Comprueba si la petición es a la página de login.
*
* Detecta wp-login.php y también URLs de login personalizadas
* que puedan estar configuradas con plugins de seguridad.
*
* @since 1.0.0
* @param string $request_uri URI de la petición actual.
* @return bool True si es página de login.
*/
function ayudawp_force_login_is_login_page( $request_uri ) {
// Comprobar wp-login.php estándar.
if ( false !== strpos( $request_uri, 'wp-login.php' ) ) {
return true;
}
// Comprobar wp-admin (redirige a login si no está autenticado).
if ( false !== strpos( $request_uri, 'wp-admin' ) ) {
return true;
}
// Comprobar wp-register.php para compatibilidad.
if ( false !== strpos( $request_uri, 'wp-register.php' ) ) {
return true;
}
return false;
}
/**
* Comprueba si la petición es a un sitemap.
*
* Detecta sitemaps del núcleo de WordPress, Yoast SEO,
* Rank Math, All in One SEO y otros plugins populares.
*
* @since 1.0.0
* @param string $request_uri URI de la petición actual.
* @return bool True si es un sitemap.
*/
function ayudawp_force_login_is_sitemap( $request_uri ) {
$sitemap_patterns = array(
'sitemap', // Patrón genérico (cubre sitemap.xml, sitemap_index.xml, etc.).
'wp-sitemap', // Sitemaps nativos de WordPress.
'.xml', // Archivos XML en general que contengan sitemap.
);
foreach ( $sitemap_patterns as $pattern ) {
if ( false !== strpos( $request_uri, $pattern ) && false !== strpos( $request_uri, 'sitemap' ) ) {
return true;
}
}
// Caso especial: solo extensión .xml sin sitemap en la URL (por si acaso).
if ( preg_match( '/sitemap[^\/]*\.xml/i', $request_uri ) ) {
return true;
}
return false;
}
/**
* Comprueba si la petición es a robots.txt.
*
* @since 1.0.0
* @param string $request_uri URI de la petición actual.
* @return bool True si es robots.txt.
*/
function ayudawp_force_login_is_robots( $request_uri ) {
return ( 'robots.txt' === basename( wp_parse_url( $request_uri, PHP_URL_PATH ) ) );
}
/**
* Comprueba si el visitante es un bot de buscadores.
*
* Detecta los crawlers más comunes de Google, Bing, Yahoo,
* DuckDuckGo, Baidu, Yandex y otros buscadores populares.
*
* @since 1.0.0
* @return bool True si es un bot de buscadores.
*/
function ayudawp_force_login_is_search_bot() {
$user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
if ( empty( $user_agent ) ) {
return false;
}
// Lista de User-Agents de bots de buscadores conocidos.
$search_bots = array(
'Googlebot', // Google.
'Bingbot', // Bing.
'Slurp', // Yahoo.
'DuckDuckBot', // DuckDuckGo.
'Baiduspider', // Baidu.
'YandexBot', // Yandex.
'Sogou', // Sogou.
'Exabot', // Exalead.
'facebot', // Facebook.
'ia_archiver', // Alexa.
'AdsBot-Google', // Google Ads.
'Mediapartners-Google', // Google AdSense.
'APIs-Google', // Google APIs.
'FeedFetcher-Google', // Google Feed Fetcher.
'Google-Read-Aloud', // Google Read Aloud.
'Chrome-Lighthouse', // Google Lighthouse.
'msnbot', // MSN/Bing antiguo.
'LinkedInBot', // LinkedIn.
'Twitterbot', // Twitter.
'applebot', // Apple.
);
foreach ( $search_bots as $bot ) {
if ( false !== stripos( $user_agent, $bot ) ) {
return true;
}
}
return false;
}
/**
* Comprueba si es una petición HTTP HEAD.
*
* Las peticiones HEAD se usan habitualmente por servicios
* de monitorización de uptime para verificar disponibilidad.
*
* @since 1.0.0
* @return bool True si es petición HEAD.
*/
function ayudawp_force_login_is_head_request() {
$method = isset( $_SERVER['REQUEST_METHOD'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : '';
return ( 'HEAD' === strtoupper( $method ) );
}
/**
* Comprueba si la petición es a un archivo estático.
*
* Detecta archivos CSS, JavaScript, imágenes, fuentes y
* otros recursos estáticos comunes.
*
* @since 1.0.0
* @param string $request_uri URI de la petición actual.
* @return bool True si es archivo estático.
*/
function ayudawp_force_login_is_static_file( $request_uri ) {
// Extensiones de archivos estáticos comunes.
$static_extensions = array(
// Hojas de estilo.
'css',
// JavaScript.
'js',
// Imágenes.
'jpg',
'jpeg',
'png',
'gif',
'webp',
'svg',
'ico',
'bmp',
'avif',
// Fuentes.
'woff',
'woff2',
'ttf',
'eot',
'otf',
// Mapas de código.
'map',
);
// Obtener la extensión del archivo.
$path = wp_parse_url( $request_uri, PHP_URL_PATH );
$extension = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) );
return in_array( $extension, $static_extensions, true );
}
/**
* Comprueba si la URI actual está en la lista de rutas permitidas.
*
* Las rutas se definen en la constante AYUDAWP_FORCE_LOGIN_ALLOWED_PATHS
* separadas por comas.
*
* @since 1.0.0
* @param string $request_uri URI de la petición actual.
* @return bool True si la ruta está permitida.
*/
function ayudawp_force_login_is_allowed_path( $request_uri ) {
if ( ! defined( 'AYUDAWP_FORCE_LOGIN_ALLOWED_PATHS' ) || empty( AYUDAWP_FORCE_LOGIN_ALLOWED_PATHS ) ) {
return false;
}
// Convertir la cadena de rutas en array.
$allowed_paths = array_map( 'trim', explode( ',', AYUDAWP_FORCE_LOGIN_ALLOWED_PATHS ) );
$allowed_paths = array_filter( $allowed_paths ); // Eliminar vacíos.
// Normalizar la URI actual (sin query string).
$current_path = wp_parse_url( $request_uri, PHP_URL_PATH );
$current_path = trailingslashit( $current_path );
foreach ( $allowed_paths as $allowed ) {
$allowed = trailingslashit( $allowed );
// Coincidencia exacta.
if ( $current_path === $allowed ) {
return true;
}
// Coincidencia de inicio (para permitir subrutas).
if ( 0 === strpos( $current_path, $allowed ) ) {
return true;
}
}
return false;
}
/**
* Comprueba si la IP del visitante está en la lista de permitidas.
*
* Las IPs se definen en la constante AYUDAWP_FORCE_LOGIN_ALLOWED_IPS
* separadas por comas. Soporta IPs individuales.
*
* @since 1.0.0
* @return bool True si la IP está permitida.
*/
function ayudawp_force_login_is_allowed_ip() {
if ( ! defined( 'AYUDAWP_FORCE_LOGIN_ALLOWED_IPS' ) || empty( AYUDAWP_FORCE_LOGIN_ALLOWED_IPS ) ) {
return false;
}
// Obtener IP del visitante (considerar proxies).
$user_ip = ayudawp_force_login_get_user_ip();
if ( empty( $user_ip ) ) {
return false;
}
// Convertir la cadena de IPs en array.
$allowed_ips = array_map( 'trim', explode( ',', AYUDAWP_FORCE_LOGIN_ALLOWED_IPS ) );
$allowed_ips = array_filter( $allowed_ips );
return in_array( $user_ip, $allowed_ips, true );
}
/**
* Obtiene la IP real del visitante.
*
* Considera cabeceras de proxy comunes para obtener la IP
* original cuando el sitio está detrás de un CDN o proxy inverso.
*
* @since 1.0.0
* @return string IP del visitante o cadena vacía.
*/
function ayudawp_force_login_get_user_ip() {
$ip_headers = array(
'HTTP_CF_CONNECTING_IP', // Cloudflare.
'HTTP_X_REAL_IP', // Nginx proxy.
'HTTP_X_FORWARDED_FOR', // Proxy estándar.
'REMOTE_ADDR', // Directo.
);
foreach ( $ip_headers as $header ) {
if ( ! empty( $_SERVER[ $header ] ) ) {
$ip = sanitize_text_field( wp_unslash( $_SERVER[ $header ] ) );
// X-Forwarded-For puede contener múltiples IPs, usar la primera.
if ( 'HTTP_X_FORWARDED_FOR' === $header && false !== strpos( $ip, ',' ) ) {
$ips = explode( ',', $ip );
$ip = trim( $ips[0] );
}
// Validar que es una IP válida.
if ( filter_var( $ip, FILTER_VALIDATE_IP ) ) {
return $ip;
}
}
}
return '';
}
/**
* Comprueba si el User-Agent está en la lista de permitidos.
*
* Los User-Agents se definen en AYUDAWP_FORCE_LOGIN_ALLOWED_USER_AGENTS
* separados por comas. La coincidencia es parcial (contiene).
*
* @since 1.0.0
* @return bool True si el User-Agent está permitido.
*/
function ayudawp_force_login_is_allowed_user_agent() {
if ( ! defined( 'AYUDAWP_FORCE_LOGIN_ALLOWED_USER_AGENTS' ) || empty( AYUDAWP_FORCE_LOGIN_ALLOWED_USER_AGENTS ) ) {
return false;
}
$user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
if ( empty( $user_agent ) ) {
return false;
}
// Convertir la cadena de User-Agents en array.
$allowed_agents = array_map( 'trim', explode( ',', AYUDAWP_FORCE_LOGIN_ALLOWED_USER_AGENTS ) );
$allowed_agents = array_filter( $allowed_agents );
foreach ( $allowed_agents as $allowed ) {
// Coincidencia parcial (el User-Agent contiene el patrón).
if ( false !== stripos( $user_agent, $allowed ) ) {
return true;
}
}
return false;
}
/**
* Bloquea el acceso según la configuración.
*
* Puede redirigir al login, mostrar error 403 o redirigir
* a una URL personalizada según AYUDAWP_FORCE_LOGIN_REDIRECT_TYPE.
*
* @since 1.0.0
* @param string $request_uri URI original para redirección post-login.
* @return void
*/
function ayudawp_force_login_block_access( $request_uri ) {
$redirect_type = defined( 'AYUDAWP_FORCE_LOGIN_REDIRECT_TYPE' ) ? AYUDAWP_FORCE_LOGIN_REDIRECT_TYPE : 'login';
switch ( $redirect_type ) {
case '403':
// Mostrar error 403 Forbidden.
status_header( 403 );
nocache_headers();
// Intentar cargar plantilla 403 del tema.
if ( locate_template( '403.php' ) ) {
get_template_part( '403' );
} else {
wp_die(
esc_html__( 'Access denied. You must be logged in to view this content.', 'ayudawp-force-login' ),
esc_html__( 'Access Denied', 'ayudawp-force-login' ),
array( 'response' => 403 )
);
}
exit;
case 'url':
// Redirigir a URL personalizada.
$custom_url = defined( 'AYUDAWP_FORCE_LOGIN_REDIRECT_URL' ) ? AYUDAWP_FORCE_LOGIN_REDIRECT_URL : '';
if ( ! empty( $custom_url ) && wp_http_validate_url( $custom_url ) ) {
wp_safe_redirect( esc_url_raw( $custom_url ) );
exit;
}
// Si la URL no es válida, usar login por defecto.
// No break intencionado para caer al caso 'login'.
case 'login':
default:
// Redirigir a la página de login con URL de retorno.
$redirect_to = home_url( $request_uri );
wp_safe_redirect( wp_login_url( $redirect_to ) );
exit;
}
}
Vale, me ha salido una pequeña bestia, pero oye, que es copiar y pegar, no tienes que escribirlo a mano ni memorizarlo, y viene con las opciones recomendables activas o inactivas por defecto, y luego tú decides lo que cambias, que viene todo muy bien explicado, para que no tengas que guardar por ahí un manual, y también por eso es un poco más largo el código.
¿Que qué hace este bicho?
Qué es y para qué sirve
El pequeñín te permite convertir cualquier sitio WordPress en una web privada, obligando a todos los visitantes a iniciar sesión antes de poder ver cualquier contenido. Es perfecto para, por ejemplo, Intranets corporativas, sitios de desarrollo, áreas de clientes o cualquier proyecto donde necesites restringir el acceso público.
A diferencia de los plugins típicos de forzar login, este código te da control total sobre las excepciones mediante constantes que defines en tu archivo wp-config.php. Esto significa que puedes configurar exactamente qué debe permanecer accesible sin tocar ni una línea del código.
Funciona inmediatamente, sin configuración
El código, por supuesto, funciona tal cual viene, solo tienes que copiarlo a tu tema hijo o subirlo como mini-plugin y ya está funcionando. Por defecto viene configurado con valores seguros y prácticos que cubren la mayoría de casos de uso.
Puedes copiar y pegar el código y funcionará tal cual, con los ajustes por defecto que trae si te sirven tal cual.
Las personalizaciones mediante constantes en el wp-config.php son por si quieres modificar las exclusiones por defecto sin tocar el código, sin romper nada de ese montón de código, así lo dejas a buen recaudo en tu instalación y solo tienes que añadir (o no) la constante con que quieras modificar su comportamiento por defecto.
Qué hace por defecto (sin tocar nada)
Nada más activarlo, el código …
Bloquea el acceso público a:
- Todas las páginas, entradas y contenido del sitio
- Archivos adjuntos y medios
- Cualquier URL que no esté en las excepciones
Permite el acceso automáticamente:
- Usuarios identificados (obviamente)
- Página de login (wp-login.php) y área de admin
- REST API (necesaria para el editor de bloques)
- WP-Cron (tareas programadas)
- WP-CLI (comandos de terminal)
- Peticiones AJAX (formularios, carga dinámica)
- Sitemaps XML (para SEO)
- robots.txt (para crawlers)
- Bots de buscadores (Google, Bing, etc.)
- Peticiones HEAD (servicios de uptime)
Bloquea por defecto (por seguridad):
- XML-RPC (fuente habitual de ataques)
- Feeds RSS/Atom
- Archivos estáticos públicos
Cuando bloquea:
- Redirige a wp-login.php
- Después del login, vuelve a la página que se intentaba ver
Cómo se instala
Hazlo como prefieras, pero te recomiendo una de estas dos opciones:
- En el
functions.phpde tu tema hijo: Copia todo el código en el archivofunctions.phpde tu tema hijo. Es la forma más rápida si solo necesitas la funcionalidad básica. - Como mini-plugin: Crea un archivo PHP (por ejemplo
force-login.phpo similar), añade al principio esta cabecera:
<?php
/**
* Plugin Name: AyudaWP Force Login
* Description: Fuerza inicio de sesión en todo el sitio
* Version: 1.0.0
* Author: Tu nombre
*/
Luego pega el resto del código debajo y sube el archivo a/wp-content/plugins/.
Personalización (opcional) mediante constantes
Casi lo que más me gusta de este chiquitín está en su configuración mediante constantes. Simplemente añades el código, y luego lo personalizas (totalmente opcional) definiendo cómo quieres que funcione mediante constantes en tu wp-config.php (antes de la línea "That's all, stop editing!"). El código las lee automáticamente.
Excepciones de sistema
Estas opciones controlan funcionalidades críticas de WordPress. Vienen activadas por defecto porque desactivarlas podría romper cosas importantes:
AYUDAWP_FORCE_LOGIN_ALLOW_REST_API (por defecto: true)
define( 'AYUDAWP_FORCE_LOGIN_ALLOW_REST_API', true );
La REST API es el corazón del editor de bloques, WooCommerce, Contact Form 7 y prácticamente cualquier plugin moderno. Si la desactivas, el editor Gutenberg dejará de funcionar y muchos plugins se romperán. Solo desactívala si sabes exactamente lo que haces y tu sitio usa exclusivamente el editor clásico o no usa plugin alguno que necesite de REST API.
AYUDAWP_FORCE_LOGIN_ALLOW_CRON (por defecto: true)
define( 'AYUDAWP_FORCE_LOGIN_ALLOW_CRON', true );
WP-Cron, como ya sabes, gestiona todas las tareas programadas: publicar entradas diferidas, enviar emails, ejecutar copias de seguridad automáticas, renovar certificados… Desactivarlo significa que nada de esto funcionará cuando tu sitio esté en modo privado.
AYUDAWP_FORCE_LOGIN_ALLOW_CLI (por defecto: true)
define( 'AYUDAWP_FORCE_LOGIN_ALLOW_CLI', true );
WP-CLI es la herramienta de línea de comandos para WordPress. Si usas SSH para administrar tu sitio, hacer actualizaciones automatizadas o tienes scripts de mantenimiento, necesitas esta opción activada.
AYUDAWP_FORCE_LOGIN_ALLOW_AJAX (por defecto: true)
define( 'AYUDAWP_FORCE_LOGIN_ALLOW_AJAX', true );
Las peticiones AJAX permiten que los formularios de contacto envíen datos, que se cargue contenido dinámico y que muchas funcionalidades sin recargar página funcionen. En la mayoría de casos querrás mantenerla activada.
AYUDAWP_FORCE_LOGIN_ALLOW_XMLRPC (por defecto: false)
define( 'AYUDAWP_FORCE_LOGIN_ALLOW_XMLRPC', false );
XML-RPC es un protocolo antiguo que usa la app móvil de WordPress, Jetpack y algunas herramientas de publicación remota. El problema es que también es un vector de ataque muy común (ataques de fuerza bruta, amplificación DDoS…), vamos, una puerta abierta a los malos. Solo actívalo si realmente necesitas alguna de esas funcionalidades, y considera usar alternativas más seguras cuando sea posible.
Excepciones SEO
Si necesitas que tu sitio siga apareciendo en buscadores aunque sea privado (algo contradictorio pero hay casos), estas opciones te ayudan:
AYUDAWP_FORCE_LOGIN_ALLOW_SITEMAP (por defecto: true)
define( 'AYUDAWP_FORCE_LOGIN_ALLOW_SITEMAP', true );
Permite acceso a los mapas del sitio en XML que usan los buscadores para descubrir tus páginas. Funciona con los sitemaps nativos de WordPress y con los de plugins como Yoast, Rank Math o SEOPress.
AYUDAWP_FORCE_LOGIN_ALLOW_ROBOTS (por defecto: true)
define( 'AYUDAWP_FORCE_LOGIN_ALLOW_ROBOTS', true );
El archivo robots.txt indica a los buscadores qué pueden y qué no pueden rastrear. Sin acceso a él, los bots no sabrán cómo comportarse en tu web.
AYUDAWP_FORCE_LOGIN_ALLOW_SEO_BOTS (por defecto: true)
define( 'AYUDAWP_FORCE_LOGIN_ALLOW_SEO_BOTS', true );
Esta opción detecta automáticamente los User-Agent de los principales buscadores (Google, Bing, Yahoo, DuckDuckGo, Baidu, Yandex…) y los deja pasar. Muy útil si quieres un sitio privado para humanos pero público para buscadores.
AYUDAWP_FORCE_LOGIN_ALLOW_FEEDS (por defecto: false)
define( 'AYUDAWP_FORCE_LOGIN_ALLOW_FEEDS', false );
Los feeds RSS y Atom permiten que lectores de noticias y agregadores accedan a tu contenido. Por defecto está desactivado porque si quieres privacidad, probablemente tampoco quieras que el contenido se distribuya por RSS.
Excepciones técnicas
Estas son especialitas pero importantes:
AYUDAWP_FORCE_LOGIN_ALLOW_HEAD (por defecto: true)
define( 'AYUDAWP_FORCE_LOGIN_ALLOW_HEAD', true );
Las peticiones HTTP HEAD son las que usan servicios de monitorización como UptimeRobot o StatusCake para comprobar si tu sitio está online. Solo piden las cabeceras HTTP, no el contenido. Si las bloqueas, estos servicios pensarán que tu web está caída.
AYUDAWP_FORCE_LOGIN_ALLOW_STATIC (por defecto: false)
define( 'AYUDAWP_FORCE_LOGIN_ALLOW_STATIC', false );
Esta opción permite acceso público a archivos estáticos: CSS, JavaScript, imágenes, fuentes… Puede ser necesaria si tu página de login personalizada necesita cargar recursos del tema. Por defecto está desactivada porque normalmente WordPress ya gestiona esto correctamente.
Excepciones personalizadas
Aquí es donde defines exactamente qué debe quedar público en tu caso concreto:
AYUDAWP_FORCE_LOGIN_ALLOWED_PATHS
define( 'AYUDAWP_FORCE_LOGIN_ALLOWED_PATHS', '/contacto/,/landing/,/tienda/,/' );
Lista de rutas que quieres mantener públicas, separadas por comas. Usa rutas relativas desde la raíz del sitio. El «/» solo (al final del ejemplo) representa la página de inicio. Las subrutas se incluyen automáticamente, así que si pones /tienda/ también quedará pública /tienda/producto-1/.
AYUDAWP_FORCE_LOGIN_ALLOWED_IPS
define( 'AYUDAWP_FORCE_LOGIN_ALLOWED_IPS', '192.168.1.100,10.0.0.50,203.0.113.42' );
IPs que pueden acceder sin autorización. Muy útil para tu equipo de desarrollo, tu agencia, o para crear una lista blanca de oficinas que siempre tengan acceso. El código detecta correctamente la IP real incluso si usas Cloudflare u otros proxies.
AYUDAWP_FORCE_LOGIN_ALLOWED_USER_AGENTS
define( 'AYUDAWP_FORCE_LOGIN_ALLOWED_USER_AGENTS', 'UptimeRobot,Pingdom,StatusCake' );
Estos son User-Agent permitidos mediante coincidencia parcial. Si el User-Agent del visitante contiene alguno de estos textos, podrá acceder. Útil para servicios de monitorización que no uses peticiones HEAD, APIs específicas o cualquier herramienta que se identifique con un User-Agent conocido.
Comportamiento cuando bloquea
¿Qué pasa cuando alguien sin permisos intenta acceder?
AYUDAWP_FORCE_LOGIN_REDIRECT_TYPE (por defecto: ‘login’)
define( 'AYUDAWP_FORCE_LOGIN_REDIRECT_TYPE', 'login' );
Tienes tres opciones:
'login': Redirige awp-login.php. Después de identificarse, el usuario vuelve a la página que intentaba ver.'403': Muestra un error403 Forbidden. Si tu tema tiene una plantilla403.php, la usará.'url': Redirige a una URL personalizada que definas con la siguiente constante.
AYUDAWP_FORCE_LOGIN_REDIRECT_URL
define( 'AYUDAWP_FORCE_LOGIN_REDIRECT_URL', 'https://tudominio.com/acceso-restringido/' );
Solo se usa cuando REDIRECT_TYPE de la constante anterior es 'url'. Puede ser una página de tu sitio o incluso una URL externa.
Ejemplo de configuración completa
Imagina que tienes una Intranet corporativa donde quieres:
- Que la página de inicio y la de contacto sean públicas
- Que el equipo de sistemas (con IPs fijas) pueda acceder sin login
- Que los buscadores puedan indexar solo las páginas públicas
- Que UptimeRobot pueda monitorizar el sitio
Tu configuración en wp-config.php sería:
// Activar excepciones necesarias define( 'AYUDAWP_FORCE_LOGIN_ALLOW_REST_API', true ); define( 'AYUDAWP_FORCE_LOGIN_ALLOW_CRON', true ); define( 'AYUDAWP_FORCE_LOGIN_ALLOW_SEO_BOTS', true ); define( 'AYUDAWP_FORCE_LOGIN_ALLOW_HEAD', true ); // Desactivar lo que no necesitas define( 'AYUDAWP_FORCE_LOGIN_ALLOW_XMLRPC', false ); define( 'AYUDAWP_FORCE_LOGIN_ALLOW_FEEDS', false ); // Páginas públicas define( 'AYUDAWP_FORCE_LOGIN_ALLOWED_PATHS', '/,/contacto/' ); // IPs del equipo de IT define( 'AYUDAWP_FORCE_LOGIN_ALLOWED_IPS', '192.168.1.10,192.168.1.11,192.168.1.12' ); // Servicios de monitorización define( 'AYUDAWP_FORCE_LOGIN_ALLOWED_USER_AGENTS', 'UptimeRobot' ); // Redirigir al login por defecto define( 'AYUDAWP_FORCE_LOGIN_REDIRECT_TYPE', 'login' );
Consideraciones de seguridad
Este código está pensado para complementar, no sustituir, otras medidas de seguridad. Ten en cuenta lo siguiente:
- Los
User-Agentse pueden falsificar: Cualquiera puede cambiar suUser-Agentpara hacerse pasar porGooglebot. Si necesitas seguridad real, confía más en la autenticación que en las excepciones deUser-Agent. - Las IPs pueden cambiar: Si tu equipo trabaja con IPs dinámicas (conexiones domésticas típicas), la lista blanca de IPs no será práctica. En ese caso es mejor usar VPN o autenticación estándar.
- El protocolo
XML-RPCes un riesgo siempre: Hay una razón por la que viene desactivado por defecto. Si lo activas, asegúrate de tener otras protecciones comofail2bano un WAF. - Revisa los logs: De vez en cuando comprueba los registros de acceso de tu servidor para ver si hay patrones sospechosos de intentos de acceso.
Solución de problemas (podría pasar)
A ver, nada es perfecto, y un exceso de celo en la privacidad puede hacer que no funcionen cosas, no es que el código no funcione, es que a veces pasan cosas, como por ejemplo:
- El editor de bloques no funciona:
Comprueba queAYUDAWP_FORCE_LOGIN_ALLOW_REST_APIestá entrue. - Los formularios de contacto no envían:
Probablemente necesitasAYUDAWP_FORCE_LOGIN_ALLOW_AJAXentrue. - Las tareas programadas no se ejecutan:
Verifica queAYUDAWP_FORCE_LOGIN_ALLOW_CRONestá entrue. - Mi servicio de uptime dice que el sitio está caído:
Añade suUser-AgentaAYUDAWP_FORCE_LOGIN_ALLOWED_USER_AGENTSo asegúrate de queAYUDAWP_FORCE_LOGIN_ALLOW_HEADestá entrue. - Quiero que una sección entera sea pública:
Añade la ruta base aAYUDAWP_FORCE_LOGIN_ALLOWED_PATHS. Por ejemplo,/blog/hará públicas todas las entradas del blog.
¿Y no sería mejor hacer un plugin con todo esta virguería?
Si prefieres una interfaz gráfica en lugar de editar wp-config.php, estoy preparando ya un plugin que, si nada se tuerce, le llamaré algo pomposo como Easy Force Login Pro, al final me vine arriba y preparé un plugin completo con página de ajustes donde poder configurar todas estas opciones con casillas de selección y campos de texto, y se llama Gozer, para que puedas hacer un WordPress privado totalmente según tus necesidades.
Será Es la misma funcionalidad pero menos intimidante para usuarios que no quieren tocar código, y con algunas mejoras adicionales.
El código es ideal si prefieres control total, necesitas que la configuración no sea modificable desde el admin, o simplemente quieres evitar añadir otro plugin a tu instalación. Y encima es gratis, vamos, que no le veo pegas, y sino me lo cuentas abajo, en los comentarios.
¿Te gustó este artículo? ¡Ni te imaginas lo que te estás perdiendo en YouTube!







Gracias