tawasuyu · Hacer
Tu WordPress, sin WordPress: el mismo sitio al píxel, sin una sola línea de PHP
Un sitio hecho con WordPress es un programa que corre cada vez que alguien lo visita, con su base de datos, sus plugins y sus puertas de administración abiertas a internet. qhatu se lo lleva entero, igual al píxel, y lo deja sin nada que ejecutar: el mismo sitio, más rápido, y sin una sola línea de PHP.
8 min de lecturaPor Redacción Gioser
Cada vez que alguien abre una página de un sitio WordPress, en el servidor arranca un programa: lee la base de datos, carga el tema, despierta cada plugin, arma el HTML y lo manda. Lo mismo, una y otra vez, para un texto que cambia una vez por mes. Y mientras tanto, ese programa deja abiertas a todo internet las puertas por donde se administra. Este artículo cuenta cómo qhatu —el gestor de contenidos de tawasuyu— se lleva un sitio WordPress real entero, lo deja igual al píxel, y le quita todo lo que se podía atacar.
El sitio que se arma en cada visita
WordPress es la forma más común de tener un sitio, y hay buenas razones: se instala en una tarde, hay un tema para todo, y un constructor como Elementor deja diseñar sin escribir código. El precio se paga después, y en cuotas. Cada plugin es código ajeno que corre con todos los permisos del sitio; cada actualización que se posterga es una puerta; y la base de datos, el login y la API quedan escuchando las veinticuatro horas, aunque el sitio lo edite una persona dos veces por mes.
El sitio con el que se probó todo esto es el de un cliente real: un centro de retiros hecho con WordPress, el tema WoodMart y Elementor Pro, con 64 páginas, una galería, un blog y dos formularios de contacto. Un sitio común, del tipo que hay a millones. El pedido fue simple: que quede igualito, y que se pueda seguir editando fácil.
Un comando
Lo que el primer día se hizo a mano —bajar el sitio renderizado, limpiar lo que WordPress agrega a cada nombre de archivo, reapuntar los formularios— hoy es un verbo:
qhatu absorb-wordpress https://el-sitio.com/ carpeta
Lee los mapas del sitio que publica WordPress, baja cada página y todo lo que necesita —hojas de estilo, scripts, imágenes en todos sus tamaños, fuentes, íconos—, y deja cada referencia apuntando a la copia local. No baja lo que no es contenido: el carrito, el panel de administración, los feeds, las búsquedas. Si una dirección vieja redirige a otra, no la duplica: la anota como redirección para el servidor, así un enlace que alguien compartió hace años sigue llegando. Sobre el sitio del cliente:
| qué | medido |
|---|---|
| páginas | 64 |
| recursos (hojas, scripts, imágenes, fuentes) | 448, 45 MB |
| tiempo, con una pausa de cortesía entre pedido y pedido | 6 minutos |
| referencias rotas en la copia | 0 |
Igual al píxel, y con brazo de control
«Igualito» se puede medir. Se abrió la misma página del original y de la copia en Chrome, a 1366 por 2400 píxeles, y se compararon las dos capturas punto por punto. En la portada y en una entrada del blog, 0 píxeles distintos de 3.278.400. Para que ese cero signifique algo, la prueba lleva brazo de control: cada captura tiene 216.987 colores distintos, así que no se están comparando dos páginas en blanco.
Y la honestidad incluye lo que no dio cero. En una tercera página aparecieron 1.785 píxeles distintos en la franja del menú. Antes de culpar a la copia se comparó el original consigo mismo, dos veces: dio 1.642 y 0. Es la propia página, que no se pinta igual dos veces seguidas.
Nada que hackear
Un escáner de WordPress empieza siempre igual: pregunta por las direcciones que todo WordPress tiene. Se hizo esa misma pregunta a los dos sitios, en modo pasivo —sólo pedidos de lectura a direcciones públicas, sin probar una sola contraseña—:
| dirección | qué es | WordPress | copia |
|---|---|---|---|
/wp-login.php | la puerta del login | contesta | 404 |
/wp-admin/ | el panel | contesta | 404 |
/xmlrpc.php | la puerta clásica de la fuerza bruta en lote | contesta | 404 |
/wp-json/ | la API | contesta | 404 |
/wp-json/wp/v2/users | la lista de quienes pueden entrar | contesta, con 3 nombres | 404 |
/?author=1 | el nombre de usuario del primer autor | lo dice | no |
/readme.html | la versión exacta | contesta | 404 |
/wp-admin/install.php | el instalador | contesta | 404 |
/wp-cron.php | tareas que dispara cualquiera | contesta | 404 |
Nueve de nueve contra cero de nueve. Además, el original anunciaba en su propio código la versión exacta de WordPress y de dos de sus plugins: exactamente lo que un atacante busca para cruzar con una lista de fallas conocidas. No es que la copia tenga esas puertas mejor cerradas: no las tiene. No hay PHP que ejecutar, ni base de datos que consultar, ni usuarios que adivinar.
Midiendo esto apareció un detalle que vale contar. La copia todavía decía «WordPress» en una etiqueta invisible del HTML, heredada del original: un escáner la leía y anunciaba un WordPress que ya no existía. El absorbedor ahora la saca.
¿Y los formularios de contacto, que en WordPress son un plugin más? En la copia los atiende una función WebAssembly de 205 KB que corre en el servidor con capacidades declaradas: puede guardar lo que llega y leer el reloj, y nada más. No puede leer el disco ni salir a la red; lo que no pidió no existe para ella. Los mensajes se leen en el panel, y bajan como planilla.
Más rápido, y medido sin trampa
Cinco páginas, cinco cargas en frío de cada lado —un navegador nuevo por carga, sin caché—, alternando original y copia para que una mala racha de la red no caiga entera de un lado. Las medianas:
| página | primer byte | lo más grande pintado | carga completa |
|---|---|---|---|
| portada | 2,5 s → 0,9 s | 6,1 s → 4,9 s | 9,9 s → 5,9 s |
| página de servicio | 1,9 s → 0,8 s | 6,5 s → 3,8 s | 14,9 s → 5,4 s |
| página larga con galería | 2,3 s → 0,9 s | 11,7 s → 6,3 s | 22,3 s → 7,9 s |
| listado del blog | 2,4 s → 0,7 s | 4,5 s → 4,1 s | 5,1 s → 5,3 s |
| una entrada | 2,3 s → 0,8 s | 5,9 s → 2,5 s | 7,0 s → 5,2 s |
Los bytes que viajan son prácticamente los mismos: es la misma página. Lo que cambia es el servidor. Y hay una salvedad que un artículo honesto tiene que hacer: los dos sitios están en servidores distintos, en lugares distintos, y la copia quedaba más cerca de donde se midió. Así que se separó la red del software. Abrir la conexión cuesta 392 milisegundos hasta el original y 179 hasta la copia; eso es distancia, no código. Descontando una ida y vuelta de cada lado, lo que queda es el trabajo de armar la página:
Y se sigue editando, en el lugar
Una copia estática que no se puede tocar sería un museo. La de qhatu se edita desde un panel que vive en su propia dirección —nada del sitio público lo enlaza—, sobre la página misma, tal como se ve:
- Clic en un texto y se escribe. Una barra ofrece negrita, cursiva, enlaces, listas, títulos y citas, y los colores son los del sitio, no un selector cualquiera. Pegar desde Word o Google Docs deja el formato y tira la basura.
- Clic en una imagen y se reemplaza. Elegir varias de una vez agrega fotos a una galería: cada una de más duplica el ítem y le pone la suya.
- Duplicar, mover y borrar cualquier bloque: una tarjeta, una sección, un ítem de una lista. Y una página nueva es la copia de otra, en la dirección que se quiera.
- La paleta del sitio a un costado. Los colores que usa la página, contados; se cambia uno y se ve al instante, y un botón lo aplica a las ochenta y tantas hojas de estilo que dejó Elementor. Si un texto deja de leerse sobre su fondo, el panel lo dice con la medida de contraste en la mano.
- Publicar es un acto firmado. Guardar cambia la carpeta; publicar convierte el sitio en una versión nueva, firmada, que el servidor sirve entera o no sirve. Volver a la anterior es un botón. Y cada guardado deja además la versión anterior de la página, por si acaso.
Todo eso corre en saywa, el servidor web de tawasuyu. En la máquina donde vive este sitio, saywa y la forja propia del proyecto ya reemplazaron a cuatro programas: el servidor web, la forja de git, el proxy y el intérprete de PHP. Alguien escribió las dos piezas desde cero, en Rust, con una regla que se repite en todo el proyecto: lo que no se midió no se afirma.
Lo que todavía no
- El absorbedor no ejecuta JavaScript. Baja lo que el servidor manda; un sitio que arma su contenido recién en el navegador queda a medias. El de este artículo, como casi todo WordPress, manda el HTML ya armado.
- No hay tienda todavía. El carrito y el pago de WooCommerce no se absorben: son programas, no páginas. La tienda propia de qhatu —sin base de datos, con el precio recalculado en el servidor— está en el plan y es lo próximo para este mismo cliente.
- Ni comentarios, ni búsqueda, ni áreas con usuarios. Un sitio de membresías no se absorbe así.
- Editar no es diseñar desde cero. Hoy se modifica lo que el sitio ya tiene; los bloques propios —tarjetas, carruseles, galerías— y los temas por valores, sin código, están escritos en el plan y sin construir.
- Las tipografías no se cambian desde la paleta, sólo los colores.
- Los formularios no avisan por correo. Los mensajes llegan al panel; mandar un aviso pide que las funciones puedan salir a la red, y esa capacidad todavía no existe.
- Volver atrás cambia el sitio, no la carpeta. Si después se publica sin tocar nada, vuelve lo deshecho; para recuperar una página está la versión guardada.
- Los números de velocidad son de un día y desde un lugar. Por eso va la separación de la red: es la parte que no depende de dónde se mida.
Cómo verlo
qhatu vive en el repositorio de tawasuyu —git.tawasuyu.net, en un servidor propio— bajo 02_ruway/qhatu, y saywa bajo 03_ukupacha/saywa. Los dos instrumentos de este artículo, el de exposición y el de velocidad, están en scripts/qhatu-careo/: cualquiera puede correrlos contra su propio WordPress y contra su copia, y comparar.