Tema
Cloudflare a fondo
Ya tienes mitienda.com en Cloudflare con SSL en Full (strict). Ahora vamos a sacarle todo el jugo al plan gratuito: caché para que el sitio vuele, redirecciones sin tocar el servidor, un escudo contra bots y ataques, la IP real de tus visitantes, y una probada de las herramientas avanzadas (Workers, Pages, API, DNSSEC, túneles).
Los menús cambian
Cloudflare rediseña su panel con frecuencia. Las rutas de esta lección describen dónde suele estar cada opción; si no la encuentras, usa el buscador del panel (arriba) escribiendo el nombre de la función: "Cache Rules", "Redirect Rules", "Bot Fight Mode"...
1. Caché: qué guarda Cloudflare y cuándo
Concepto
Con nube naranja, Cloudflare puede guardar copias de tus archivos en cada uno de sus centros de datos. Cuando otro visitante de la misma región pide el mismo archivo, Cloudflare lo entrega sin molestar a tu servidor.
txt
Primer visitante en Guadalajara:
Visitante → Cloudflare GDL (no lo tiene: MISS) → tu hosting → Cloudflare guarda copia → visitante
Segundo visitante en Guadalajara:
Visitante → Cloudflare GDL (lo tiene: HIT) → visitante (tu hosting ni se entera)Qué se cachea por defecto
Cloudflare cachea por extensión de archivo: imágenes (jpg, png, webp, gif, svg, ico), css, js, fuentes (woff, woff2), pdf, zip, video y similares. No cachea HTML por defecto, porque el HTML suele ser dinámico (carrito, sesión, contenido personalizado).
Lo puedes comprobar con la cabecera cf-cache-status:
bash
curl -sI https://mitienda.com/wp-content/themes/tema/style.css | grep -i cf-cache-status
# cf-cache-status: HIT
curl -sI https://mitienda.com/ | grep -i cf-cache-status
# cf-cache-status: DYNAMIC ← HTML, no se cachea| Valor | Significado |
|---|---|
HIT | Servido desde la caché de Cloudflare. |
MISS | No estaba; se pidió al origen (la próxima vez será HIT). |
EXPIRED | Estaba pero venció; se volvió a pedir. |
REVALIDATED | Estaba vencido, el origen confirmó que sigue igual. |
BYPASS | Una regla o cabecera (ej. cookie, Cache-Control: private) indicó no cachear. |
DYNAMIC | No es cacheable según la configuración (típico del HTML). |
Purgar caché
Subiste un nuevo logo.png con el mismo nombre y sigues viendo el viejo. Ruta: Caching → Configuration → Purge Cache:
- Custom Purge / Purgar personalizado: pegas las URLs exactas (
https://mitienda.com/img/logo.png). Es lo ideal. - Purge Everything / Purgar todo: borra todo. Úsalo con moderación: el siguiente minuto tu servidor recibirá todas las peticiones de golpe.
Modo de desarrollo
Caching → Configuration → Development Mode: desactiva temporalmente la caché (se apaga solo después de 3 horas). Útil mientras haces cambios de diseño. No olvides que existe: si se queda activo durante una campaña, pierdes la ventaja de la CDN.
Reglas de caché (Cache Rules)
Ruta: Caching → Cache Rules (o Rules → Cache Rules). Permiten decir "esto sí se cachea, esto no, y por cuánto tiempo". Ejemplo: cachear el HTML de un sitio WordPress de información, excepto el panel y usuarios con sesión.
txt
Nombre: Cachear HTML público
Cuando la petición coincida con (expresión personalizada):
(http.host eq "mitienda.com"
and not starts_with(http.request.uri.path, "/wp-admin")
and not starts_with(http.request.uri.path, "/wp-login.php")
and not http.cookie contains "wordpress_logged_in"
and not http.cookie contains "woocommerce_items_in_cart")
Entonces:
Cache eligibility: Eligible for cache (Apto para caché)
Edge TTL: Ignorar cabecera del origen → 2 horas
Browser TTL: Respetar el origenNunca caches páginas personalizadas
Carrito, checkout, "mi cuenta", paneles. Si cacheas HTML con sesión, el visitante B puede ver la página del visitante A (¡con su nombre y pedido!). Ante la duda, no caches HTML; con la caché de estáticos ya ganas mucho.
2. Redirecciones sin tocar el servidor
Ruta: Rules → Redirect Rules (Reglas de redireccionamiento). Funcionan solo si el nombre tiene un registro DNS en nube naranja, porque es Cloudflare quien responde.
www → raíz
Cloudflare suele ofrecer una plantilla ("Redirect from WWW to root"). A mano:
txt
Nombre: www a raíz
Cuando: (http.host eq "www.mitienda.com")
Entonces:
Tipo: Dinámico (Dynamic)
Expresión: concat("https://mitienda.com", http.request.uri.path)
Código: 301
Conservar cadena de consulta (Preserve query string): ✓Resultado: http://www.mitienda.com/productos?cat=2 → https://mitienda.com/productos?cat=2.
Dominio viejo → dominio nuevo
La tienda cambió de nombre: viejatienda.com → mitienda.com. Agrega viejatienda.com como otra zona en Cloudflare (gratis), crea un registro "de relleno" en naranja para que el tráfico llegue a Cloudflare, y una regla:
txt
DNS de viejatienda.com:
AAAA @ 100:: 🟠 Proxied (dirección "de descarte": nunca se usa, solo activa el proxy)
AAAA www 100:: 🟠 Proxied
Redirect Rule en viejatienda.com:
Cuando: (http.host eq "viejatienda.com" or http.host eq "www.viejatienda.com")
URL: concat("https://mitienda.com", http.request.uri.path) 301, conservar queryAsí cada página vieja lleva a su equivalente nueva y Google transfiere el posicionamiento. Si tienes cientos de dominios o URLs sueltas, usa Bulk Redirects (listas de redirecciones).
Error común: bucles por redirigir en dos lugares
Si el .htaccess manda mitienda.com → www y Cloudflare manda www → mitienda.com, tienes ERR_TOO_MANY_REDIRECTS. Decide un solo lugar y una sola dirección canónica.
3. Page Rules, Configuration Rules y compañía
Las Page Rules eran la forma antigua de hacer "si la URL es X, cambia Y" (redirigir, cachear todo, cambiar nivel de seguridad). Cloudflare las está reemplazando por reglas especializadas, más flexibles:
| Antes (Page Rule) | Ahora |
|---|---|
| Forwarding URL | Redirect Rules |
| Cache Level, Edge Cache TTL | Cache Rules |
| Security Level, SSL, Rocket Loader por ruta | Configuration Rules |
| Cambiar cabeceras | Transform Rules |
| Cambiar puerto o host de origen | Origin Rules |
Ejemplo de Configuration Rule: subir la seguridad y desactivar optimizaciones de JavaScript en el panel de WordPress.
txt
Cuando: (starts_with(http.request.uri.path, "/wp-admin"))
Entonces:
Security Level: High
Rocket Loader: Off
Email Obfuscation: OffSi heredas un sitio con Page Rules, funcionan; al crear nuevas, prefiere las reglas modernas.
4. Seguridad
WAF: reglas administradas y personalizadas
Ruta: Security → WAF (puede aparecer como "Security rules").
- Managed rules (gratuitas): Cloudflare incluye un conjunto de reglas contra vulnerabilidades de alto impacto (por ejemplo, fallas famosas tipo Log4Shell). Déjalas activas.
- Custom rules: el plan Free permite unas cuantas. Ejemplos útiles:
txt
Regla 1: proteger el login de WordPress fuera de México
Cuando: (http.request.uri.path eq "/wp-login.php" and ip.src.country ne "MX")
Acción: Managed Challenge (desafío administrado)
Regla 2: bloquear países a los que no vendes (solo si estás seguro)
Cuando: (ip.src.country in {"KP" "IR"})
Acción: Block
Regla 3: bloquear acceso a archivos sensibles
Cuando: (http.request.uri.path contains "/.env" or http.request.uri.path contains "/.git")
Acción: BlockAcciones disponibles: Block (bloquear), Managed Challenge (Cloudflare decide si mostrar verificación; recomendado), JS Challenge, Interactive Challenge, Skip (saltarse otras reglas, útil para tu propia IP o webhooks).
No bloquees a Google ni a tus pagos
Bloquear países o "todo lo que parezca bot" puede impedir que Googlebot indexe tu sitio o que la pasarela de pagos (Stripe, Mercado Pago, PayPal) avise de un cobro por webhook. Usa Managed Challenge en vez de Block cuando dudes, y revisa Security → Events para ver qué estás bloqueando.
Bot Fight Mode
Security → Bots → Bot Fight Mode: desafía tráfico de bots conocidos. Es gratis y útil, pero es "todo o nada": no puedes hacer excepciones. Si tienes integraciones (webhooks, apps móviles, monitoreo) que dejan de funcionar al activarlo, apágalo y usa reglas WAF más finas.
Under Attack Mode
En Overview (acciones rápidas) o Security → Settings: muestra una verificación de unos segundos a todos los visitantes. Úsalo solo durante un ataque (DDoS a nivel de aplicación, el servidor saturado); luego vuelve al nivel normal. Si lo dejas activo, rompes APIs, webhooks y empeoras la experiencia de usuarios reales.
Rate limiting (limitar frecuencia)
Security → WAF → Rate limiting rules. El plan Free incluye una regla básica, suficiente para frenar ataques de fuerza bruta al login:
txt
Nombre: Limitar login
Cuando: (http.request.uri.path eq "/wp-login.php" and http.request.method eq "POST")
Contar por: IP
Límite: 5 peticiones en 10 segundos
Acción: Block durante 10 segundosTurnstile: captcha gratis y sin "semáforos"
Turnstile reemplaza a reCAPTCHA: verifica que el visitante es humano casi siempre sin que tenga que hacer nada. Es gratis y funciona aunque tu dominio no use Cloudflare.
- En el panel de la cuenta: Turnstile → Add widget. Nombre, dominio
mitienda.com, modo Managed. - Copia la Site Key (pública) y la Secret Key (privada, va en tu servidor).
En el formulario:
html
<form action="/contacto.php" method="POST">
<input name="nombre" required>
<input name="correo" type="email" required>
<textarea name="mensaje" required></textarea>
<!-- Aquí aparece el widget de Turnstile -->
<div class="cf-turnstile" data-sitekey="TU_SITE_KEY"></div>
<button type="submit">Enviar</button>
</form>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>En el servidor, siempre valida el token (sin esto, el captcha no protege nada):
php
<?php
// contacto.php
$token = $_POST['cf-turnstile-response'] ?? '';
$ch = curl_init('https://challenges.cloudflare.com/turnstile/v0/siteverify');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => http_build_query([
'secret' => getenv('TURNSTILE_SECRET'), // nunca escribas la llave en el código
'response' => $token,
]),
]);
$respuesta = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($respuesta['success'])) {
http_response_code(400);
exit('Verificación fallida. Intenta de nuevo.');
}
// ...aquí envías el correo o guardas el mensaje
echo 'Gracias, te contactaremos pronto.';5. La IP real del visitante
El problema
Con nube naranja, quien se conecta a tu servidor es Cloudflare. En tus logs, en WordPress, en tu sistema de comentarios o en tu límite de intentos de login, todas las visitas parecen venir de IPs como 172.68.x.x. Si bloqueas "esa IP", bloqueas a medio país.
Cloudflare agrega la IP original en la cabecera CF-Connecting-IP (y también en X-Forwarded-For).
Solución en el servidor
Apache con acceso a la configuración (VPS): módulo mod_remoteip.
apache
# /etc/apache2/conf-available/cloudflare.conf
# Activar con: sudo a2enmod remoteip && sudo a2enconf cloudflare && sudo systemctl reload apache2
RemoteIPHeader CF-Connecting-IP
# Confía en la cabecera SOLO si la petición viene de Cloudflare.
# Lista completa y actualizada: https://www.cloudflare.com/ips/
RemoteIPTrustedProxy 173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22
RemoteIPTrustedProxy 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20
RemoteIPTrustedProxy 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/13
RemoteIPTrustedProxy 104.24.0.0/14 172.64.0.0/13 131.0.72.0/22En Nginx se hace con set_real_ip_from y real_ip_header CF-Connecting-IP (ya lo viste en Rendimiento y CDN).
Hosting cPanel compartido: muchos hostings ya tienen mod_remoteip configurado para Cloudflare; pregúntale a soporte. Si no, hazlo en tu código PHP, validando que la petición venga de Cloudflare:
php
<?php
// Devuelve la IP real del visitante solo si la petición vino de un rango de Cloudflare.
function ipEnRango(string $ip, string $cidr): bool {
[$red, $bits] = explode('/', $cidr);
$mascara = -1 << (32 - (int) $bits);
return (ip2long($ip) & $mascara) === (ip2long($red) & $mascara);
}
function ipVisitante(): string {
$remota = $_SERVER['REMOTE_ADDR'] ?? '';
$rangosCloudflare = ['173.245.48.0/20', '103.21.244.0/22', '141.101.64.0/18',
'108.162.192.0/18', '162.158.0.0/15', '104.16.0.0/13', '104.24.0.0/14',
'172.64.0.0/13', '188.114.96.0/20', '190.93.240.0/20', '197.234.240.0/22',
'198.41.128.0/17', '131.0.72.0/22', '103.22.200.0/22', '103.31.4.0/22'];
if (filter_var($remota, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
foreach ($rangosCloudflare as $rango) {
if (ipEnRango($remota, $rango) && !empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}
}
}
return $remota; // no vino de Cloudflare: no confíes en la cabecera
}Nunca confíes en CF-Connecting-IP sin validar
Cualquiera puede enviar una cabecera CF-Connecting-IP: 1.2.3.4 si se conecta directo a tu IP de origen. Solo es confiable cuando la conexión viene de un rango oficial de Cloudflare. En Laravel, configura el middleware de proxies de confianza con esos rangos.
6. Analíticas
- Analytics & Logs → Traffic / Tráfico: solicitudes totales, porcentaje servido desde caché, ancho de banda ahorrado, países, códigos de estado (¿muchos 404 o 5xx?).
- Security → Events / Eventos: qué se bloqueó o desafió, con qué regla, desde qué país e IP. Es lo primero que revisas cuando "un cliente no puede entrar".
- Web Analytics: estadísticas de visitas sin cookies (respetuosas de la privacidad), gratis. Se activa con un clic si usas el proxy o con un pequeño script si no.
Las analíticas de Cloudflare cuentan peticiones, no personas: no las compares 1 a 1 con Google Analytics.
7. Cloudflare Pages y Workers (introducción)
- Pages: hosting gratuito para sitios estáticos y frameworks (Astro, Vite, VitePress, Next.js con adaptador). Conectas tu repositorio de GitHub, defines el comando de compilación y la carpeta de salida, y cada
git pushpublica. Luego en Custom domains agregasmitienda.comy Cloudflare crea los registros solo (porque ya administra tu DNS). - Workers: código JavaScript que corre en los servidores de Cloudflare, antes de llegar a tu origen. Sirve para APIs pequeñas, redirecciones complejas, pruebas A/B, cabeceras. El plan gratuito incluye un número generoso de peticiones diarias.
Un Worker mínimo:
js
// Responde directamente desde el borde de Cloudflare, sin servidor propio
export default {
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === '/salud') {
return new Response('ok', { status: 200 });
}
return fetch(request); // todo lo demás pasa al origen normal
},
};8. Tokens de API
Si automatizas (Certbot con DNS-01, scripts de despliegue, Terraform), no uses la Global API Key: da acceso total a toda tu cuenta. Crea un API Token con permisos mínimos:
- Ícono de perfil → My Profile → API Tokens → Create Token.
- Plantilla Edit zone DNS.
- Zone Resources: Include → Specific zone →
mitienda.com. - Opcional: filtrar por IP del servidor y fecha de expiración.
- Copia el token (se muestra una vez).
bash
export CF_TOKEN="pega_aqui_tu_token"
# Verificar que el token funciona
curl -s https://api.cloudflare.com/client/v4/user/tokens/verify \
-H "Authorization: Bearer $CF_TOKEN"
# Obtener el ID de la zona
curl -s "https://api.cloudflare.com/client/v4/zones?name=mitienda.com" \
-H "Authorization: Bearer $CF_TOKEN"
# Crear un registro A proxeado (ttl 1 = automático)
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/json" \
--data '{"type":"A","name":"app","content":"198.51.100.20","proxied":true,"ttl":1}'9. DNSSEC con un clic
DNSSEC firma digitalmente las respuestas DNS para que nadie pueda falsificarlas en el camino (envenenamiento de caché). Es como un sello holográfico en cada respuesta.
- Cloudflare: DNS → Settings → DNSSEC → Enable.
- Te muestra un registro DS (Key Tag, Algoritmo 13, Digest Type 2, Digest).
- Cópialo en el registrador (sección DNSSEC). Si tu dominio está registrado en Cloudflare Registrar, se hace solo.
- En unas horas el estado cambia a Success / Activo.
Antes de cambiar de nameservers, quita DNSSEC
Si mueves el dominio fuera de Cloudflare con el DS todavía en el registrador, el dominio deja de resolver en todos los resolvers que validan (Google, Cloudflare, muchos ISP). Orden correcto: quitar DS en registrador → esperar → desactivar DNSSEC en Cloudflare → cambiar nameservers.
10. Cloudflare Registrar
Cloudflare también vende y renueva dominios a precio de costo (lo que cobra el registro del TLD, sin margen), sin "precio de promoción el primer año" ni cargos ocultos.
- Requisito: el dominio debe usar los nameservers de Cloudflare.
- No soporta todas las terminaciones (por ejemplo,
.mxno está disponible: se queda en Akky u otro registrador acreditado). - Para transferir: desbloquea el dominio en el registrador actual, pide el código de autorización (EPP), y en Cloudflare ve a Domain Registration → Transfer Domains. Se cobra un año de renovación (que se suma al tiempo que ya tenías).
- Un dominio no puede transferirse en los 60 días siguientes a su registro o a una transferencia previa.
11. Zero Trust y túneles (introducción)
Cloudflare Tunnel (cloudflared) conecta tu servidor con Cloudflare desde dentro hacia fuera: no necesitas abrir puertos ni exponer la IP. Ideal para un servidor en casa, una Raspberry Pi o un VPS donde quieres cerrar todo el firewall.
bash
# Prueba rápida (sin cuenta): publica tu localhost:3000 en una URL temporal *.trycloudflare.com
cloudflared tunnel --url http://localhost:3000Para algo permanente, crea el túnel desde el panel (Zero Trust → Networks → Tunnels → Create a tunnel): te da un comando con un token para instalar cloudflared como servicio, y ahí mismo asignas app.mitienda.com → http://localhost:3000. Cloudflare crea el registro DNS (un CNAME a <id>.cfargotunnel.com) automáticamente.
Cloudflare Access (también en Zero Trust) pone una puerta de inicio de sesión delante de una URL: por ejemplo, admin.mitienda.com solo abre para correos @mitienda.com que reciban un código. El plan gratuito cubre equipos pequeños.
Ejercicios
- Después de un despliegue,
https://mitienda.com/css/estilos.csssigue mostrando la versión anterior. Escribe el comando para comprobar si viene de caché y explica cómo purgar solo ese archivo. - Escribe la Redirect Rule para mandar
tienda-vieja.mxywww.tienda-vieja.mxahttps://mitienda.comconservando la ruta. ¿Qué registros DNS necesitatienda-vieja.mx? - Tu formulario de contacto recibe 200 mensajes de spam al día. Propón dos medidas con Cloudflare (gratis).
- En los logs de WordPress todos los intentos de login vienen de
172.70.x.xy un plugin de seguridad bloqueó esa IP: ahora nadie puede iniciar sesión. ¿Qué pasó y cómo se corrige? - Vas a mudar
mitienda.comde Cloudflare a otro proveedor DNS y tiene DNSSEC activo. Escribe el orden de pasos.
Ver solución
bash
curl -sI https://mitienda.com/css/estilos.css | grep -i cf-cache-statusSi dice HIT, viene de caché. Caching → Configuration → Purge Cache → Custom Purge y pega https://mitienda.com/css/estilos.css. A futuro, usa nombres con versión (estilos.v2.css o estilos.css?v=2).
- DNS de
tienda-vieja.mx(zona propia en Cloudflare):
txt
AAAA @ 100:: Proxied
AAAA www 100:: ProxiedRegla:
txt
Cuando: (http.host eq "tienda-vieja.mx" or http.host eq "www.tienda-vieja.mx")
URL dinámica: concat("https://mitienda.com", http.request.uri.path)
Código 301, conservar query string- Agregar Turnstile al formulario (y validar el token en el servidor) y una rate limiting rule para
POSTa/contacto.php. Opcional: regla WAF con Managed Challenge para países de donde no esperas clientes. - Con el proxy, el servidor ve la IP de Cloudflare y no la del visitante; el plugin bloqueó un nodo de Cloudflare. Desbloquea esa IP y restaura la IP real:
mod_remoteipconCF-Connecting-IPy los rangos oficiales (o pide al hosting que lo active), o configura el plugin para leerCF-Connecting-IPsolo desde rangos de Cloudflare. - (1) Quitar el registro DS en el registrador. (2) Esperar a que expire (al menos el TTL del DS; en la práctica 24–48 h). (3) Desactivar DNSSEC en Cloudflare. (4) Copiar la zona al nuevo proveedor. (5) Cambiar nameservers. (6) Cuando esté estable, activar DNSSEC en el nuevo proveedor y poner su DS en el registrador.
Resumen
- Cloudflare cachea estáticos por defecto, no HTML. Mira
cf-cache-status, purga por URL y usa Cache Rules con cuidado. - Redirect Rules resuelven www→raíz y dominio viejo→nuevo sin tocar el servidor (requieren nube naranja).
- Page Rules son legado: usa Redirect, Cache, Configuration, Transform y Origin Rules.
- Seguridad gratis: WAF administrado + reglas propias, Bot Fight Mode, Under Attack solo en emergencias, rate limiting y Turnstile.
- Restaura la IP real con CF-Connecting-IP, validando que venga de Cloudflare.
- Pages/Workers para sitios y código en el borde; API Tokens con permisos mínimos.
- DNSSEC en un clic (y quítalo antes de mudarte); Registrar a precio de costo; Tunnel sin abrir puertos.
- Siguiente: Escenarios reales.