Con la Pascua encima y los huevos de pascua en el ambiente, me ha parecido el momento perfecto para hablar de los easter eggs que escondí en las páginas de error de este blog. Porque si alguien va a acabar en un 404, que al menos sea una experiencia memorable.
El problema con las páginas de error genéricas
La mayoría de sitios tienen una de dos cosas: la página de error por defecto de Nginx (ese blanco con "404 Not Found" centrado en Times New Roman), o una página que carga el header, el footer, el menú de navegación y todo el stack PHP... justo cuando algo ha fallado.
El segundo caso tiene un problema serio: si el servidor está en las últimas con un 500, el intento de cargar el header PHP puede fallar también. Acabas con una página de error rota intentando mostrar que hay un error. El clásico error dentro del error.
Mi solución: páginas de error completamente estáticas, HTML puro, sin PHP, sin dependencias externas críticas. Nginx las sirve directamente, aunque el proceso PHP-FPM esté caído.
El concepto: una sesión de terminal
La idea era que cada página de error pareciera una sesión real de terminal donde algo ha salido mal. No solo visualmente, sino narrativamente. Cada una tiene su propio "personaje":
- 404 —
ENOENT: No such file or directory. El clásico. Rojo. - 403 —
EACCES: Permission denied. Con unls -lamostrando----------y un diagrama de permisos octal. Púrpura. - 500 —
SIGSEGV: Segmentation fault. Kernel panic, stack trace de PHP-FPM, dirección de memoria0xdeadbeefcafebabe. Naranja. - 503 —
systemctl status php8.4-fpmmostrandofailed. Con el log de Nginx quejándose del socket. Amarillo.
Cada una usa un color de acento diferente para que visualmente sepas de un vistazo qué tipo de error es. Y todas comparten la misma estética: fondo negro, tipografía JetBrains Mono, scanlines sutiles, viñeta, barra de estado estilo Vim en el footer.
El glitch
El detalle que más trabajo me dio, y el más visible, es el efecto glitch en el número de error grande.
La primera aproximación fallaba porque el elemento tenía animation: fadeSlide forwards y el forwards congela el estado final, bloqueando cualquier animación posterior que intentas aplicar con JavaScript. La solución fue separar las responsabilidades: el div exterior gestiona solo el fade de entrada, y dentro hay un <span class="glitch-inner"> completamente independiente que es donde vive el glitch.
El truco real del glitch son los pseudoelementos ::before y ::after. Usando content: attr(data-text) se crean dos copias del texto, una en cián y otra en rojo. Con clip-path: inset() se recortan en franjas horizontales y se desplazan en horizontal con transform: translate(). El resultado son las franjas de color características de un monitor CRT roto.
.glitch-inner::before,
.glitch-inner::after {
content: attr(data-text);
position: absolute;
top: 0;
left: 0;
width: 100%;
overflow: hidden;
pointer-events: none;
opacity: 0;
}
.glitch-inner::before {
color: #5dd8d8;
} /* cián */
.glitch-inner::after {
color: #ff5f5f;
} /* rojo */La animación usa steps(1) en lugar de interpolación suave, que da ese efecto de saltos bruscos tan característico. El JavaScript lo dispara automáticamente cada ~4 segundos con una variación aleatoria de ±1 segundo para que no parezca mecánico.
Los easter eggs
Aquí está la parte de Pascua. Cada página tiene un easter egg diferente activado escribiendo una palabra clave en cualquier momento (sin que el foco esté en el campo de búsqueda):
- 404 — Escribe
sudo→ "jaume is not in the sudoers file. This incident will be reported." - 500 — Escribe
reboot→ Broadcast message de root anunciando el reinicio del sistema. - 503 — Escribe
systemctl→ Te pide autenticación para reiniciar php-fpm, conhunter2oculto en el campo de contraseña. - 403 — Escribe
chmod→ "chmod: changing permissions: Operation not permitted. No tienes sudo. Nadie te lo va a dar."
Efecto glitch automático cada ~4 segundos
El código es sencillo — escucha keydown globalmente y va acumulando los últimos N caracteres en un string, comprobando si termina con la palabra clave:
let typed = "";
document.addEventListener("keydown", (e) => {
if (e.target.tagName === "INPUT") return;
typed = (typed + e.key).slice(-10);
if (typed.endsWith("sudo")) {
// mostrar el easter egg
typed = "";
}
});Detalles que no se ven a primera vista
Algunos extras que quería mencionar porque no son obvios:
La barra de estado estilo Vim en el footer tiene un reloj en tiempo real. El modo cambia según la página: NORMAL en la 404, PANIC en la 500, MAINT en la 503, DENIED en la 403.
El timestamp y el x-request-id de la 404 son reales y se generan en JavaScript al cargar la página. El request ID es un hex aleatorio de 8 bytes generado con crypto.getRandomValues.
La IP del visitante en el log de Nginx de la 503 es aleatoria pero verosímil, generada en JS. Pequeño detalle que hace que el log parezca real.
El archivo .secrets en el ls -la de la 403 existe en el listado con permisos r-------- y el comentario "sí, existe. no, no puedes leerlo". Si alguien lo ve, que sepa que es broma... o no.
Configuración en Nginx
La parte técnica de servir estas páginas es sencilla. Lo importante es el internal — evita que las páginas de error sean accesibles directamente como URLs normales, solo Nginx las sirve cuando se produce el error correspondiente:
# Error pages
error_page 403 /pages/error/403.html;
error_page 404 /pages/error/404.html;
error_page 500 502 /pages/error/500.html;
error_page 503 504 /pages/error/503.html;
location = /pages/error/403.html { internal; }
location = /pages/error/404.html { internal; }
location = /pages/error/500.html { internal; }
location = /pages/error/503.html { internal; }Los archivos son HTML estático puro. Sin PHP, sin dependencias de servidor. La única dependencia externa es la fuente JetBrains Mono de Google Fonts — que si quisiera ser purista podría autoalojar, pero tampoco es crítica para una página de error.
El resultado
Cuatro páginas de error que no dan vergüenza. Que si alguien acaba en un 404, probablemente se ría un momento antes de volver al inicio. Que funcionan aunque el servidor esté en las últimas. Y que tienen suficientes detalles ocultos para que un sysadmin o desarrollador que las encuentre pierda cinco minutos explorando.
A veces la diferencia entre un sitio cuidado y uno descuidado está en los detalles que casi nadie ve. Las páginas de error son uno de esos sitios donde la mayoría no pone esfuerzo. Y precisamente por eso merece la pena ponerlo.
Feliz Pascua. Y si encuentras los huevos, ya sabes dónde escribirme.
# co-authored-by: Claude (Anthropic) — no aparece en el git log pero debería
¿Te ha sido útil?
Ayúdame a mejorar con tu puntuación y comentarios.
Comentarios
Los comentarios están gestionados por GitHub Discussions. Necesitas una cuenta de GitHub para participar. ¡Es gratis y rápido!