Configuración de Early Hints y Código de Estado 103
HTTP 103 Early Hints es un código de estado informativo que permite al servidor enviar cabeceras de precarga al navegador mientras sigue procesando la respuesta final, eliminando el tiempo muerto en que el navegador espera sin poder hacer nada útil. Al implementar Early Hints en Nginx para recursos críticos como CSS, fuentes y JavaScript, puedes reducir el tiempo de carga percibido entre 200ms y 1 segundo en aplicaciones con alto tiempo de procesamiento en el servidor.
Requisitos Previos
- Nginx 1.25.1+ (con soporte para HTTP 103 Early Hints)
- HTTPS habilitado (Early Hints solo funciona con HTTP/2 o HTTP/3)
- Conocimiento de los recursos críticos de tu aplicación (CSS, JS, fuentes)
- Acceso root o sudo al servidor
Early Hints en Nginx
A partir de Nginx 1.25.1, hay soporte nativo para Early Hints:
# Verificar la versión de Nginx
nginx -v
# Verificar soporte de Early Hints
nginx -V 2>&1 | grep -i "http_v2\|http_v3"
# Ubuntu/Debian - actualizar a la versión mainline de Nginx
curl https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
echo "deb https://nginx.org/packages/mainline/ubuntu $(lsb_release -cs) nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt update && sudo apt install -y nginx
Configuración de Early Hints en Nginx:
# /etc/nginx/sites-available/tudominio.conf
server {
listen 443 ssl http2;
server_name tudominio.com;
ssl_certificate /etc/letsencrypt/live/tudominio.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tudominio.com/privkey.pem;
root /var/www/tudominio;
location / {
# Enviar 103 Early Hints antes de procesar la respuesta
http2_push_preload on;
# Método 1: Usando la directiva add_header con 103
# (disponible en Nginx 1.25.1+)
# Recursos CSS críticos - se precargan mientras el servidor procesa
add_header Link "</css/critical.css>; rel=preload; as=style" always;
add_header Link "</css/app.css>; rel=preload; as=style" always;
# JavaScript principal
add_header Link "</js/app.js>; rel=preload; as=script" always;
# Fuentes críticas
add_header Link "</fonts/main.woff2>; rel=preload; as=font; type=font/woff2; crossorigin" always;
# Preconnect a dominios externos críticos
add_header Link "<https://fonts.googleapis.com>; rel=preconnect" always;
add_header Link "<https://cdn.tudominio.com>; rel=preconnect; crossorigin" always;
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Habilitar Early Hints para peticiones PHP que tarden
# Nginx 1.25.1+ puede enviar el 103 antes de que PHP responda
}
}
Configuración más avanzada con http2_push:
# Para Nginx con módulo http2 - envío activo de recursos
server {
listen 443 ssl http2;
server_name tudominio.com;
# Caché de push (evita re-enviar recursos ya cacheados por el cliente)
http2_push_preload on;
location = / {
# Push activo de recursos críticos
http2_push /css/critical.css;
http2_push /js/app.js;
add_header Link "</css/critical.css>; rel=preload; as=style";
add_header Link "</js/app.js>; rel=preload; as=script";
try_files $uri /index.html;
}
# Los recursos pusheados deben tener caché adecuada
location ~* \.(css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Directivas Preload y Preconnect
Diferencias clave entre las directivas:
# rel=preload: Descarga el recurso con alta prioridad para usarlo pronto
# Usar para recursos críticos en el rendering path
add_header Link "</css/critical.css>; rel=preload; as=style";
add_header Link "</js/bundle.js>; rel=preload; as=script";
add_header Link "</fonts/inter.woff2>; rel=preload; as=font; type=font/woff2; crossorigin";
add_header Link "</hero-image.webp>; rel=preload; as=image; imagesizes=(max-width: 800px) 100vw, 800px";
# rel=preconnect: Solo establece la conexión TCP/TLS, sin descargar nada
# Usar para dominios de terceros que se usarán pronto
add_header Link "<https://fonts.googleapis.com>; rel=preconnect";
add_header Link "<https://cdn.analytics.com>; rel=preconnect; crossorigin";
# rel=dns-prefetch: Solo resuelve el DNS, más ligero que preconnect
# Usar para dominios menos críticos
add_header Link "<https://tracking.terceros.com>; rel=dns-prefetch";
# rel=prefetch: Descarga con baja prioridad para la siguiente página
# Usar para recursos que se necesitarán en la navegación siguiente
add_header Link "</js/pagina-siguiente.js>; rel=prefetch";
Configuración específica por tipo de contenido:
# Aplicar Early Hints diferentes según la página solicitada
map $uri $early_hints_resources {
"~^/$" "</css/home.css>; rel=preload; as=style, </js/home.js>; rel=preload; as=script";
"~^/blog" "</css/blog.css>; rel=preload; as=style, </fonts/serif.woff2>; rel=preload; as=font; crossorigin";
"~^/shop" "</css/shop.css>; rel=preload; as=style, </js/cart.js>; rel=preload; as=script";
default "</css/app.css>; rel=preload; as=style";
}
server {
location / {
add_header Link $early_hints_resources always;
try_files $uri $uri/ /index.php$is_args$args;
}
}
Soporte en CDN
Las principales CDN soportan Early Hints:
# Cloudflare: Early Hints está disponible automáticamente si:
# 1. El servidor de origen envía las cabeceras Link correctas
# 2. HTTP/2 o HTTP/3 está habilitado
# Cloudflare almacena los Early Hints y los sirve sin consultar el origen
# Fastly: Soporta Early Hints con configuración de VCL
# En Fastly VCL:
# sub vcl_recv {
# if (req.url ~ "^/$") {
# declare local var.hints STRING;
# set var.hints = "</css/app.css>; rel=preload; as=style";
# early_hints(var.hints);
# }
# }
# Verificar que la CDN preserva las cabeceras Early Hints
curl -I https://tudominio.com/ | grep -i "link\|103"
Compatibilidad con Navegadores
| Navegador | Soporte 103 Early Hints |
|---|---|
| Chrome 103+ | Completo |
| Firefox 120+ | Completo |
| Safari 17+ | Completo (solo preload) |
| Edge 103+ | Completo |
# Los navegadores no compatibles simplemente ignoran el 103
# No hay riesgo de romper funcionalidad al implementar Early Hints
# Verificar la respuesta desde curl (no todos los clientes muestran el 103)
curl --http2 -v https://tudominio.com/ 2>&1 | grep -E "< HTTP|< link"
# Usar wget para ver el 103 intermedio
wget --server-response --spider https://tudominio.com/ 2>&1 | head -30
Pruebas de Rendimiento
# Medir el impacto de Early Hints con WebPageTest o Lighthouse
# Instalar Lighthouse CLI
npm install -g lighthouse
# Ejecutar análisis antes de configurar Early Hints
lighthouse https://tudominio.com \
--output json \
--quiet \
--chrome-flags="--headless --no-sandbox" \
--only-audits=largest-contentful-paint,first-contentful-paint,server-response-time \
| jq '.audits'
# Script de benchmark simple
cat > /usr/local/bin/benchmark-hints.sh << 'SCRIPT'
#!/bin/bash
URL="${1:-https://tudominio.com}"
RUNS=10
echo "Midiendo tiempos de carga para $URL ($RUNS ejecuciones)"
echo "================================================"
TOTAL_TTFB=0
for i in $(seq 1 $RUNS); do
TTFB=$(curl -w "%{time_starttransfer}" -o /dev/null -s -H "Cache-Control: no-cache" "$URL")
TTFB_MS=$(echo "$TTFB * 1000" | bc | cut -d'.' -f1)
echo "Ejecución $i: ${TTFB_MS}ms"
TOTAL_TTFB=$((TOTAL_TTFB + TTFB_MS))
done
echo "================================================"
echo "Media TTFB: $((TOTAL_TTFB / RUNS))ms"
SCRIPT
chmod +x /usr/local/bin/benchmark-hints.sh
./benchmark-hints.sh https://tudominio.com
Herramientas online para verificar Early Hints:
# Verificar manualmente con HTTP/2 headers
curl --http2 -v https://tudominio.com/ 2>&1 | grep -E "^< "
# El 103 aparecerá antes del 200 en la respuesta:
# < HTTP/2 103
# < link: </css/app.css>; rel=preload; as=style
# [Nginx procesa la petición PHP...]
# < HTTP/2 200
# < content-type: text/html
Solución de Problemas
Las cabeceras Early Hints no aparecen:
# Verificar la versión de Nginx (debe ser 1.25.1+)
nginx -v
# Verificar que HTTP/2 está habilitado
curl -I --http2 https://tudominio.com/ | head -5
# Comprobar que las cabeceras Link están en la configuración
sudo nginx -T | grep -A2 "add_header Link"
# Verificar la configuración con nginx -T
sudo nginx -T 2>/dev/null | grep -B5 -A5 "early_hints\|Link"
Recursos preloaded no se usan (aparece "unused preload" en DevTools):
# Asegúrate de que los recursos preloaded coinciden exactamente
# con lo que la página necesita
# Verificar con Chrome DevTools:
# Network > Ver filtro "Other" > Buscar "preload" con tipo "Unused"
# Reducir el tiempo de as= para que el navegador lo use:
# Cambiar: rel=preload; as=script
# Por: rel=preload; as=script; fetchpriority=high
Early Hints causa problemas con el caché de la CDN:
# Asegúrate de que las cabeceras Vary están bien configuradas
add_header Vary "Accept-Encoding, Accept";
# Si la CDN cachea 103 junto con 200 (incorrecto), contactar soporte
# o deshabilitar Early Hints para contenido dinámico
Conclusión
HTTP 103 Early Hints es una mejora de rendimiento elegante que aprovecha el tiempo de procesamiento del servidor para pre-cargar recursos críticos en el navegador. Su implementación en Nginx es sencilla y el fallback para navegadores no compatibles es transparente, lo que lo convierte en una optimización de bajo riesgo y alto impacto. Los mayores beneficios se obtienen en aplicaciones con tiempos de procesamiento del servidor superiores a 200ms, donde el navegador puede descargar CSS y fuentes mientras espera el HTML final.


