Logs de Apache y Nginx: Análisis y Rotación
Los logs de servidores web son fuentes invaluables de información para monitorear la salud de aplicaciones, diagnosticar problemas, analizar patrones de tráfico, detectar amenazas de seguridad y comprender el comportamiento de usuarios. Tanto Apache como Nginx generan logs de acceso y error detallados que, cuando se gestionan y analizan apropiadamente, proporcionan insights críticos sobre tu infraestructura web. Esta guía completa cubre configuración de logs, técnicas de análisis, estrategias de rotación y mejores prácticas para servidores web Apache y Nginx.
La gestión efectiva de logs es esencial para mantener visibilidad operacional, solucionar problemas de rendimiento, asegurar cumplimiento de seguridad y tomar decisiones basadas en datos sobre tu infraestructura. Ya sea que estés rastreando un misterioso error 500, analizando patrones de tráfico para planificación de capacidad o investigando posibles incidentes de seguridad, dominar el análisis y gestión de logs de servidores web es una habilidad fundamental para administradores de sistemas e ingenieros DevOps.
Tabla de Contenidos
- Prerequisitos
- Comprendiendo los Logs de Servidores Web
- Configuración de Logs de Apache
- Configuración de Logs de Nginx
- Técnicas de Análisis de Logs
- Rotación de Logs
- Logging Centralizado
- Seguridad y Cumplimiento
- Solución de Problemas
- Mejores Prácticas
- Conclusión
Prerequisitos
Antes de trabajar con logs de servidores web, asegúrate de tener:
- Sistema Operativo: Ubuntu 20.04/22.04, Debian 10/11, CentOS 8/Rocky Linux 8, o similar
- Servidor Web: Apache 2.4+ o Nginx 1.18+ instalado y en ejecución
- Acceso root o sudo: Requerido para configurar ajustes de logs y rotación
- Conocimiento básico: Comprensión de HTTP, servidores web y herramientas de línea de comandos
- Espacio en disco: Almacenamiento adecuado para retención de logs (los logs pueden crecer rápidamente)
- Editor de texto: nano, vim o similar para editar archivos de configuración
- Herramientas de análisis de logs: Familiaridad básica con grep, awk, sed recomendada
Comprendiendo los Logs de Servidores Web
Tipos de Logs de Servidores Web
Logs de Acceso: Registran todas las peticiones procesadas por el servidor web
- Direcciones IP de clientes
- Marcas de tiempo de peticiones
- Métodos HTTP y URLs
- Códigos de estado de respuesta
- Bytes transferidos
- User agents y referrers
Logs de Error: Registran errores de servidor, advertencias e información diagnóstica
- Errores de aplicación (errores 500)
- Problemas de configuración
- Archivos faltantes (errores 404)
- Problemas de permisos
- Advertencias de módulos
Formatos de Logs
Formato Combined Log (más común):
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
Entrada de ejemplo:
192.168.1.100 - - [10/Jan/2026:10:30:45 +0000] "GET /index.html HTTP/1.1" 200 2326 "https://google.com" "Mozilla/5.0..."
Campos explicados:
%h: Dirección IP del cliente (192.168.1.100)%l: Identidad (usualmente -)%u: Usuario autenticado (o -)%t: Marca de tiempo%r: Línea de petición%>s: Código de estado (200)%b: Tamaño en bytes (2326)Referer: De dónde vino el visitanteUser-Agent: Información del navegador
Ubicaciones de Logs
Apache:
- Ubuntu/Debian:
/var/log/apache2/- access.log
- error.log
- CentOS/Rocky:
/var/log/httpd/- access_log
- error_log
Nginx:
- Todas las distribuciones:
/var/log/nginx/- access.log
- error.log
Configuración de Logs de Apache
Formatos de Logs Personalizados
Definir formatos de logs personalizados en la configuración de Apache:
sudo nano /etc/apache2/apache2.conf
# Standard Combined format
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
# Common format
LogFormat "%h %l %u %t \"%r\" %>s %O" common
# Virtual host combined format
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
# Custom format with response time
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" custom_timing
# JSON format for easy parsing
LogFormat "{ \"time\":\"%t\", \"remote_addr\":\"%a\", \"status\":\"%>s\", \"request\":\"%U%q\", \"method\":\"%m\", \"bytes\":\"%B\", \"referer\":\"%{Referer}i\", \"agent\":\"%{User-Agent}i\", \"duration\":\"%D\" }" json_format
# Detailed format with SSL information
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %{SSL_PROTOCOL}x %{SSL_CIPHER}x" ssl_combined
Configuración de Logs de Virtual Host
Configurar logging por sitio:
sudo nano /etc/apache2/sites-available/example.com.conf
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/example.com
# Separate access log for this site
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
# Separate error log for this site
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
# Set error log level (debug, info, notice, warn, error, crit, alert, emerg)
LogLevel warn
# Conditional logging (skip logging for specific paths)
SetEnvIf Request_URI "^/health-check$" dontlog
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined env=!dontlog
</VirtualHost>
Niveles de Log de Error
Configurar verbosidad del log de errores de Apache:
# Global log level
LogLevel warn
# Module-specific log levels
LogLevel core:info ssl:warn rewrite:trace3
# Per-directory log level
<Directory /var/www/html/api>
LogLevel debug
</Directory>
Niveles de log (de más a menos verboso):
trace8-trace1: Mensajes de trazadebug: Mensajes de depuracióninfo: Mensajes informativosnotice: Condiciones normales pero significativaswarn: Condiciones de advertenciaerror: Condiciones de errorcrit: Condiciones críticasalert: Acción debe tomarse inmediatamenteemerg: Sistema inutilizable
Logging Condicional
Omitir logging para peticiones específicas:
# Don't log health checks and monitoring
SetEnvIf Request_URI "^/health$" dontlog
SetEnvIf Request_URI "^/status$" dontlog
SetEnvIf Request_URI "^/ping$" dontlog
# Don't log static assets (optional)
SetEnvIf Request_URI "\.(jpg|gif|png|css|js|ico)$" dontlog
# Apply conditional logging
CustomLog ${APACHE_LOG_DIR}/access.log combined env=!dontlog
# Log only errors for specific IPs
SetEnvIf Remote_Addr "192\.168\.1\." internalip
CustomLog ${APACHE_LOG_DIR}/internal-access.log combined env=internalip
Configuración de Logs de Nginx
Formatos de Logs Personalizados
Definir formatos de logs personalizados en Nginx:
sudo nano /etc/nginx/nginx.conf
http {
# Default combined format
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
# Extended format with timing information
log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
# JSON format for structured logging
log_format json_combined escape=json
'{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status": "$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"http_referrer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"http_x_forwarded_for":"$http_x_forwarded_for"'
'}';
# Performance monitoring format
log_format performance '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'rt=$request_time '
'uct=$upstream_connect_time '
'uht=$upstream_header_time '
'urt=$upstream_response_time '
'cs=$upstream_cache_status';
# SSL/TLS format
log_format ssl_combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$ssl_protocol $ssl_cipher';
# Default access log
access_log /var/log/nginx/access.log combined;
# Error log with level
error_log /var/log/nginx/error.log warn;
}
Configuración de Logs de Bloque de Servidor
Configurar logging por sitio:
sudo nano /etc/nginx/sites-available/example.com
server {
listen 80;
server_name example.com www.example.com;
root /var/www/html;
# Access log with custom format
access_log /var/log/nginx/example.com-access.log detailed;
# Error log with level
error_log /var/log/nginx/example.com-error.log warn;
# Conditional logging - skip health checks
location = /health {
access_log off;
return 200 "OK\n";
}
# Disable logging for static files (optional)
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ {
access_log off;
expires 30d;
}
# Main location
location / {
try_files $uri $uri/ =404;
}
}
Niveles de Log de Error
Configurar verbosidad del log de errores de Nginx:
# Available levels: debug, info, notice, warn, error, crit, alert, emerg
# Global error log
error_log /var/log/nginx/error.log warn;
# Server-specific error log
server {
error_log /var/log/nginx/example.com-error.log error;
}
# Debug logging for specific location
location /api {
error_log /var/log/nginx/api-debug.log debug;
}
Logging con Buffer
Mejorar rendimiento de I/O con logging en buffer:
# Buffer logs to reduce disk I/O
access_log /var/log/nginx/access.log combined buffer=32k flush=5s;
# Disable access logging for high-performance scenarios
access_log off;
Técnicas de Análisis de Logs
Comandos Básicos de Análisis de Logs
Ver entradas recientes de log:
# Apache
sudo tail -f /var/log/apache2/access.log
sudo tail -f /var/log/apache2/error.log
# Nginx
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
# View last 100 lines
sudo tail -100 /var/log/apache2/access.log
# View logs from specific time
sudo grep "10/Jan/2026" /var/log/nginx/access.log
Analizar Códigos de Estado
# Count status codes
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -nr
# Count 404 errors
grep " 404 " /var/log/apache2/access.log | wc -l
# List unique 404 URLs
grep " 404 " /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -nr | head -20
# Count 500 errors by hour
grep " 500 " /var/log/apache2/access.log | cut -d[ -f2 | cut -d] -f1 | awk -F: '{print $2":00"}' | sort -n | uniq -c
Analizar Fuentes de Tráfico
# Top IP addresses
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20
# Top user agents
awk -F'"' '{print $6}' /var/log/apache2/access.log | sort | uniq -c | sort -nr | head -10
# Top referrers
awk -F'"' '{print $4}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20
# Requests by country (requires GeoIP)
# Install: sudo apt install geoip-bin geoip-database
Analizar Patrones de Peticiones
# Most requested URLs
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20
# Requests by HTTP method
awk '{print $6}' /var/log/apache2/access.log | sort | uniq -c | sort -nr
# Requests per hour
cut -d[ -f2 /var/log/nginx/access.log | cut -d] -f1 | awk -F: '{print $2":00"}' | sort -n | uniq -c
# Bandwidth usage by IP
awk '{bytes+=$10} END {print bytes/1024/1024 " MB"}' /var/log/nginx/access.log
Detectar Problemas de Seguridad
# Detect SQL injection attempts
grep -i "select.*from\|union.*select\|insert.*into\|delete.*from" /var/log/apache2/access.log
# Detect path traversal attempts
grep -E "\.\./|\.\.%2F" /var/log/nginx/access.log
# Detect brute force login attempts
grep "POST /login" /var/log/apache2/access.log | awk '{print $1}' | sort | uniq -c | sort -nr
# Detect suspicious user agents
grep -i "sqlmap\|nikto\|nessus\|nmap" /var/log/nginx/access.log
# Find requests from specific IP
grep "192.168.1.100" /var/log/apache2/access.log
Análisis de Rendimiento
# Slowest requests (Nginx with timing in log format)
awk '{print $NF, $7}' /var/log/nginx/access.log | sort -nr | head -20
# Average response time
awk '{sum+=$NF; count++} END {print sum/count}' /var/log/nginx/access.log
# Requests taking longer than 1 second
awk '$NF > 1.0 {print $7, $NF}' /var/log/nginx/access.log
# Traffic by hour
awk '{print $4}' /var/log/apache2/access.log | cut -d: -f2 | sort -n | uniq -c
Usando GoAccess para Análisis en Tiempo Real
Instalar y usar GoAccess para análisis interactivo de logs:
# Install GoAccess
sudo apt install goaccess -y
# Real-time Apache analysis
sudo goaccess /var/log/apache2/access.log --log-format=COMBINED -o /var/www/html/report.html --real-time-html
# Real-time Nginx analysis
sudo goaccess /var/log/nginx/access.log --log-format=COMBINED -o /var/www/html/report.html --real-time-html
# Terminal-based interface
sudo goaccess /var/log/nginx/access.log --log-format=COMBINED
# Generate static HTML report
sudo goaccess /var/log/apache2/access.log --log-format=COMBINED -o /tmp/report.html
Rotación de Logs
Configuración de Logrotate
Logrotate rota, comprime y gestiona logs automáticamente:
Configuración de logrotate para Apache:
sudo nano /etc/logrotate.d/apache2
/var/log/apache2/*.log {
# Rotate daily
daily
# Keep 14 days of logs
rotate 14
# Compress old logs
compress
# Delay compression until next rotation
delaycompress
# Don't error if log file is missing
missingok
# Don't rotate if log is empty
notifempty
# Create new log with specific permissions
create 640 root adm
# Share log files between multiple processes
sharedscripts
# Reload Apache after rotation
postrotate
if /etc/init.d/apache2 status > /dev/null 2>&1; then \
/etc/init.d/apache2 reload > /dev/null 2>&1; \
fi;
endscript
# Alternatively, use systemd
# postrotate
# systemctl reload apache2 > /dev/null 2>&1 || true
# endscript
}
Configuración de logrotate para Nginx:
sudo nano /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 www-data adm
sharedscripts
# Signal Nginx to reopen log files
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 $(cat /var/run/nginx.pid)
fi
endscript
}
Programaciones de Rotación Personalizadas
Rotación semanal con retención de 8 semanas:
/var/log/nginx/example.com-access.log {
weekly
rotate 8
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
Rotación basada en tamaño:
/var/log/apache2/high-traffic-access.log {
# Rotate when log reaches 100MB
size 100M
# Keep 50 rotated logs
rotate 50
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
postrotate
systemctl reload apache2
endscript
}
Rotación Manual de Logs
# Force log rotation
sudo logrotate -f /etc/logrotate.d/nginx
# Test logrotate configuration
sudo logrotate -d /etc/logrotate.d/apache2
# Check logrotate status
cat /var/lib/logrotate/status
Alternativa: Rotación con Temporizador Systemd
Crear rotación personalizada con systemd:
# Create rotation script
sudo nano /usr/local/bin/rotate-nginx-logs.sh
#!/bin/bash
LOG_DIR="/var/log/nginx"
ARCHIVE_DIR="/var/log/nginx/archive"
DATE=$(date +%Y%m%d-%H%M%S)
mkdir -p "$ARCHIVE_DIR"
# Move current log
mv "$LOG_DIR/access.log" "$ARCHIVE_DIR/access-$DATE.log"
mv "$LOG_DIR/error.log" "$ARCHIVE_DIR/error-$DATE.log"
# Signal Nginx to create new logs
kill -USR1 $(cat /var/run/nginx.pid)
# Compress old logs
gzip "$ARCHIVE_DIR/access-$DATE.log"
gzip "$ARCHIVE_DIR/error-$DATE.log"
# Delete logs older than 30 days
find "$ARCHIVE_DIR" -name "*.log.gz" -mtime +30 -delete
# Make executable
sudo chmod +x /usr/local/bin/rotate-nginx-logs.sh
# Create systemd service
sudo nano /etc/systemd/system/nginx-log-rotation.service
[Unit]
Description=Nginx Log Rotation
[Service]
Type=oneshot
ExecStart=/usr/local/bin/rotate-nginx-logs.sh
# Create systemd timer
sudo nano /etc/systemd/system/nginx-log-rotation.timer
[Unit]
Description=Nginx Log Rotation Timer
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
# Enable and start timer
sudo systemctl enable nginx-log-rotation.timer
sudo systemctl start nginx-log-rotation.timer
# Check timer status
sudo systemctl list-timers
Logging Centralizado
Configuración de rsyslog
Enviar logs a servidor syslog centralizado:
Configurar Apache para usar syslog:
# Add to Apache configuration
CustomLog "|/usr/bin/logger -t apache -p local6.info" combined
ErrorLog syslog:local6
Configurar Nginx para usar syslog:
access_log syslog:server=192.168.1.100:514,facility=local6,tag=nginx,severity=info combined;
error_log syslog:server=192.168.1.100:514,facility=local6,tag=nginx,severity=error;
Integración con ELK Stack
Enviar logs a Elasticsearch/Logstash/Kibana:
Instalar Filebeat:
# Add Elastic repository
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
echo "deb https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
# Install Filebeat
sudo apt update
sudo apt install filebeat -y
# Configure Filebeat
sudo nano /etc/filebeat/filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
fields:
log_type: nginx_access
- type: log
enabled: true
paths:
- /var/log/nginx/error.log
fields:
log_type: nginx_error
output.elasticsearch:
hosts: ["localhost:9200"]
setup.kibana:
host: "localhost:5601"
# Enable and start Filebeat
sudo systemctl enable filebeat
sudo systemctl start filebeat
Seguridad y Cumplimiento
Asegurar Archivos de Log
# Set appropriate permissions
sudo chmod 640 /var/log/nginx/access.log
sudo chmod 640 /var/log/apache2/error.log
# Set ownership
sudo chown root:adm /var/log/nginx/*.log
sudo chown root:adm /var/log/apache2/*.log
# Restrict directory access
sudo chmod 750 /var/log/nginx
sudo chmod 750 /var/log/apache2
Anonimización de Logs
Eliminar datos sensibles de los logs:
Anonimización en Apache:
# Hash IP addresses
LogFormat "%{c}a %l %u %t \"%r\" %>s %b" anonymized
SetEnvIf Remote_Addr "(\d+\.\d+\.\d+)\.\d+" ANONYMIZED_IP=$1.0
CustomLog ${APACHE_LOG_DIR}/access.log "%{ANONYMIZED_IP}e %l %u %t \"%r\" %>s %b"
Anonimización en Nginx:
# Hash or truncate IP addresses
map $remote_addr $anonymized_ip {
~(?P<ip>\d+\.\d+\.\d+)\.\d+ $ip.0;
~(?P<ip>[^:]+:[^:]+): $ip::;
default 0.0.0.0;
}
log_format anonymized '$anonymized_ip - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log anonymized;
Consideraciones de Cumplimiento
Cumplimiento GDPR:
- Anonimizar o pseudonimizar direcciones IP
- Implementar políticas de retención de datos
- Asegurar almacenamiento y acceso a logs
- Documentar actividades de procesamiento de logs
Cumplimiento PCI DSS:
- Registrar todo acceso a datos de titulares de tarjetas
- Implementar retención de logs (mínimo 1 año)
- Asegurar almacenamiento de logs con controles de acceso
- Revisiones y monitoreo regular de logs
Solución de Problemas
Logs No se Están Creando
Verificar permisos:
# Verify log directory permissions
ls -la /var/log/nginx
ls -la /var/log/apache2
# Fix permissions
sudo mkdir -p /var/log/nginx
sudo chown www-data:adm /var/log/nginx
sudo chmod 755 /var/log/nginx
Verificar configuración:
# Test Apache configuration
sudo apache2ctl configtest
# Test Nginx configuration
sudo nginx -t
# Check if logs are defined
sudo apache2ctl -S | grep -i log
sudo nginx -T | grep -i log
Problemas de Espacio en Disco
Monitorear uso de disco:
# Check disk usage
df -h
# Check log directory size
du -sh /var/log/nginx
du -sh /var/log/apache2
# Find largest log files
find /var/log -type f -size +100M -exec ls -lh {} \;
Limpiar logs antiguos:
# Manually delete old compressed logs
sudo find /var/log/nginx -name "*.gz" -mtime +30 -delete
sudo find /var/log/apache2 -name "*.gz" -mtime +30 -delete
# Truncate current log (use with caution)
sudo truncate -s 0 /var/log/nginx/access.log
Rotación de Logs No Funciona
Depurar logrotate:
# Run logrotate in debug mode
sudo logrotate -d /etc/logrotate.d/nginx
# Force rotation
sudo logrotate -f /etc/logrotate.d/nginx
# Check logrotate status
cat /var/lib/logrotate/status
# Check for errors
sudo journalctl -u logrotate
Mejores Prácticas
- Implementar rotación de logs: Prevenir agotamiento de espacio en disco
- Monitorear tamaños de logs: Configurar alertas para crecimiento inusual
- Usar logging estructurado: Formato JSON para análisis más fácil
- Centralizar logs: Enviar a sistema dedicado de gestión de logs
- Establecer retención apropiada: Balancear cumplimiento y costos de almacenamiento
- Asegurar acceso a logs: Restringir permisos e implementar controles de acceso
- Análisis regular: Revisar logs para problemas de seguridad y rendimiento
- Automatizar alertas: Configurar notificaciones para errores críticos
- Documentar formatos de logs: Mantener documentación de formatos personalizados
- Probar configuración de logs: Verificar logging después de cambios de configuración
Conclusión
La gestión efectiva de logs de servidores web es crucial para mantener visibilidad operacional, solucionar problemas, asegurar seguridad y cumplir con requisitos de cumplimiento. Al configurar apropiadamente el logging de Apache y Nginx, implementar estrategias robustas de rotación de logs y utilizar herramientas poderosas de análisis, puedes extraer insights valiosos de tus logs de servidor web mientras gestionas el almacenamiento eficientemente.
Puntos clave:
- Configurar apropiadamente: Personalizar formatos de logs para tus necesidades específicas
- Rotar regularmente: Implementar rotación automática de logs para gestionar espacio en disco
- Analizar sistemáticamente: Usar herramientas de línea de comandos y software especializado para análisis de logs
- Centralizar cuando sea posible: Enviar logs a sistemas centralizados para mejor visibilidad
- Asegurar logs: Implementar permisos apropiados y anonimización donde sea requerido
- Monitorear continuamente: Configurar monitoreo y alertas automatizadas
Los logs son tu fuente principal de verdad para comprender el comportamiento del servidor web, diagnosticar problemas y detectar amenazas de seguridad. Invierte tiempo en configuración y análisis apropiados de logs—pagará dividendos cuando soluciones problemas de producción o investigues incidentes de seguridad.


