Blog
Cómo reconstruimos editandoideas.com: estático, bilingüe y sin scripts en línea.

En resumen. Este sitio es estático: cada URL se genera como HTML durante el build con React Router, y no hay servidor de aplicación ni formularios. Todo sale de un solo mapa de rutas —prerender, hreflang, sitemap, selector de idioma y breadcrumbs—, las reglas editoriales se verifican con pruebas automáticas y la política de seguridad de contenido no admite scripts en línea. Aquí contamos por qué y qué haríamos distinto.
Por qué un sitio estático
El sitio no recibe datos: no tiene formularios, cuentas ni base de datos. El contacto es por correo y WhatsApp, y la calculadora de proyectos funciona entera en el navegador. Con esas condiciones, un servidor de aplicación solo añadiría superficie de ataque y una pieza más que mantener.
Por eso cada una de las URL se pre-renderiza durante el build: React Router 8 con Vite y React 19, en modo sin servidor, genera un HTML completo por página y por idioma. Un buscador recibe el contenido entero en la primera respuesta, sin depender de que se ejecute JavaScript, y el hosting solo tiene que servir archivos.
Un solo mapa de rutas
La decisión que más trabajo nos ha ahorrado es tener una única fuente de verdad para las URL. Un archivo declara cada página y su ruta en español y en inglés, y de ahí sale todo lo demás:
- La lista de páginas que se pre-renderizan.
- Los enlaces hreflang recíprocos entre idiomas y el x-default.
- El sitemap.xml, con la fecha real de la última modificación de cada página.
- El selector de idioma, que lleva a la página equivalente y no a la portada.
- Los breadcrumbs y sus datos estructurados.
- Los enlaces internos, que se escriben por identificador de página y nunca como texto literal.
Las rutas se comprueban, no se revisan
React Router necesita su propia declaración de rutas, así que hay dos listas que describen lo mismo. En lugar de confiar en que alguien las mantenga iguales, una prueba compara ambas y hace fallar el build si se separan. El síntoma que evita es el peor posible: una URL que aparece en el menú y devuelve 404 en producción.
Lo mismo ocurre con los redirects del sitio anterior: una prueba verifica que cada destino exista. Cuando reorganizamos los servicios en Desarrollo, Diseño y Consultoría, las URL viejas siguieron llevando a su equivalente en un solo salto.
Dos idiomas sin contaminar el prerender
El español vive en la raíz y el inglés bajo /en. El idioma se deduce siempre de la URL, nunca del navegador: una dirección compartida tiene que mostrar lo mismo a todo el mundo.
Hay un detalle menos evidente. Durante el build se renderizan todas las páginas en el mismo proceso, y una configuración de idioma global y mutable basta para que una página salga en el idioma equivocado si el orden de render cambia. Por eso usamos una instancia fija por idioma en lugar de cambiar el idioma al vuelo: el error se vuelve imposible por construcción.
Una política de seguridad sin excepciones
La política de seguridad de contenido del sitio solo permite scripts servidos desde el propio dominio, más el cargador de analítica. Sin unsafe-inline.
El problema es que React Router incrusta el arranque de la aplicación como scripts en línea, y la política los bloquea. Hubo una versión que se publicó así: el HTML era correcto, pero no se ejecutaba nada de JavaScript, así que el menú móvil y la calculadora no respondían. La solución fue un paso posterior al build que extrae cada script en línea a un archivo propio y detiene el build si queda alguno. Preferimos un build en rojo a un despliegue que parece bien y no funciona.
La analítica sigue la misma lógica: no se carga hasta que el visitante la acepta en el aviso de cookies.
Reglas editoriales que se prueban
Una regla escrita en un documento se incumple a la tercera semana; una regla que hace fallar el build, no. Por eso las reglas de contenido también son pruebas automáticas, que hoy suman más de 350 junto con las técnicas:
- Las dos traducciones tienen exactamente las mismas claves: no puede faltar media sección en inglés.
- El español es neutro: una lista de términos regionales hace fallar la prueba si aparece alguno.
- Ninguna cadena queda vacía ni con texto de relleno.
- Las frases del sitio anterior que ya no representan a la empresa no pueden volver como posicionamiento.
- Los clientes solo se nombran con permiso explícito.
Apache y los detalles que nadie ve
El sitio se publica en un hosting con Apache mediante rsync, y el despliegue reintenta porque la conexión con el servidor se corta de vez en cuando. La configuración de Apache no se escribe a mano: se genera en cada build a partir de la lista de redirects, igual que el sitemap sale del mapa de rutas.
Esa configuración resuelve cosas pequeñas con mucho efecto: una sola forma de cada URL, sin barra final; el dominio sin www redirigido a www; y una página 404 que devuelve un 404 real, no una página vacía con estado 200 que le diría a Google que cualquier dirección existe.
Lo que haríamos igual y lo que cambiaríamos
Repetiríamos el mapa de rutas único y convertir cada regla en una prueba: son las dos decisiones que hacen que el sitio aguante cambios frecuentes sin romperse.
Cambiaríamos dos cosas. La fecha de modificación del sitemap empezó siendo la del build, así que todas las páginas decían haber cambiado en cada despliegue; ahora cada página toma la fecha del último cambio real de su contenido, y debió ser así desde el principio. Y habríamos guardado una medición de rendimiento el primer día, para poder comparar cada cambio con un punto de partida.