Guía detallada

Fcp Optimization Guide

Ver contenido

Guía Completa de Optimización de FCP: Logrando un First Contentful Paint Rápido

Introducción

First Contentful Paint (FCP) mide cuándo los usuarios ven por primera vez contenido en tu página. Lograr una buena puntuación de FCP (menos de 1.8 segundos) requiere optimizar la ruta crítica de renderizado—la secuencia de pasos que los navegadores toman para convertir HTML, CSS y JavaScript en píxeles visibles.

Esta guía cubre estrategias avanzadas para eliminar retrasos y entregar contenido a los usuarios lo más rápido posible.

Entendiendo la Ruta Crítica de Renderizado

Antes de que FCP pueda ocurrir, el navegador debe:

Ruta Crítica de Renderizado:
1. Descargar HTML
2. Parsear HTML, descubrir recursos
3. Descargar CSS crítico
4. Parsear CSS, construir CSSOM
5. Construir árbol de renderizado (DOM + CSSOM)
6. Calcular layout
7. Pintar contenido → ¡FCP!

Insight clave: FCP es bloqueado por:

  • Descarga y parseo de HTML
  • Descarga y parseo de CSS
  • JavaScript síncrono

Optimizando el Tiempo de Respuesta del Servidor

1. Mejora TTFB (Time to First Byte)

Cada milisegundo de TTFB retrasa FCP:

Componentes de TTFB:
├── DNS Lookup: 20-120ms
├── Conexión TCP: 20-100ms
├── Handshake TLS: 30-100ms
├── Procesamiento del Servidor: Variable
└── Latencia de Red: Variable

Objetivos de optimización:
├── TTFB Total < 200ms (excelente)
├── TTFB Total < 500ms (bueno)
└── TTFB Total > 800ms (necesita trabajo)

2. Usa una CDN

Sirve contenido desde servidores edge cerca de los usuarios:

Sin CDN:
Usuario (Chile) → Servidor Origen (US Este) = 180ms latencia

Con CDN:
Usuario (Chile) → CDN Edge (Santiago) = 20ms latencia

3. Implementa Caché

