--- title: "Analizar y rotar logs de Apache y Nginx en Ubuntu 24.04" description: "Aprende a configurar el formato de los logs de Apache y Nginx, analizarlos con awk y GoAccess y controlar su tamaño con logrotate en Ubuntu 24.04 paso a paso." category: "Servidores Web" language: es source: https://cubepath.com/es/docs/servidores-web/analisis-y-rotacion-de-logs-de-apache-y-nginx updated: 2026-09-25 --- # Analizar y rotar logs de Apache y Nginx en Ubuntu 24.04 Los logs de acceso y de errores son la primera fuente de información cuando una web va lenta, devuelve errores o recibe tráfico sospechoso. En este tutorial configurarás en Ubuntu 24.04 los logs de Apache o Nginx para que incluyan el tiempo de respuesta, los analizarás desde la terminal con `awk` y con GoAccess, y ajustarás logrotate para que no llenen el disco. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Apache (`apache2`) o Nginx instalado desde los repositorios de Ubuntu y recibiendo tráfico. ## Paso 1: Localizar los logs En Ubuntu, cada servidor escribe dos archivos principales: | Servidor | Log de acceso | Log de errores | |---|---|---| | Apache | `/var/log/apache2/access.log` | `/var/log/apache2/error.log` | | Nginx | `/var/log/nginx/access.log` | `/var/log/nginx/error.log` | Los archivos rotados aparecen en el mismo directorio como `access.log.1`, `access.log.2.gz`, etc. Pertenecen al grupo `adm`, así que puedes leerlos sin `sudo` si añades tu usuario a ese grupo: ```bash sudo usermod -aG adm $USER ``` El cambio se aplica en la siguiente sesión. Mientras tanto, usa `sudo`. Mira las últimas peticiones en tiempo real: ```bash sudo tail -f /var/log/nginx/access.log ``` Cada línea sigue el formato `combined`, común a ambos servidores: ```text 203.0.113.25 - - [25/Sep/2026:10:14:02 +0000] "GET /blog/ HTTP/1.1" 200 5123 "https://www.google.com/" "Mozilla/5.0 (X11; Linux x86_64) ..." ``` Los campos son: IP del cliente, usuario (si hay autenticación), fecha, línea de la petición, código de estado, bytes enviados, `Referer` y `User-Agent`. Pulsa `Ctrl+C` para salir de `tail`. ## Paso 2: Añadir el tiempo de respuesta al log El formato `combined` no dice cuánto tardó cada petición, que es justo lo que necesitas para encontrar páginas lentas. Añade ese dato al final de la línea, así las herramientas que entienden `combined` siguen funcionando. ### En Nginx Crea un archivo con el nuevo formato. Nginx carga `/etc/nginx/conf.d/*.conf` dentro del bloque `http`: ```bash sudo nano /etc/nginx/conf.d/log-format.conf ``` ```nginx log_format timed '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' 'rt=$request_time urt="$upstream_response_time"'; ``` `$request_time` es el tiempo total en segundos con milisegundos, y `$upstream_response_time` el tiempo que tardó el backend (PHP-FPM, una aplicación Node, etc.) cuando Nginx actúa como proxy. Usa el formato en el bloque `server` de tu sitio: ```bash sudo nano /etc/nginx/sites-available/your_domain ``` ```nginx access_log /var/log/nginx/your_domain.access.log timed; error_log /var/log/nginx/your_domain.error.log warn; ``` Tener un archivo por sitio facilita el análisis cuando el servidor aloja varios dominios. Comprueba y recarga: ```bash sudo nginx -t && sudo systemctl reload nginx ``` ### En Apache Define el formato en un archivo de configuración propio. `%D` es el tiempo de respuesta en microsegundos: ```bash sudo nano /etc/apache2/conf-available/log-format.conf ``` ```apache LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" rt=%D" timed ``` Actívalo y úsalo en el virtual host de tu sitio: ```bash sudo a2enconf log-format sudo nano /etc/apache2/sites-available/your_domain.conf ``` ```apache CustomLog ${APACHE_LOG_DIR}/your_domain.access.log timed ErrorLog ${APACHE_LOG_DIR}/your_domain.error.log ``` Comprueba y recarga: ```bash sudo apache2ctl configtest && sudo systemctl reload apache2 ``` ### Verificar el nuevo formato Haz una petición y mira la última línea del log (sustituye la ruta por la de Apache si usas Apache): ```bash curl -s -o /dev/null http://localhost/ sudo tail -n 1 /var/log/nginx/your_domain.access.log ``` ```output 127.0.0.1 - - [25/Sep/2026:10:20:11 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.5.0" rt=0.002 urt="-" ``` Si la petición no aparece en el log del sitio, `curl` ha caído en otro `server` o virtual host; prueba con `curl -H "Host: your_domain" http://localhost/`. ## Paso 3: Excluir peticiones que no aportan nada Los health checks de un balanceador o de un monitor externo pueden generar miles de líneas al día. Puedes dejarlas fuera del log. En Nginx, usa un `map` en `/etc/nginx/conf.d/log-format.conf`: ```nginx map $request_uri $loggable { /health 0; default 1; } ``` Y añade la condición a la directiva `access_log` del sitio: ```nginx access_log /var/log/nginx/your_domain.access.log timed if=$loggable; ``` En Apache, marca la petición con `SetEnvIf` dentro del virtual host y excluye esa variable en `CustomLog`: ```apache SetEnvIf Request_URI "^/health$" dontlog CustomLog ${APACHE_LOG_DIR}/your_domain.access.log timed env=!dontlog ``` Recarga el servidor como en el paso anterior y comprueba que `curl http://localhost/health` ya no añade líneas al log. ## Paso 4: Analizar los logs desde la terminal Con `awk`, `sort` y `uniq` puedes responder a la mayoría de preguntas sin instalar nada. En el formato `combined`, `$1` es la IP, `$7` la ruta y `$9` el código de estado. Define una variable con la ruta de tu log para no repetirla: ```bash LOG=/var/log/nginx/your_domain.access.log ``` Reparto de códigos de estado: ```bash sudo awk '{print $9}' "$LOG" | sort | uniq -c | sort -rn ``` ```output 18234 200 1203 304 412 404 37 301 5 502 ``` Las 10 IP con más peticiones: ```bash sudo awk '{print $1}' "$LOG" | sort | uniq -c | sort -rn | head -10 ``` Las rutas que más errores 404 generan, útil para detectar enlaces rotos o escaneos de vulnerabilidades (`/wp-login.php`, `/.env`): ```bash sudo awk '$9 == 404 {print $7}' "$LOG" | sort | uniq -c | sort -rn | head -10 ``` Las 10 peticiones más lentas, usando el campo `rt=` del paso 2 (en Apache el valor está en microsegundos): ```bash sudo grep -o '"[A-Z]* [^"]*" .* rt=[0-9.]*' "$LOG" | awk '{print $NF, $2}' | sort -t= -k2 -rn | head -10 ``` ```output rt=2.184 /api/reports rt=1.920 /api/reports rt=0.873 /search ``` Peticiones por hora, para ver picos de tráfico: ```bash sudo awk '{print substr($4, 2, 14)}' "$LOG" | sort | uniq -c ``` Para incluir los archivos ya rotados y comprimidos, usa `zcat -f`, que lee tanto archivos `.gz` como sin comprimir: ```bash sudo zcat -f /var/log/nginx/your_domain.access.log* | awk '{print $9}' | sort | uniq -c | sort -rn ``` Para el log de errores basta con filtrar por nivel o por texto. Por ejemplo, las últimas conexiones fallidas con el backend en Nginx: ```bash sudo grep -E "upstream|connect\(\) failed" /var/log/nginx/your_domain.error.log | tail -20 ``` ## Paso 5: Generar informes con GoAccess GoAccess analiza los logs y muestra visitantes, rutas, códigos de estado, sistemas operativos y referers en la terminal o en un informe HTML. Está en los repositorios de Ubuntu: ```bash sudo apt update sudo apt install goaccess ``` Abre el panel interactivo sobre el log de acceso. Como tu formato añade campos al final de `combined`, GoAccess lo reconoce con el formato `COMBINED`: ```bash sudo goaccess /var/log/nginx/your_domain.access.log --log-format=COMBINED ``` Muévete entre paneles con `Tab` y sal con `q`. Para generar un informe HTML estático de todo el historial, incluidos los archivos rotados: ```bash sudo zcat -f /var/log/nginx/your_domain.access.log* | goaccess - --log-format=COMBINED -o ~/report.html ``` ```output [PARSING STDIN] {21,891} @ {0/s} ``` Descarga `~/report.html` a tu equipo con `scp` y ábrelo en el navegador. No publiques el informe en un directorio web accesible sin protegerlo con contraseña: contiene las IP de tus visitantes. ## Paso 6: Configurar la rotación con logrotate Ubuntu rota los logs de Apache y Nginx con logrotate, que se ejecuta una vez al día mediante un temporizador de systemd. Compruébalo: ```bash systemctl list-timers logrotate.timer ``` ```output NEXT LEFT LAST PASSED UNIT ACTIVATES Fri 2026-09-26 00:00:00 UTC 13h left Thu 2026-09-25 00:00:03 UTC 10h ago logrotate.timer logrotate.service ``` La configuración de cada servidor está en `/etc/logrotate.d/nginx` y `/etc/logrotate.d/apache2`. Ambas usan el patrón `*.log` del directorio, así que los logs por sitio que creaste en el paso 2 también se rotan. Abre la de tu servidor: ```bash sudo nano /etc/logrotate.d/nginx ``` Ajusta la política a tus necesidades. Este ejemplo rota a diario, guarda 30 días con la fecha en el nombre y comprime todo salvo el archivo más reciente: ```text /var/log/nginx/*.log { daily missingok rotate 30 dateext compress delaycompress notifempty create 0640 www-data adm sharedscripts prerotate if [ -d /etc/logrotate.d/httpd-prerotate ]; then \ run-parts /etc/logrotate.d/httpd-prerotate; \ fi \ endscript postrotate invoke-rc.d nginx rotate >/dev/null 2>&1 endscript } ``` Deja intactos los bloques `prerotate` y `postrotate` que ya trae tu archivo: son los que avisan al servidor para que abra los logs nuevos (en Apache, con una recarga). Si solo quieres cambiar la retención, basta con modificar `rotate` y añadir `dateext`. > Consejo: `delaycompress` deja sin comprimir el log del día anterior (`access.log.1` o con fecha). Así puedes seguir consultándolo con `grep` y `tail` directamente, y las órdenes con `zcat -f` del paso 4 funcionan con ambos. Prueba la configuración en modo simulación. `-d` muestra lo que haría sin tocar ningún archivo: ```bash sudo logrotate -d /etc/logrotate.d/nginx ``` ```output rotating pattern: /var/log/nginx/*.log after 1 days (30 rotations) empty log files are not rotated, old logs are removed considering log /var/log/nginx/access.log ``` Si no hay errores, fuerza una rotación para comprobar que todo funciona de extremo a extremo: ```bash sudo logrotate -f /etc/logrotate.d/nginx ls -lh /var/log/nginx/ ``` Debes ver archivos nuevos vacíos y los anteriores con la fecha en el nombre. Haz una petición y comprueba que se escribe en el `access.log` nuevo; si se escribe en el rotado, el `postrotate` no ha avisado al servidor. ## Solución de problemas **Los logs rotados siguen creciendo y el nuevo está vacío.** El servidor mantiene abierto el archivo antiguo. Revisa que el bloque `postrotate` está presente y ejecuta `sudo systemctl reload nginx` o `sudo systemctl reload apache2`. **logrotate no se ejecuta.** Mira el resultado de la última ejecución con `sudo journalctl -u logrotate.service -n 20`. Un error de sintaxis en cualquier archivo de `/etc/logrotate.d/` puede afectar a los demás; pruébalos todos con `sudo logrotate -d /etc/logrotate.conf`. **El disco se llena igualmente.** Busca qué ocupa espacio con `sudo du -sh /var/log/* | sort -h`. Si la aplicación escribe sus propios logs fuera de `/var/log/nginx` o `/var/log/apache2`, necesita su propio archivo en `/etc/logrotate.d/`. **GoAccess muestra "Token doesn't match specifier".** El formato del log no coincide con el indicado. Comprueba que usas `--log-format=COMBINED` y que el log no está en JSON u otro formato personalizado. ## Conclusión Tus logs ya incluyen el tiempo de respuesta de cada petición, ignoran los health checks, se pueden analizar con unas pocas órdenes o con GoAccess y se rotan con una retención definida. Con esto puedes localizar rápidamente errores, páginas lentas e IP abusivas. Como siguientes pasos puedes: - Usar las IP detectadas en el paso 4 para configurar rate limiting en Nginx o reglas de Fail2ban. - Enviar los logs a un sistema centralizado (rsyslog, Loki o Elasticsearch) si gestionas varios servidores. - Programar un informe diario de GoAccess con un temporizador de systemd.