01 / Problema
Un alquiler de campers vende sobre todo a turistas extranjeros que comparan desde el móvil, a veces desde el extranjero y con prisa. Necesita mostrar disponibilidad real, cobrar sin intermediarios y aparecer en varios idiomas. Y la auditoría móvil de agosto de 2026 mostró que la imagen principal tardaba casi cinco segundos en pintarse, no por su peso sino por esperas evitables.
02 / Contexto
Contexto
La auditoría separó el problema en partes medibles. El 65 % del retraso era tiempo esperando a que la imagen principal empezara a cargarse, porque el elemento no existía hasta que la página se hidrataba. A eso se sumaban dos contenedores de analítica en paralelo, una redirección de la raíz al español que costaba unos 700 ms e imágenes enlazadas desde dominios ajenos.
Ver el servicio de diseño web ↗ 03 / Solución
Cómo lo resolvimos
La web está construida con Next.js sobre Supabase y Stripe. La disponibilidad se lee de una vista diaria por vehículo y se sincroniza con calendarios externos por iCal. El precio se recalcula en el servidor antes de crear el pago, y la base de datos impide por sí misma que dos reservas se solapen. Tras la auditoría se corrigieron en el código las causas encontradas, una por una.
Flujo
- 01
Restricción de exclusión en Postgres para que dos reservas no puedan solaparse
- 02
Precio recalculado en el servidor, sin fiarse del total que envía el navegador
- 03
Checkout con clave de idempotencia y webhook con firma verificada
- 04
Hero renderizado en el servidor con prioridad alta en lugar de esperar a la hidratación
- 05
Hreflang por idioma con x-default y sitemap con fecha de modificación real
Ejes del trabajo
Reservas que no se pisan
El overbooking se evita en la base de datos, no en la interfaz. Aunque dos pagos lleguen a la vez, Postgres rechaza el segundo.
- Rangos de fechas con restricción de exclusión
- Sincronización iCal idempotente con otros canales
- Reservas pendientes de pago reutilizadas, no duplicadas
Rendimiento atacado por causas
Cada corrección responde a un hallazgo medido de la auditoría, no a una lista genérica de buenas prácticas.
- Un solo contenedor de analítica en lugar de dos
- Reescritura en la raíz en lugar de redirección 307
- Polyfills antiguos eliminados con una lista de navegadores actual
Internacional de verdad
El público llega en muchos idiomas, así que las rutas, los vehículos y las páginas de destino tienen versión propia y hreflang recíproco.
- Nueve idiomas en la interfaz
- Datos estructurados de empresa de alquiler, rutas y preguntas frecuentes
04 / Alcance
Qué hace
- Muestra la disponibilidad de cada camper con días bloqueados en el calendario
- Calcula el precio con extras y cobra con Stripe Checkout
- Sincroniza reservas de otros canales mediante iCal
- Publica rutas por Andalucía y páginas de vehículo en varios idiomas
- Ofrece contacto directo por WhatsApp desde cada vehículo
05 / Resultados
Lo que cambió
La imagen principal ya se renderiza en el servidor con prioridad alta, queda un único contenedor de analítica cargado de forma diferida, la raíz se resuelve con una reescritura en vez de una redirección y todas las imágenes se sirven desde el propio dominio. Falta volver a medir con la misma metodología para publicar las cifras de después; cuando estén, aparecerán aquí con su fecha.
Seguridad y confidencialidad
- Seguridad a nivel de fila en las tablas de Supabase.
- El webhook de Stripe verifica la firma de cada evento antes de confirmar una reserva.
- Las imágenes de terceros se sustituyeron por imágenes propias alojadas en el dominio.
06 / Preguntas
Preguntas frecuentes
¿Por qué no se publican todavía las cifras de rendimiento?
Porque solo publicamos números medidos con método y fecha. Las correcciones están en el código; la nueva medición con PageSpeed Insights, con mediana de varias ejecuciones, se publicará en esta ficha cuando esté hecha.
¿Por qué el problema no era el peso de la imagen?
La descarga de la imagen tardaba unos 126 ms. El tiempo se iba en esperar: casi 1,8 segundos hasta empezar a cargarla, porque el elemento no existía en el HTML inicial, y otros 0,8 segundos hasta poder pintarla.