--- title: "Optimizar imágenes WebP y AVIF con Nginx en Ubuntu 24.04" description: "Crea un pipeline en Ubuntu 24.04 que comprime tus imágenes, genera versiones WebP y AVIF con un temporizador de systemd y deja que Nginx sirva el mejor formato." category: "Rendimiento Web" language: es source: https://cubepath.com/es/docs/rendimiento-web/pipeline-de-optimizacion-de-imagenes-web updated: 2026-09-25 --- # Optimizar imágenes WebP y AVIF con Nginx en Ubuntu 24.04 Las imágenes suelen ser la mayor parte del peso de una página y el elemento que más influye en el LCP (Largest Contentful Paint). En este tutorial montarás un pipeline en Ubuntu 24.04 que comprime los JPEG y PNG originales, genera versiones WebP y AVIF de cada imagen y lo repite automáticamente con un temporizador de systemd. Después configurarás Nginx para que entregue a cada navegador el formato más ligero que soporte, sin cambiar las URL de la aplicación. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Nginx instalado y sirviendo tu sitio en `tudominio.com`, con las imágenes en `/var/www/tudominio.com/images`. Ajusta esa ruta a la tuya en todos los pasos. - Una copia de seguridad de las imágenes: la compresión del paso 2 modifica los originales. ## Paso 1: Instalar las herramientas de conversión Usarás herramientas especializadas por formato, todas en los repositorios de Ubuntu: - `jpegoptim`: recomprime JPEG y elimina metadatos. - `optipng`: optimiza PNG sin pérdida. - `cwebp` (paquete `webp`): convierte a WebP. - `avifenc` (paquete `libavif-bin`): convierte a AVIF. ```bash sudo apt update sudo apt install jpegoptim optipng webp libavif-bin ``` Comprueba que están disponibles: ```bash cwebp -version avifenc --version | head -n 1 ``` Cada comando debe mostrar el número de versión (libwebp 1.3.x y libavif 1.0.x en Ubuntu 24.04). Si alguno responde `command not found`, revisa que el paquete correspondiente se instaló sin errores. ## Paso 2: Probar la conversión con una imagen Antes de automatizar, prueba los ajustes con una imagen para comparar tamaños. Trabaja sobre una copia: ```bash cd /tmp cp /var/www/tudominio.com/images/foto.jpg . jpegoptim --strip-all --max=85 foto.jpg cwebp -quiet -q 80 foto.jpg -o foto.jpg.webp avifenc -q 60 foto.jpg foto.jpg.avif ls -lh foto.jpg* ``` ```output -rw-r--r-- 1 usuario usuario 412K foto.jpg -rw-r--r-- 1 usuario usuario 98K foto.jpg.avif -rw-r--r-- 1 usuario usuario 186K foto.jpg.webp ``` `--max=85` limita la calidad JPEG a 85 (solo recomprime si la imagen tiene más), `-q 80` es la calidad WebP y `-q 60` la calidad AVIF, ambas de 0 a 100. Abre los tres archivos y ajusta los valores si notas pérdida visible. Las versiones se nombran añadiendo la extensión al nombre completo (`foto.jpg.webp`), lo que permite a Nginx encontrarlas a partir de la URL original. ## Paso 3: Crear el script de optimización El script recorre el directorio, procesa solo las imágenes nuevas o modificadas (las que no tienen versión WebP o la tienen más antigua que el original) y es seguro con nombres que contienen espacios. Créalo: ```bash sudo nano /usr/local/bin/optimize-images ``` ```bash #!/usr/bin/env bash set -euo pipefail IMAGE_DIR="${1:-/var/www/tudominio.com/images}" JPEG_MAX_QUALITY=85 WEBP_QUALITY=80 AVIF_QUALITY=60 find "$IMAGE_DIR" -type f \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' \) -print0 | while IFS= read -r -d '' img; do # Saltar si la versión WebP existe y es más reciente que el original if [[ -f "$img.webp" && "$img.webp" -nt "$img" ]]; then continue fi case "${img,,}" in *.png) optipng -quiet -o2 "$img" ;; *) jpegoptim --quiet --strip-all --max="$JPEG_MAX_QUALITY" "$img" ;; esac cwebp -quiet -q "$WEBP_QUALITY" "$img" -o "$img.webp" avifenc -q "$AVIF_QUALITY" "$img" "$img.avif" > /dev/null echo "Optimizada: $img" done ``` Hazlo ejecutable y asigna las imágenes al usuario con el que correrá el servicio (`www-data`), para que pueda escribir las nuevas versiones: ```bash sudo chmod 0755 /usr/local/bin/optimize-images sudo chown -R www-data:www-data /var/www/tudominio.com/images ``` Ejecútalo una vez a mano para procesar las imágenes existentes: ```bash sudo -u www-data /usr/local/bin/optimize-images ``` ```output Optimizada: /var/www/tudominio.com/images/foto.jpg Optimizada: /var/www/tudominio.com/images/logo.png ``` Si lo ejecutas de nuevo, no debería mostrar nada: todas las imágenes están al día. ## Paso 4: Automatizar con un temporizador de systemd Un temporizador que lanza el script cada 15 minutos es más fiable que un bucle con `inotifywait`: si falla, systemd lo registra y lo vuelve a intentar en la siguiente ejecución. Crea el servicio: ```bash sudo nano /etc/systemd/system/optimize-images.service ``` ```ini [Unit] Description=Optimizar imágenes y generar WebP/AVIF [Service] Type=oneshot User=www-data Group=www-data Nice=10 IOSchedulingClass=idle ExecStart=/usr/local/bin/optimize-images /var/www/tudominio.com/images ``` `Nice` e `IOSchedulingClass` reducen la prioridad para que la conversión no compita con Nginx. Crea el temporizador: ```bash sudo nano /etc/systemd/system/optimize-images.timer ``` ```ini [Unit] Description=Ejecutar optimize-images cada 15 minutos [Timer] OnCalendar=*:0/15 Persistent=true [Install] WantedBy=timers.target ``` Actívalo y comprueba la próxima ejecución: ```bash sudo systemctl daemon-reload sudo systemctl enable --now optimize-images.timer systemctl list-timers optimize-images.timer ``` ```output NEXT LEFT LAST PASSED UNIT ACTIVATES Fri 2026-09-25 10:15:00 UTC 9min left - - optimize-images.timer optimize-images.service ``` Para ver qué procesó en cada ejecución usa `journalctl -u optimize-images.service`. ## Paso 5: Servir WebP y AVIF desde Nginx Nginx elegirá el archivo según la cabecera `Accept` del navegador: AVIF si lo acepta y existe, si no WebP, y si no el original. Crea dos mapas en el contexto `http`: ```bash sudo nano /etc/nginx/conf.d/image-formats.conf ``` ```nginx map $http_accept $avif_suffix { default ""; "~*image/avif" ".avif"; } map $http_accept $webp_suffix { default ""; "~*image/webp" ".webp"; } ``` Comprueba que el `mime.types` de Nginx conoce ambos formatos, para que se envíen con el `Content-Type` correcto: ```bash grep -E 'avif|webp' /etc/nginx/mime.types ``` Si falta la línea de AVIF, edita `/etc/nginx/mime.types` y añade `image/avif avif;` dentro del bloque `types`, junto a la de `image/webp`. Ahora abre el archivo de tu sitio, por ejemplo `/etc/nginx/sites-available/tudominio.com`, y añade este bloque dentro de `server`: ```nginx location ~* ^/images/.+\.(jpe?g|png)$ { add_header Vary Accept; add_header Cache-Control "public, max-age=2592000"; try_files $uri$avif_suffix $uri$webp_suffix $uri =404; } ``` `Vary: Accept` es imprescindible: indica a navegadores, proxies y CDN que la misma URL devuelve contenidos distintos según el cliente. Comprueba la sintaxis y recarga: ```bash sudo nginx -t sudo systemctl reload nginx ``` ## Paso 6: Verificar la negociación de formato Simula tres navegadores con distintas cabeceras `Accept`: ```bash URL=https://tudominio.com/images/foto.jpg curl -sI -H "Accept: image/avif,image/webp,*/*" "$URL" | grep -iE 'content-type|content-length' curl -sI -H "Accept: image/webp,*/*" "$URL" | grep -iE 'content-type|content-length' curl -sI "$URL" | grep -iE 'content-type|content-length' ``` ```output content-type: image/avif content-length: 100352 content-type: image/webp content-length: 190464 content-type: image/jpeg content-length: 421888 ``` En el navegador, abre las herramientas de desarrollo, pestaña Red, y comprueba la columna Tipo de las imágenes de la página. ## Paso 7: Aplicar carga diferida y dimensiones en el HTML El servidor no puede decidir qué imágenes están fuera de pantalla, así que este último ajuste va en las plantillas. Añade `loading="lazy"` a las imágenes que no se ven al cargar, e indica siempre `width` y `height` para evitar saltos de diseño (CLS): ```html Galería ``` No lo uses en la imagen principal de la parte superior de la página: diferirla empeora el LCP. ## Solución de problemas **El script falla con `Permission denied`.** El usuario `www-data` no puede escribir en el directorio. Revisa el propietario con `ls -ld /var/www/tudominio.com/images`. **Nginx sigue enviando JPEG a un navegador moderno.** Comprueba que existen `foto.jpg.avif` o `foto.jpg.webp` y que ningún otro `location` con expresión regular declarado antes captura las imágenes, porque Nginx usa el primero que coincide. **Una CDN delante entrega el formato equivocado.** Asegúrate de que respeta la cabecera `Vary: Accept` o de que tiene activada la variación de caché por tipo de imagen; si no, purga la caché y revisa su configuración. ## Conclusión Tus imágenes se comprimen automáticamente al subirse y cada navegador recibe AVIF, WebP o el original según lo que soporte, con la misma URL y sin tocar la aplicación. Como siguientes pasos, puedes generar versiones redimensionadas para usar con `srcset`, activar HTTP/3 en Nginx y medir el impacto en el LCP con PageSpeed Insights.