Un web server che funziona senza log è un sistema di cui sappiamo soltanto che, in questo momento, sembra rispondere. Quando qualcosa rallenta, restituisce errori, viene sondato da uno scanner o smette di comportarsi come previsto, i log diventano la memoria tecnica con cui ricostruire l’accaduto.
Apache distingue principalmente due famiglie: access log ed error log. Il primo descrive le richieste ricevute e le risposte prodotte; il secondo contiene errori, warning e messaggi dei moduli. Sono complementari: una richiesta con HTTP 500 compare nell’access log come evento client/server, mentre l’error log può spiegare perché il backend PHP, il proxy o Apache stesso abbiano fallito.
Cosa contiene davvero una riga di access log
Il formato non è fisso. Apache usa LogFormat per definire quali informazioni registrare e CustomLog per decidere dove scriverle. Un formato classico può contenere client, data, richiesta HTTP, status, byte trasferiti, referer e user-agent.
LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
CustomLog /var/log/apache2/example-access.log combined
Alcuni token sono particolarmente utili:
%a: indirizzo client logico usato da Apache;%{c}a: indirizzo reale della connessione TCP, utile dietro proxy;%v: VirtualHost;%t: timestamp;%r: prima riga della richiesta, per esempioGET /login HTTP/1.1;%>s: status finale restituito;%b: dimensione del body della risposta;%D: tempo impiegato dalla richiesta in microsecondi.
Se carichiamo mod_logio possiamo aggiungere anche byte ricevuti e inviati con %I e %O. Non è sempre necessario, ma può essere utile quando vogliamo correlare performance e volume di traffico.
Un formato utile per il troubleshooting
LogFormat "%v %a via=%{c}a %t \"%r\" status=%>s bytes=%b time_us=%D \"%{Referer}i\" \"%{User-Agent}i\"" diagnostic
CustomLog /var/log/apache2/example-access.log diagnostic
Questo formato aggiunge il VirtualHost, distingue client logico e peer TCP e registra la durata della richiesta. È molto più utile di un log minimale quando dobbiamo capire se una lentezza nasce dal backend, se una CDN sta mascherando l’IP reale o se un singolo client sta martellando una risorsa.
Dietro reverse proxy o CDN: quale IP stiamo registrando?
Se Apache riceve le connessioni da un reverse proxy, senza configurazioni aggiuntive il client apparente sarà il proxy stesso. Per ricostruire l’indirizzo originario usiamo mod_remoteip. Ma c’è una regola fondamentale: dobbiamo fidarci dell’header Real-IP soltanto quando arriva da proxy conosciuti.
a2enmod remoteip
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 172.16.0.50
apache2ctl configtest && systemctl reload apache2
Dopo mod_remoteip, %a contiene il client ricostruito e %{c}a conserva il peer della connessione. Registrare entrambi ci permette di sapere sia chi crediamo sia il client sia attraverso quale proxy è arrivato.
Accettare X-Forwarded-For da qualunque sorgente è invece pericoloso: un client diretto potrebbe inventare l’indirizzo e contaminare log, ACL, rate limiting e sistemi di ban.
Un browser genera molte richieste, non una sola
Visitare una pagina non significa produrre una singola riga. Il browser chiede HTML, CSS, JavaScript, immagini, font, favicon, API e altre risorse. Una pagina apparentemente semplice può quindi creare decine di eventi. È il motivo per cui un access log molto frequentato diventa rapidamente rumoroso.
Non sempre vogliamo eliminare quel rumore: se stiamo diagnosticando errori 404 sugli asset, proprio quelle richieste ci interessano. Possiamo però separarle in un log dedicato.
SetEnvIf Request_URI "^/assets/" asset_request
CustomLog /var/log/apache2/example-assets.log combined env=asset_request
CustomLog /var/log/apache2/example-access.log combined env=!asset_request
La stessa tecnica può separare aree amministrative, API o endpoint particolarmente sensibili. Va usata con criterio: creare venti file diversi rende l’indagine più difficile invece che più semplice.
Log condizionali con espressioni
Apache 2.4 consente di usare espressioni direttamente in CustomLog. Possiamo quindi mantenere un log generale e, in parallelo, raccogliere soltanto gli errori HTTP.
CustomLog /var/log/apache2/example-access.log combined
CustomLog /var/log/apache2/example-errors-http.log combined "expr=%{REQUEST_STATUS} >= 400"
Oppure isolare soltanto alcuni status:
CustomLog /var/log/apache2/example-auth-errors.log combined "expr=%{REQUEST_STATUS} -in {'401','403'}"
Questo è utile per alimentare sistemi di detection, ma non dobbiamo dimenticare il contesto: un 404 su una favicon non vale quanto cinquanta 404 al secondo verso file sensibili.
ErrorLog: dove Apache racconta i problemi interni
ErrorLog /var/log/apache2/example-error.log
LogLevel warn
Il livello può essere aumentato temporaneamente per un modulo specifico:
LogLevel warn proxy:debug rewrite:trace3
Un livello molto verboso è prezioso durante un’indagine ma può produrre enormi quantità di dati e informazioni sensibili. Va quindi attivato per il tempo necessario e poi riportato a un valore normale.
Leggere i log senza annegare
# Seguire gli accessi
tail -F /var/log/apache2/example-access.log
# Solo errori HTTP
awk '$9 ~ /^[45]/ {print}' /var/log/apache2/example-access.log
# IP più presenti, su un formato con IP nel primo campo
awk '{print $1}' /var/log/apache2/example-access.log |
sort | uniq -c | sort -nr | head -20
# Richieste che contengono un path
grep -F '/wp-login.php' /var/log/apache2/example-access.log
# Errori del servizio Apache
journalctl -u apache2 --since "-30 min"
Gli esempi con awk dipendono naturalmente dal formato scelto. Se cambiamo posizione dei campi dobbiamo cambiare anche il parser. È un altro motivo per cui il formato dei log va trattato come una piccola API interna e non modificato casualmente.
Rotazione e spazio disco
Un log senza retention prima o poi riempie il filesystem. Su Debian e Ubuntu la rotazione viene normalmente gestita da logrotate. Controlliamo le policy esistenti prima di aggiungere file fuori dai percorsi standard.
cat /etc/logrotate.d/apache2
logrotate -d /etc/logrotate.conf
df -h /var/log
du -sh /var/log/apache2/* | sort -h
Se creiamo directory personalizzate dobbiamo anche curarne proprietario e permessi: Apache avverte esplicitamente che una directory di log scrivibile da utenti non privilegiati può diventare un problema di sicurezza.
Dai log all’incident response
Il logging è utile quando possiamo correlare gli eventi. Un IP che genera richieste anomale nel reverse proxy, un login SSH riuscito e una modifica a un file sensibile sono tre segnali separati; nella stessa finestra temporale possono raccontare una storia. È questo il passaggio dal semplice “tenere i log” all’incident response.
Per questo i log Apache si collegano naturalmente al nostro IR Stack e, sul fronte applicativo, al Reactive Web Firewall.
Riferimenti
Apache mod_log_config · Apache mod_remoteip · Apache Expressions.

