Rendimiento web desde el HTML
En esta lección
- Cómo carga una página
- El peso de la página
- Imágenes: el mayor ahorro
- Scripts: defer y async
- Pistas de carga: preconnect y preload
- Fuentes sin texto invisible
- Core Web Vitals: cómo mide Google la experiencia
- Medir: Lighthouse y PageSpeed Insights
- Un <head> bien ordenado para el rendimiento
- Errores frecuentes
- Resumen
Una web lenta pierde visitas: si la página tarda varios segundos en mostrar algo, mucha gente se va antes de verla, sobre todo en el móvil y con mala cobertura. El rendimiento web (web performance) es todo lo que haces para que la página llegue antes, se vea antes y responda rápido. En esta lección verás qué hace que una página pese, qué atributos de HTML ayudan al navegador a cargar lo importante primero, cómo mide Google la experiencia (las Core Web Vitals) y cómo analizar tu web con Lighthouse.
Mucho de esto se decide en el HTML, antes de escribir una sola línea de JavaScript.
Cómo carga una página
Recuerda lo que viste en cómo funciona la web: el navegador pide el HTML, lo lee de arriba abajo y, cada vez que encuentra un recurso (CSS, imagen, script, fuente), lo pide también. Con todo eso construye la página y la pinta en pantalla.
Tres ideas que debes tener claras:
- El CSS bloquea el pintado. El navegador no muestra nada hasta tener el CSS del
<head>, porque no quiere enseñarte la página sin estilos y luego cambiarla. - Un
<script>normal bloquea la lectura del HTML. Al encontrarlo, el navegador para, lo descarga, lo ejecuta y solo después sigue leyendo. - Cada recurso es una petición. Si el recurso está en otro dominio, antes de descargarlo hay que buscar la dirección del servidor (DNS) y abrir una conexión segura. Eso cuesta tiempo aunque el archivo sea pequeño.
Todo lo que el navegador necesita para mostrar lo primero que ves se llama ruta crítica de renderizado (critical rendering path). La regla general del rendimiento es: que la ruta crítica sea corta y ligera, y que lo demás espere.
El peso de la página
El peso de una página es la suma de todo lo que se descarga: HTML, CSS, JavaScript, imágenes, fuentes, vídeos. Puedes verlo en las DevTools, pestaña Red (Network), abajo del todo: número de peticiones, kilobytes transferidos y tiempo de carga. Lo viste en editor y DevTools.
En una web normal, lo que más pesa suele ser, por este orden:
- Imágenes. Una sola foto sacada del móvil puede pesar 4 MB.
- JavaScript. No solo pesa: además hay que ejecutarlo, y en un móvil modesto eso tarda.
- Fuentes web. Cada grosor y estilo es un archivo.
- CSS y HTML, que normalmente son lo más ligero.
Algunas medidas sencillas que reducen el peso sin tocar el diseño:
- Redimensiona y comprime las imágenes antes de subirlas. Una foto de portada rara vez necesita más de 1600 píxeles de ancho.
- Usa formatos modernos: AVIF o WebP para fotos, SVG para logos e iconos.
- No cargues librerías enteras para usar una función. Si solo necesitas un carrusel sencillo, quizá baste con CSS.
- Limita las fuentes web: dos familias y pocos grosores. Usa el formato WOFF2, que es el más comprimido.
- Minifica el CSS y el JavaScript (quitar espacios y comentarios). Las herramientas de construcción como Vite lo hacen solas.
- Activa la compresión en el servidor (gzip o Brotli). La mayoría de hostings ya la tienen activada para HTML, CSS y JS.
Consejo: en la pestaña Red puedes simular una conexión lenta (menú “Sin limitación” → “4G lenta”). Prueba así tu web: es como la ve mucha gente en el móvil.
Imágenes: el mayor ahorro
Todo lo relativo a imágenes lo tienes explicado en imágenes responsive. Aquí va el resumen, porque es lo que más mejora el rendimiento:
<!-- Imagen principal, visible al abrir la página: que llegue cuanto antes -->
<img src="portada.avif" width="1600" height="700"
fetchpriority="high" alt="Sala de bloque con presas de colores">
<!-- Imágenes de más abajo: que esperen a que el usuario se acerque -->
<img src="taller-nudos.webp" width="600" height="400"
loading="lazy" decoding="async" alt="Monitora enseñando el nudo de ocho">
| Atributo | Qué hace | Dónde usarlo |
|---|---|---|
width y height | Reserva el hueco antes de que llegue la imagen | En todas las imágenes |
loading="lazy" | No descarga la imagen hasta que se acerca a la pantalla | Imágenes que no se ven al abrir la página |
fetchpriority="high" | Pide la imagen con prioridad alta | Solo la imagen principal |
decoding="async" | Decodifica sin bloquear el resto | Galerías con muchas imágenes |
srcset + sizes | Cada pantalla descarga el tamaño que necesita | Fotos que cambian de tamaño |
<picture> + type | AVIF o WebP con JPG de reserva | Fotos |
loading="lazy" también funciona en los <iframe>. Un mapa o un vídeo de YouTube incrustado pesa bastante; si está al final de la página, que espere:
<iframe src="https://www.youtube-nocookie.com/embed/ID_DEL_VIDEO"
width="560" height="315" loading="lazy"
title="Vídeo: cómo asegurar en top rope"></iframe>
Cuidado: poner
loading="lazy"en la imagen de portada es uno de los errores más comunes. El navegador espera a saber dónde queda en la página antes de pedirla, y la imagen más importante llega tarde.
Scripts: defer y async
Lo explicamos en el head y metadatos, así que solo recordamos la idea: un <script> sin atributos detiene la lectura del HTML. Con defer o async se descarga en paralelo.
<head>
<!-- Tu código: se ejecuta cuando el HTML está leído, en orden -->
<script src="js/app.js" defer></script>
<!-- Algo independiente, como unas estadísticas: se ejecuta en cuanto llega -->
<script src="https://estadisticas.example.com/contador.js" async></script>
</head>
| Forma | ¿Bloquea la lectura del HTML? | ¿Respeta el orden? |
|---|---|---|
<script src> | Sí | Sí |
<script src defer> | No | Sí |
<script src async> | No (solo un momento al ejecutarse) | No |
<script type="module"> | No (se comporta como defer) | Sí |
Además del cómo, piensa en el cuánto. Cada script de terceros (chat, mapas, anuncios, redes sociales) añade peso y trabajo. Antes de añadir uno, pregúntate si hace falta en todas las páginas o solo en una.
Pistas de carga: preconnect y preload
Las pistas de recursos (resource hints) son etiquetas <link> en el <head> que le dan al navegador información que todavía no tiene.
preconnect: abrir la conexión antes
Si vas a pedir algo a otro dominio (una fuente de Google Fonts, un CDN de imágenes), el navegador tiene que buscar el servidor y abrir la conexión. Con preconnect lo hace mientras sigue leyendo el HTML:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
El atributo crossorigin hace falta en el segundo porque los archivos de fuentes se piden siempre en modo CORS (un modo de petición entre dominios); si no coincide, el navegador abre una conexión nueva y la primera no sirve.
Úsalo con 2 o 3 dominios como máximo, los que de verdad vas a usar enseguida. Cada conexión abierta consume recursos.
Existe una versión más ligera, dns-prefetch, que solo resuelve el nombre del dominio. Sirve para dominios que se usarán más tarde:
<link rel="dns-prefetch" href="https://mapas.example.com">
preload: descargar ya algo que se descubre tarde
Algunos recursos importantes el navegador los descubre tarde. El caso típico son las fuentes: están dentro del CSS, así que el navegador primero descarga el CSS, lo lee, ve que hay un texto con esa fuente y solo entonces la pide. Con preload la pide desde el principio:
<link rel="preload" href="/fuentes/inter-variable.woff2"
as="font" type="font/woff2" crossorigin>
asindica qué tipo de recurso es (font,image,style,script…). Es obligatorio: sin él el navegador no sabe qué prioridad darle.- Las fuentes llevan siempre
crossorigin, aunque estén en tu propio dominio.
Otro uso habitual: una imagen de portada que se pone como fondo desde el CSS (background-image), que el navegador no ve en el HTML.
<link rel="preload" href="img/portada.avif" as="image" type="image/avif" fetchpriority="high">
Cuidado:
preloadno es gratis. Lo que precargas compite con lo demás por la conexión. Si precargas algo que la página no usa en los primeros segundos, la consola muestra un aviso y has empeorado la carga. Reservapreloadpara uno o dos recursos críticos.
| Pista | Qué adelanta | Cuándo |
|---|---|---|
dns-prefetch | Buscar la dirección del dominio | Dominios que usarás más tarde |
preconnect | Búsqueda + conexión segura | Dominios que usarás enseguida (2-3 como máximo) |
preload | La descarga de un recurso concreto | Fuentes o imágenes críticas que se descubren tarde |
Fuentes sin texto invisible
Mientras se descarga una fuente web, algunos navegadores esconden el texto. Con font-display: swap en la regla @font-face se muestra primero con una fuente del sistema y se cambia cuando llega la buena. Si usas Google Fonts, añade &display=swap a la URL (ya viene por defecto en el código que te dan). Tienes los detalles en fuentes web.
Core Web Vitals: cómo mide Google la experiencia
Google resume la experiencia de carga en tres métricas, las Core Web Vitals. Las usa como una de las señales para ordenar los resultados de búsqueda, pero sobre todo son una buena forma de saber si tu web va bien.
| Métrica | Qué mide | Bueno | Malo |
|---|---|---|---|
| LCP (Largest Contentful Paint) | Cuánto tarda en verse el elemento más grande de la pantalla inicial | ≤ 2,5 s | > 4 s |
| CLS (Cumulative Layout Shift) | Cuánto se mueve el contenido mientras carga | ≤ 0,1 | > 0,25 |
| INP (Interaction to Next Paint) | Cuánto tarda la página en reaccionar cuando pulsas o escribes | ≤ 200 ms | > 500 ms |
Entre “bueno” y “malo” está la zona de “necesita mejorar”. Google considera que una página aprueba si el 75 % de las visitas reales están en verde.
LCP: que lo principal se vea pronto
El elemento del LCP suele ser la foto de portada o el titular principal. Para mejorarlo:
- Que la imagen esté en el HTML con
<img>(no metida por JavaScript) y confetchpriority="high". - Nada de
loading="lazy"en ella. - Que pese poco: AVIF o WebP, tamaño ajustado con
srcset. - Que el servidor responda rápido y el CSS no sea enorme.
CLS: que nada salte
Cada vez que algo aparece y empuja al resto, suma CLS. Las causas típicas:
- Imágenes, vídeos e iframes sin
widthyheight. - Banners (cookies, avisos) que se insertan arriba del contenido cuando ya se ve.
- Fuentes web que cambian mucho el tamaño del texto al llegar.
- Anuncios o incrustados sin un hueco reservado.
La solución casi siempre es reservar el espacio antes: width y height, un min-height en el hueco del anuncio, o mostrar los avisos encima del contenido en lugar de empujarlo.
INP: que responda al instante
INP sustituyó en 2024 a la antigua métrica FID. Mide el retraso entre una interacción (un clic, una tecla) y el momento en que la pantalla cambia. Se estropea cuando hay mucho JavaScript ejecutándose a la vez. Desde el HTML ayudas cargando menos scripts y con defer; el resto se trabaja en JavaScript (dividir tareas largas, no hacer cálculos pesados en cada evento). Lo verás en el curso de JavaScript.
Medir: Lighthouse y PageSpeed Insights
Lighthouse
Lighthouse es una herramienta que viene en Chrome (DevTools, pestaña Lighthouse). Carga tu página simulando un móvil con una conexión lenta y te da una nota de 0 a 100 en cuatro categorías: Performance (rendimiento), Accessibility, Best Practices y SEO.
Cómo usarlo bien:
- Abre la página en una ventana de incógnito, para que las extensiones no afecten al resultado.
- Elige Mobile y la categoría Performance.
- Pulsa Analyze page load.
- Mira las métricas (LCP, CLS, Total Blocking Time…) y, sobre todo, la lista de diagnósticos: te dice qué imagen pesa demasiado, qué script bloquea o qué elemento es el LCP.
Nota: Lighthouse no puede medir INP de verdad, porque nadie interactúa con la página durante la prueba. En su lugar muestra el TBT (Total Blocking Time): el tiempo que el JavaScript tuvo bloqueada la página. Si el TBT es alto, el INP probablemente también lo será.
Datos de laboratorio y datos reales
Lighthouse da datos de laboratorio: una prueba en condiciones controladas. Es útil para encontrar problemas, pero la nota cambia un poco cada vez que lo ejecutas.
PageSpeed Insights (pagespeed.web.dev) muestra, además, datos de campo: las métricas reales de los usuarios de Chrome que visitaron tu web en los últimos 28 días, si tiene suficientes visitas. Esos son los que Google tiene en cuenta. Si tu web es nueva o pequeña, solo verás los de laboratorio.
Consejo: no persigas el 100. Una página con 90 en Lighthouse y las tres Core Web Vitals en verde está bien. Es mejor dedicar el tiempo a quitar la imagen de 3 MB que a rascar los últimos puntos.
Un <head> bien ordenado para el rendimiento
Juntando lo visto, así podría quedar la cabecera de la web de un rocódromo:
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Rocódromo La Fisura | Escalada en Santander</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" href="/fuentes/inter-variable.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/css/estilos.css">
<script src="/js/app.js" defer></script>
</head>
Y en el <body>, la imagen de portada con fetchpriority="high" y dimensiones, y el resto de imágenes con loading="lazy".
Errores frecuentes
loading="lazy"en la imagen principal. Retrasa justo el elemento que mide el LCP. Úsalo solo en lo que está más abajo.fetchpriority="high"en muchas imágenes. Si todo es prioritario, nada lo es. Una imagen por página.- Precargar demasiado. Cada
preloadcompite con el resto. Si la consola avisa de que un recurso precargado no se usó, quítalo. - Olvidar
crossoriginal precargar fuentes. El navegador descarga la fuente dos veces: la precargada no le sirve. - Imágenes sin
widthyheight. Es la causa número uno de CLS. - Subir las fotos tal cual salen de la cámara. Varios megas por imagen. Redimensiona y convierte a AVIF o WebP.
- Scripts sin
deferen el<head>. Bloquean la lectura del HTML y retrasan todo. - Medir solo en tu ordenador con fibra. Prueba con la simulación de móvil y red lenta, que es lo que usa Lighthouse.
Resumen
| Herramienta | Para qué |
|---|---|
| Pestaña Red de las DevTools | Ver el peso, el número de peticiones y el orden de carga |
width / height en imágenes | Evitar saltos (CLS) |
loading="lazy" | Retrasar imágenes e iframes de más abajo |
fetchpriority="high" | Adelantar la imagen principal (LCP) |
AVIF / WebP + srcset | Imágenes más ligeras y del tamaño justo |
defer / async | Scripts que no bloquean |
preconnect / dns-prefetch | Adelantar conexiones con otros dominios |
preload | Adelantar fuentes o imágenes críticas que se descubren tarde |
font-display: swap | Texto visible mientras carga la fuente |
| LCP / CLS / INP | Las tres Core Web Vitals: carga, estabilidad y respuesta |
| Lighthouse | Prueba de laboratorio con diagnósticos |
| PageSpeed Insights | Datos reales de usuarios (si hay suficientes visitas) |
En la siguiente lección verás cómo describir el contenido de tu página para que Google lo entienda mejor: datos estructurados con JSON-LD.
Pruébalo tú
Cambia el código y pulsa Ejecutar (o Ctrl + Enter).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.