# Nginx - Cachear recursos estáticos
location ~* \.(css|js|jpg|jpeg|png|webp|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# Cachear HTML con revalidación
location / {
    add_header Cache-Control "public, max-age=0, must-revalidate";
}

4. Habilita Compresión

# Habilitar compresión Brotli (preferido) y Gzip
gzip on;
gzip_types text/plain text/css application/javascript application/json;
gzip_min_length 256;

# Brotli (si está disponible)
brotli on;
brotli_types text/plain text/css application/javascript application/json;

Eliminando CSS que Bloquea el Renderizado

1. Identifica CSS Crítico

CSS crítico = estilos necesarios para contenido above-the-fold:

<!-- CSS crítico inlineado en <head> -->
<style>
/* Solo estilos para el viewport inicial */
:root { --primary: #071952; --text: #333; }
body { margin: 0; font-family: system-ui; color: var(--text); }
.header { background: var(--primary); padding: 1rem; }
.hero { padding: 2rem; }
.hero h1 { font-size: 2.5rem; margin: 0; }
</style>

2. Extrae e Inlinea CSS Crítico

Usa herramientas para extraer automáticamente CSS crítico:

# Usando el paquete npm critical
npx critical index.html --inline --minify

# Usando PurgeCSS para eliminar CSS no usado
npx purgecss --css estilos.css --content index.html

3. Carga CSS No Crítico Asíncronamente

<!-- Patrón 1: preload + onload -->
<link rel="preload" href="estilos-completos.css" as="style"
      onload="this.onload=null;this.rel='stylesheet'">
<noscript>
    <link rel="stylesheet" href="estilos-completos.css">
</noscript>

<!-- Patrón 2: intercambio de media query -->
<link rel="stylesheet" href="estilos-completos.css"
      media="print" onload="this.media='all'">

<!-- Patrón 3: Inyección dinámica -->
<script>
    const link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = 'estilos-completos.css';
    document.head.appendChild(link);
</script>

4. Minimiza CSS

Mantén el CSS pequeño y eficiente:

/* ❌ CSS verboso */
.button {
    background-color: #071952;
    padding-top: 10px;
    padding-bottom: 10px;
    padding-left: 20px;
    padding-right: 20px;
    border-radius: 4px;
}

/* ✅ CSS minificado */
.button{background:#071952;padding:10px 20px;border-radius:4px}

Eliminando JavaScript que Bloquea el Renderizado

1. Difiere Scripts No Críticos

<!-- ❌ Bloquea renderizado -->
<script src="analytics.js"></script>

<!-- ✅ Diferido - ejecuta después de DOM ready -->
<script src="analytics.js" defer></script>

<!-- ✅ Async - ejecuta cuando descarga, orden no garantizado -->
<script src="widget.js" async></script>

2. Mueve Scripts al Final del Body

<!DOCTYPE html>
<html>
<head>
    <style>/* Solo CSS crítico */</style>
</head>
<body>
    <!-- Contenido que dispara FCP -->
    <header>...</header>
    <main>...</main>

    <!-- Scripts al final -->
    <script src="app.js"></script>
</body>
</html>

3. Usa Module Scripts

<!-- Los module scripts están diferidos por defecto -->
<script type="module" src="app.js"></script>

<!-- Precarga módulos críticos -->
<link rel="modulepreload" href="modulo-critico.js">

Optimizando la Carga de Recursos

1. Precarga Recursos Críticos

Dile al navegador qué es importante:

<head>
    <!-- Precarga CSS crítico si no está inlineado -->
    <link rel="preload" href="critico.css" as="style">

    <!-- Precarga imagen hero -->
    <link rel="preload" href="hero.webp" as="image"
          fetchpriority="high">

    <!-- Precarga fuente crítica -->
    <link rel="preload" href="fuente.woff2" as="font"
          type="font/woff2" crossorigin>
</head>

2. Usa Resource Hints

<!-- DNS prefetch para dominios de terceros -->
<link rel="dns-prefetch" href="https://cdn.example.com">

<!-- Preconnect para terceros críticos -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

<!-- Prefetch recursos para próxima navegación -->
<link rel="prefetch" href="siguiente-pagina.html">

3. Prioriza Contenido Visible

Usa el atributo fetchpriority:

<!-- Alta prioridad para imagen LCP -->
<img src="hero.webp" fetchpriority="high" alt="Hero">

<!-- Baja prioridad para imágenes below-fold -->
<img src="logo-footer.webp" fetchpriority="low" alt="Logo">

Optimización de Fuentes para FCP

1. Usa font-display

@font-face {
    font-family: 'FuentePersonalizada';
    src: url('fuente.woff2') format('woff2');
    font-display: swap; /* Muestra fallback inmediatamente, intercambia cuando esté lista */
}

2. Precarga Fuentes Críticas

<link rel="preload" href="fuente-principal.woff2"
      as="font" type="font/woff2" crossorigin>

3. Usa Stack de Fuentes del Sistema como Fallback

body {
    font-family: 'FuentePersonalizada', -apple-system, BlinkMacSystemFont,
                 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

4. Subconjuntos de Fuentes

Elimina caracteres no usados:

# Usando glyphanger
glyphanger --subset="*.woff2" --whitelist="U+0-7F"

Beneficios del Renderizado del Lado del Servidor

SSR puede mejorar dramáticamente FCP:

Client-Side Rendering (CSR):
HTML Shell → Descarga JS → Ejecuta JS → Renderiza Contenido → FCP
            └────────────── Retraso largo ──────────────┘

Server-Side Rendering (SSR):
HTML Completo → ¡FCP! (inmediato) → Descarga JS → Hidratación
↑ FCP ocurre mucho más rápido

Generación de Sitio Estático (SSG)

Para contenido que no cambia frecuentemente:

Proceso SSG:
Tiempo de Build: Genera HTML para todas las páginas
Runtime: Sirve HTML pre-construido → FCP inmediato

Medición y Monitoreo de FCP

Usando Performance API

// Obtener valor de FCP
const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
        if (entry.name === 'first-contentful-paint') {
            console.log('FCP:', entry.startTime, 'ms');
        }
    }
});
observer.observe({ type: 'paint', buffered: true });

Usando Web Vitals Library

import { onFCP } from 'web-vitals';

onFCP((metric) => {
    console.log('FCP:', metric.value);

    // Enviar a analytics
    analytics.track('FCP', {
        value: metric.value,
        rating: metric.rating, // 'good', 'needs-improvement', 'poor'
        url: window.location.href
    });
});

Lista de Verificación de Optimización de FCP

Respuesta del Servidor
├── [ ] TTFB menor a 200ms
├── [ ] CDN configurado
├── [ ] Compresión habilitada (Brotli/Gzip)
├── [ ] HTTP/2 o HTTP/3 habilitado
└── [ ] Headers de caché apropiados

Optimización de CSS
├── [ ] CSS crítico inlineado
├── [ ] CSS no crítico cargado async
├── [ ] CSS minificado
├── [ ] CSS no usado eliminado
└── [ ] Sin declaraciones @import

Optimización de JavaScript
├── [ ] Scripts diferidos o async
├── [ ] Sin JS bloqueante en head
├── [ ] JS crítico inlineado si es pequeño
└── [ ] Scripts de terceros diferidos

Carga de Recursos
├── [ ] Recursos críticos precargados
├── [ ] Resource hints configurados
├── [ ] Imágenes optimizadas
└── [ ] Fuentes precargadas con font-display

Monitoreo
├── [ ] FCP rastreado en RUM
├── [ ] Alertas por regresión de FCP
├── [ ] Auditorías regulares de Lighthouse
└── [ ] Datos de campo revisados en CrUX

Problemas Comunes de FCP y Soluciones

Problema Solución
Archivo CSS grande bloquea renderizado Extrae e inlinea CSS crítico
JS síncrono en head Añade defer o mueve al final del body
Respuesta lenta del servidor Usa CDN, habilita caché
Documento HTML grande Reduce tamaño del DOM, usa SSR
Recursos de terceros lentos Difiere, usa facades, o elimina
Fuentes web retrasan texto Usa font-display: swap, precarga

Artículos Relacionados

Referencias

Este artículo fue mejorado usando fuentes autoritativas:

[1] web.dev: Optimize First Contentful Paint https://web.dev/articles/optimize-fcp Guía oficial de Google para optimización de FCP. Cubre la ruta crítica de renderizado, recursos que bloquean renderizado, y técnicas específicas para mejorar FCP incluyendo estrategias de entrega de CSS y carga de JavaScript.

[2] Chrome Developers: Critical Rendering Path https://developer.chrome.com/docs/lighthouse/performance/critical-request-chains Documentación sobre cadenas de solicitudes críticas y cómo optimizar el orden de carga de recursos. Explica cómo las cadenas de solicitudes afectan FCP y cómo minimizar la profundidad de cadenas.

[3] MDN Web Docs: CSS Performance Optimization https://developer.mozilla.org/en-US/docs/Learn/Performance/CSS Referencia técnica para optimización de CSS incluyendo extracción de CSS crítico, eficiencia de selectores, y patrones de carga asíncrona que impactan directamente FCP.

[4] web.dev: Eliminate Render-Blocking Resources https://web.dev/articles/render-blocking-resources Guía completa para identificar y eliminar CSS y JavaScript que bloquean renderizado. Cubre CSS crítico inline, atributos defer/async, y priorización de recursos.


Nota: Este artículo es parte de nuestra serie de análisis SEO. Explora todos los artículos en el Hub de SEO de Rendimiento.


Fuentes: web.dev (Optimización de FCP), Chrome Developers, MDN Web Docs

Artículos relacionados

Versión relacionada

Introducción

Fcp Explained

First Contentful Paint (FCP) mide el tiempo desde que una página comienza a cargar hasta que cualquier parte del contenido de la página se renderiza en la...

Hub de categoría

Hub

Performance Seo Hub

El rendimiento es un factor de ranking crítico e impacta directamente la experiencia del usuario

En la misma categoría

Guía detallada

Lcp Optimization Guide

Largest Contentful Paint (LCP) mide cuándo el elemento de contenido más grande se vuelve visible para los usuarios

Guía detallada

Ttfb Optimization Guide

Time to First Byte (TTFB) es la base del rendimiento web—cada milisegundo de TTFB retrasa toda la carga de tu página