Perché analizzare i log server da riga di comando
Gli strumenti GUI per l’analisi dei log — Screaming Frog Log Analyzer, Oncrawl, Botify — funzionano bene quando il file pesa qualche centinaio di megabyte e si dispone di una workstation con RAM sufficiente. Ma nella pratica quotidiana su siti enterprise, i limiti emergono rapidamente: file da 10-50 GB generati da server ad alto traffico, necessità di analisi in tempo reale durante un deploy, accesso esclusivamente via SSH a un server di produzione senza interfaccia grafica.
In questi scenari, la pipeline tail | grep | awk su riga di comando non è un’alternativa “da smanettoni”: è l’unico strumento praticabile. Un file di log da 20 GB viene processato con consumo di RAM praticamente nullo, perché tail -f legge lo stream riga per riga senza caricare l’intero file in memoria. Nessun tool desktop può garantire questo comportamento.
Chi lavora regolarmente con la SEO da terminale sa che la riga di comando offre un vantaggio ulteriore: la componibilità. Ogni comando Unix fa una cosa sola e la fa bene. Combinandoli tramite pipe, si costruiscono analisi arbitrariamente complesse senza scrivere una sola riga di codice. Il workflow CLI non sostituisce i tool grafici: li complementa. Screaming Frog Log Analyzer rimane eccellente per analisi aggregate e visualizzazioni; la CLI è imbattibile per il monitoraggio in tempo reale, il debug rapido e l’automazione via cron.
Dall’analisi dei log di un e-commerce da 5M+ pagine crawlate al mese, ho potuto identificare in meno di 30 secondi via CLI un pattern di crawl waste su URL parametrizzati che consumava il 40% del crawl budget. Con un tool GUI, la stessa analisi avrebbe richiesto l’importazione del file (20+ minuti), il filtering e l’esportazione. In produzione, quei 20 minuti fanno la differenza.
Anatomia di una riga di log: formati access log
Formato Combined Log (Apache/Nginx)
Prima di filtrare qualsiasi cosa, è necessario comprendere la struttura di ogni riga del log. Il formato più diffuso è il Combined Log Format, standard sia per Apache che per Nginx. Ogni riga contiene campi separati da spazi, con alcune porzioni racchiuse tra virgolette o parentesi quadre.
Ecco una riga di esempio generata da un crawl di Googlebot:
66.249.66.1 - - [15/Mar/2026:10:22:45 +0100] "GET /blog/crawl-budget/ HTTP/1.1" 200 18456 "-" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.69 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
Analisi campo per campo:
| Campo | Posizione awk | Valore nell’esempio | Descrizione |
|---|---|---|---|
| IP client | $1 | 66.249.66.1 | Indirizzo IP del crawler |
| Identità | $2 | – | RFC 1413 identity (quasi sempre vuoto) |
| User autenticato | $3 | – | Username HTTP auth (vuoto per bot) |
| Timestamp | $4 | [15/Mar/2026:10:22:45 | Data e ora della richiesta |
| Timezone | $5 | +0100] | Offset fuso orario |
| Metodo + URL | $6 $7 $8 | “GET /blog/crawl-budget/ HTTP/1.1” | Richiesta HTTP completa |
| Status code | $9 | 200 | Codice di risposta HTTP |
| Bytes inviati | $10 | 18456 | Dimensione della risposta in byte |
| Referrer | $11 | “-“ | URL di provenienza |
| User-Agent | $12+ | “Mozilla/5.0 … Googlebot/2.1 …” | Identificativo del client |
Per verificare rapidamente il formato del proprio log:
head -1 /var/log/nginx/access.log
Se il formato è diverso (ad esempio JSON structured logging o un custom format), i comandi awk riportati in questo articolo andranno adattati alle posizioni corrette dei campi. Per approfondire la lettura dei log del web server, ho scritto una guida dedicata.
Identificare e verificare Googlebot nei log
Googlebot non è un singolo crawler: è una famiglia di user-agent con ruoli distinti. Ecco i principali da conoscere per l’analisi dei log:
- Googlebot/2.1 (Mobile) — Il crawler principale, basato su Chrome per Android. È il bot utilizzato per il mobile-first indexing.
- Googlebot/2.1 (Desktop) — Versione desktop, user-agent senza la stringa “Mobile”.
- Googlebot-Image/1.0 — Crawler dedicato alle immagini per Google Images.
- Googlebot-Video/1.0 — Crawler per contenuti video.
- AdsBot-Google — Verifica qualità delle landing page per Google Ads. Non rispetta robots.txt.
- Google-InspectionTool — Il crawler utilizzato dallo strumento Controllo URL in Google Search Console.
- Storebot-Google — Crawler per Google Shopping, crawla pagine prodotto.
Attenzione: filtrare i log solo per user-agent string non è sufficiente. Chiunque può impostare “Googlebot” come user-agent in un HTTP client. Per verificare che una richiesta provenga realmente da Google, è necessario eseguire un reverse DNS lookup sull’IP sorgente:
# Verifica reverse DNS di un IP presunto Googlebot
host 66.249.66.1
# Output atteso: 1.66.249.66.in-addr.arpa domain name pointer crawl-66-249-66-1.googlebot.com.
# Verifica in un singolo comando (restituisce exit code 0 se autentico)
host 66.249.66.1 | grep -q "googlebot.com\|google.com"
I range IP legittimi di Googlebot risolvono sempre a hostname *.googlebot.com o *.google.com. Google pubblica la documentazione ufficiale sulla verifica dei crawler nella guida alla verifica di Googlebot. In alternativa, Google pubblica i range IP in formato JSON all’indirizzo https://developers.google.com/static/search/apis/ipranges/googlebot.json, utilizzabile per filtraggio a livello firewall o script.
tail in profondità: opzioni avanzate per il monitoraggio log
Il comando tail è apparentemente banale, ma la scelta del flag corretto determina l’affidabilità del monitoraggio in produzione.
tail -f vs tail -F vs less +F
La differenza tra -f e -F è critica in ambienti di produzione dove i log vengono ruotati da logrotate:
- tail -f (follow file descriptor) — Segue il file descriptor aperto. Quando logrotate rinomina
access.loginaccess.log.1e crea un nuovoaccess.log, tail -f continua a leggere dal vecchio file (ora rinominato). Il monitoraggio si interrompe silenziosamente. - tail -F (follow file name) — Segue il nome del file. Quando il file viene ruotato, tail -F rileva il cambiamento, chiude il vecchio descriptor e apre il nuovo file. Emette un messaggio
tail: 'access.log' has been replaced; following new file. Questa è l’opzione corretta per il monitoraggio in produzione. - less +F — Apre il file in modalità follow simile a tail -f, ma permette di premere
Ctrl+Cper passare alla modalità interattiva di less, dove è possibile cercare pattern con/, navigare avanti e indietro, e poi tornare al follow conF. Ideale per sessioni di debug interattive.
# SBAGLIATO in produzione (si interrompe alla rotazione del log)
tail -f /var/log/nginx/access.log
# CORRETTO in produzione (gestisce la rotazione)
tail -F /var/log/nginx/access.log
# Monitoraggio interattivo con possibilità di ricerca
less +F /var/log/nginx/access.log
# Ctrl+C per uscire dal follow, poi /Googlebot per cercare, poi F per riprendere
–pid e –retry per script automatizzati
Per script di monitoraggio automatizzati, due opzioni aggiuntive sono rilevanti:
# Termina tail quando il processo specificato muore
tail -F --pid=$PID_NGINX /var/log/nginx/access.log
# Continua a riprovare se il file non esiste ancora (utile durante boot/deploy)
tail -F --retry /var/log/nginx/access.log
--pid=PID è particolarmente utile negli script di monitoraggio lanciati come sidecar process: quando il processo principale (Nginx, Apache) si ferma, anche lo script di monitoraggio termina automaticamente senza lasciare processi orfani. --retry gestisce lo scenario in cui lo script parte prima che il web server abbia creato il file di log.
Limiti di tail e quando usare alternative
tail opera su file di testo non compressi. In produzione i log più vecchi sono tipicamente compressi con gzip:
# Leggere log compressi
zcat /var/log/nginx/access.log.2.gz | grep "Googlebot" | tail -50
# Navigazione interattiva su file compressi
zless /var/log/nginx/access.log.2.gz
Per ambienti con throughput di log estremamente elevato (decine di migliaia di righe al secondo), tail può non essere sufficiente. In questi casi:
- journalctl -f -u nginx — Per sistemi che usano systemd/journald, offre filtering nativo per unit, priorità e timestamp senza pipe aggiuntive.
- inotifywait — Monitoring basato su filesystem events (inotify), più efficiente del polling di tail su sistemi Linux ad alto carico.
Pipeline CLI per analisi crawl SEO in tempo reale
Le pipeline seguenti sono pensate per il monitoraggio live del comportamento di Googlebot. Ogni comando è stato testato su log in formato Combined Log di Nginx e Apache.
Monitorare il crawl di Googlebot in tempo reale
tail -F /var/log/nginx/access.log | grep --line-buffered "Googlebot"
Il flag --line-buffered è fondamentale e viene spesso omesso nelle guide. Senza di esso, grep utilizza un buffer di output a blocchi (tipicamente 4 KB) quando il suo stdout non è un terminale ma una pipe. Il risultato è che l’output appare a blocchi irregolari anziché riga per riga in tempo reale. Con --line-buffered, grep scrive immediatamente ogni riga matchata, garantendo il comportamento real-time atteso.
Questo vale per qualsiasi pipeline dove grep è un elemento intermedio. Se l’output finale va a un file o a un altro comando, --line-buffered è sempre necessario per mantenere lo streaming in tempo reale.
Filtrare per tipo di richiesta: pagine vs risorse
Googlebot richiede sia pagine HTML sia risorse statiche (CSS, JS, immagini) necessarie per il rendering. Separare le due categorie è essenziale per capire come viene allocato il crawl budget:
# Solo richieste a pagine HTML (URL senza estensione o con .html)
tail -F access.log | awk '/Googlebot/ && /GET/ && $7 ~ /\.(html|php)$|\/[^.]*$/ {print $7, $9}'
# Solo risorse statiche (CSS, JS, immagini)
tail -F access.log | awk '/Googlebot/ && $7 ~ /\.(css|js|jpg|png|webp|svg|woff2)/ {print $7, $9}'
Il pattern \/[^.]*$ cattura gli URL senza estensione (cioè la maggior parte delle pagine HTML con URL clean). Il campo $9 aggiunge lo status code per individuare immediatamente risposte anomale.
Monitorare status code restituiti a Googlebot
# Distribuzione status code in tempo reale (Ctrl+C per risultato)
tail -F access.log | awk '/Googlebot/ {code[$9]++} END {for (c in code) print c, code[c]}'
Questo comando accumula un contatore per ogni status code e stampa il riepilogo quando si interrompe con Ctrl+C. I pattern da monitorare:
- 200 — Risposta corretta. Dovrebbe rappresentare l’80%+ delle richieste.
- 301/302 — Redirect. Percentuali superiori al 10% indicano catene di redirect o redirect non consolidati.
- 304 — Not Modified. Googlebot supporta conditional requests: un 304 è efficiente e normale.
- 404 — Not Found. Percentuali elevate segnalano link rotti interni, URL rimossi senza redirect, o sitemap obsolete.
- 5xx — Server error. Qualsiasi 5xx restituito a Googlebot è critico: se persistente, può causare deindexing progressivo delle pagine interessate.
Identificare crawl waste: top URL 404
tail -F access.log | awk '/Googlebot/ && $9 == "404" {print $7}' | sort | uniq -c | sort -rn | head -20
Questo comando mostra le 20 URL che generano più errori 404 per Googlebot. I risultati tipici includono: URL di paginazione obsoleta, vecchi slug modificati senza redirect, URL generati da plugin disattivati, URL parametrizzati da scraping o spam referrer. Ogni 404 restituito a Googlebot è una richiesta di crawl sprecata che avrebbe potuto essere utilizzata per una pagina utile. Per un’analisi più approfondita della salute del web server attraverso i log, è fondamentale correlare i 404 con le relative sorgenti (link interni, sitemap, backlink esterni).
Calcolare crawl rate in tempo reale
tail -F access.log | grep --line-buffered "Googlebot" | awk '{print substr($4,2,18)}' | uniq -c
L’output mostra il numero di richieste per secondo raggruppate per timestamp (con precisione al secondo). Il pattern substr($4,2,18) estrae data e ora dal campo timestamp nel formato 15/Mar/2026:10:22 (con precisione al minuto, modificare a 20 per la precisione al secondo).
I crawl rate variano enormemente in base alla dimensione del sito e alla percezione di Google della “crawl health” del server. Alcuni riferimenti indicativi:
| Dimensione sito | Crawl rate tipico | Crawl rate anomalo |
|---|---|---|
| Blog <1000 pagine | 1-5 req/min | >20 req/min |
| E-commerce 10K-100K | 10-60 req/min | >200 req/min |
| Portale >1M pagine | 100-500 req/min | >2000 req/min |
Un crawl rate anomalmente alto può indicare la scoperta di nuove sezioni (es. faceted navigation esposta), un bug nel robots.txt, o la presenza di loop di redirect. Un crawl rate anomalmente basso può segnalare problemi di server performance o una penalizzazione.
Analisi batch: estrarre pattern dal log completo
Il monitoraggio in tempo reale è utile per il debug live, ma per decisioni strategiche sul crawl budget è necessaria un’analisi sull’intero file di log. Le pipeline seguenti operano su file statici e producono report aggregati.
Top URL crawlate da Googlebot
grep "Googlebot" access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -50
Questa è probabilmente la singola query più informativa sull’intero log. Il risultato mostra la distribuzione del crawl budget tra le URL del sito. I pattern da cercare:
- URL di paginazione (
/page/2/,/page/3/, …) — Se dominano la classifica, la paginazione sta consumando crawl budget eccessivo. - URL con parametri (
?sort=,?filter=,?color=) — Faceted navigation non gestita. Ogni combinazione di filtri genera una URL distinta che Googlebot tenta di crawlare. - URL di risorse (
/wp-content/,/wp-json/) — Risorse statiche e endpoint API che consumano crawl budget. - Sitemap (
/sitemap.xml,/sitemap-post.xml) — Richieste regolari alle sitemap indicano che Google le considera affidabili.
Le URL in cima alla lista dovrebbero corrispondere alle pagine di maggior valore per il business. Se così non è, c’è un problema di allocazione del crawl budget da risolvere.
Distribuzione oraria del crawl
grep "Googlebot" access.log | awk '{print substr($4,14,2)}' | sort | uniq -c | sort -k2n
L’output produce una heatmap testuale su 24 righe, una per ogni ora del giorno, con il conteggio delle richieste Googlebot. Il pattern substr($4,14,2) estrae le due cifre dell’ora dal timestamp (posizione 14-15 nella stringa [15/Mar/2026:10:22:45).
L’analisi della distribuzione oraria rivela se Googlebot crawla prevalentemente durante le ore di basso traffico utente (tipicamente 02:00-06:00 UTC) o se distribuisce uniformemente il carico. Un picco pronunciato in orari specifici può indicare che Googlebot ha rilevato un pattern di aggiornamento dei contenuti e concentra il crawl in quelle finestre temporali.
Analisi per sezione del sito
grep "Googlebot" access.log | awk '{print $7}' | sed 's|/[^/]*$|/|' | sort | uniq -c | sort -rn | head -20
Questo comando raggruppa le URL per directory/sezione, rimuovendo l’ultimo segmento del path. Il risultato mostra quali sezioni del sito assorbono la maggior parte del crawl budget. Si può poi confrontare questa distribuzione con il valore di business delle sezioni: se /tag/ e /author/ consumano più crawl di /prodotti/ o /servizi/, è necessario intervenire con direttive robots.txt, noindex o canonical.
Confronto crawl Googlebot Desktop vs Mobile
grep "Googlebot" access.log | awk '/Mobile/ {m++} !/Mobile/ {d++} END {print "Mobile:", m, "Desktop:", d, "Ratio:", m/(m+d)*100 "%"}'
Dal passaggio al mobile-first indexing, completato da Google nel 2023, il rapporto atteso è di circa 90-95% mobile e 5-10% desktop. Se il rapporto è significativamente diverso — ad esempio 50/50 — è possibile che il sito non sia ancora stato migrato al mobile-first indexing (raro nel 2026 ma possibile per siti con gravi problemi di usabilità mobile) o che ci siano discrepanze nella configurazione del rendering mobile.
Diagnosi anomalie: casi reali
Crawl budget eroso da URL parametrizzati
Scenario: un e-commerce con catalogo da 50.000 prodotti e navigazione a filtri (colore, taglia, prezzo, brand). Ogni combinazione di filtri genera un URL unico. Il numero totale di URL possibili è nell’ordine dei milioni.
Pipeline diagnostica per quantificare il problema:
# Contare le richieste con parametri vs senza
grep "Googlebot" access.log | awk '{print $7}' | awk -F'?' '{if(NF>1) p++; else c++} END {print "Con parametri:", p, "Senza:", c, "Waste:", p/(p+c)*100 "%"}'
# Top pattern parametrizzati
grep "Googlebot" access.log | awk '{print $7}' | grep '?' | sed 's/=[^&]*/=/g' | sort | uniq -c | sort -rn | head -20
Il secondo comando normalizza i valori dei parametri (sostituendo ogni valore con un = vuoto) per raggruppare le URL per pattern di parametri. Il risultato rivela immediatamente quali combinazioni di filtri generano più crawl waste. Ho riscontrato casi in cui il 60-70% delle richieste Googlebot era destinato a URL parametrizzati che restituivano contenuto near-duplicate.
La soluzione tipica combina: canonical tag verso la pagina di categoria principale, disallow selettivo in robots.txt per i pattern più problematici, e implementazione di noindex sulle pagine con combinazioni di filtri a basso valore.
Redirect loop rilevato dal log
Un redirect loop si manifesta nei log come una sequenza ripetuta di 301/302 per la stessa URL o per URL che si redirezionano circolarmente:
# Identificare URL con redirect frequenti per Googlebot
grep "Googlebot" access.log | awk '$9 ~ /^30[12]$/ {print $7}' | sort | uniq -c | sort -rn | head -20
# Tracciare la sequenza temporale per un URL sospetto
grep "Googlebot" access.log | grep "/url-sospetto/" | awk '{print $4, $7, $9}' | tail -50
Le cause più comuni di redirect loop nella mia esperienza: conflitto tra redirect HTTP→HTTPS e www→non-www configurati a livelli diversi (server config + plugin), trailing slash enforcement inconsistente, e regole di rewrite che si sovrappongono dopo una migrazione. Googlebot segue un massimo di 10 redirect in una catena prima di abbandonare la richiesta.
Spike di 5xx durante il crawl
Quando Googlebot incrementa improvvisamente il crawl rate e il server non regge, si genera un pattern visibile nei log: burst di richieste seguiti da risposte 500/502/503.
# Correlare crawl rate con errori 5xx per minuto
grep "Googlebot" access.log | awk '{min=substr($4,2,18); total[min]++; if($9 ~ /^5/) err[min]++} END {for(m in total) print m, "total:", total[m], "5xx:", err[m]+0}' | sort
Se gli errori 5xx si concentrano nei minuti con crawl rate più alto, il server ha un problema di capacità sotto carico. Googlebot è progettato per ridurre automaticamente il crawl rate quando rileva errori 5xx, ma il feedback loop non è istantaneo: possono passare ore prima che il rate si adatti. Nel frattempo, le pagine che restituiscono 5xx in modo persistente rischiano il deindexing.
Rendering budget: Googlebot e risorse JS/CSS
Googlebot esegue il rendering JavaScript per indicizzare contenuto generato client-side. Questo processo richiede il download delle risorse JS e CSS referenziate nella pagina. Nei log, si manifesta come una sequenza: prima la richiesta alla pagina HTML, poi richieste multiple a file .js, .css e font.
# Rapporto pagine HTML vs risorse statiche richieste da Googlebot
grep "Googlebot" access.log | awk '$7 ~ /\.(js|css|woff2?|ttf)/ {res++} $7 !~ /\.(js|css|jpg|png|webp|svg|gif|ico|woff2?|ttf|map)/ {page++} END {print "Pagine:", page, "Risorse:", res, "Ratio:", res/page}'
Un ratio risorse/pagine molto alto (superiore a 20:1) indica che il sito carica molte risorse esterne necessarie al rendering. Per siti JS-heavy (React, Angular, Vue SSR/CSR), è fondamentale verificare che Googlebot riceva risorse critiche con status 200. Un 403 o 404 su un file JS critico per il rendering significa che Googlebot vede una pagina vuota o parziale.
Oltre tail: strumenti complementari
multitail per monitoraggio multi-file
Quando è necessario monitorare simultaneamente più file di log — ad esempio access log e error log, o log di più virtual host — multitail offre un’interfaccia split-screen con output colorato:
# Monitorare access log e error log in split-screen
multitail /var/log/nginx/access.log /var/log/nginx/error.log
# Con filtro Googlebot e coloring
multitail -e "Googlebot" /var/log/nginx/access.log -e "error" /var/log/nginx/error.log
In alternativa, con tmux o screen è possibile ottenere un risultato simile dividendo il terminale in pannelli, ciascuno con il proprio tail -F.
GoAccess per analisi interattiva
GoAccess è un analizzatore di log in tempo reale che funziona sia in terminale che generando report HTML statici. È lo strumento ideale per ottenere rapidamente una visione d’insieme del log senza costruire pipeline complesse:
# Report HTML statico
goaccess /var/log/nginx/access.log --log-format=COMBINED -o /var/www/html/report.html
# Dashboard interattiva in terminale
goaccess /var/log/nginx/access.log --log-format=COMBINED
# Solo traffico Googlebot
grep "Googlebot" access.log | goaccess --log-format=COMBINED -o googlebot-report.html
Ho scritto una guida dettagliata su come monitorare i log del web server con GoAccess, con configurazione e casi d’uso specifici per l’analisi SEO.
jq per log in formato JSON
Un numero crescente di configurazioni Nginx e stack moderni (es. Caddy, Traefik) producono log in formato JSON strutturato. In questo formato, ogni riga del log è un oggetto JSON con campi nominati, eliminando la fragilità del parsing posizionale con awk:
# Filtrare richieste Googlebot da log JSON e selezionare campi specifici
tail -F /var/log/nginx/access.json | jq --unbuffered 'select(.user_agent | test("Googlebot")) | {url: .request_uri, status: .status, bytes: .body_bytes_sent}'
# Contare status code per Googlebot
jq 'select(.user_agent | test("Googlebot")) | .status' access.json | sort | uniq -c | sort -rn
Il flag --unbuffered in jq è l’equivalente del --line-buffered di grep: senza di esso, l’output in pipe viene bufferizzato e perde il comportamento real-time. La configurazione JSON logging in Nginx si attiva con la direttiva log_format con escape JSON:
log_format json_combined escape=json
'{"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request_uri":"$request_uri",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"user_agent":"$http_user_agent"}';
Da CLI a Python per analisi su larga scala
La CLI è imbattibile per query ad hoc e monitoraggio in tempo reale, ma per analisi su scala temporale estesa (settimane o mesi di log), correlazione con dati esterni (GSC, Analytics) e visualizzazioni avanzate, Python è lo strumento più appropriato. Con pandas si possono importare e aggregare mesi di log in un DataFrame, correlare i crawl pattern con i dati di Search Console e generare visualizzazioni con matplotlib o plotly.
Il passaggio dalla CLI a Python è naturale: le stesse operazioni grep | awk | sort | uniq si traducono in operazioni pandas (df.query(), groupby(), value_counts()). Per chi volesse approfondire l’integrazione con i dati di Google Search Console, ho scritto una guida sull’uso delle API GSC con Python.
Script operativo: monitoraggio crawl Googlebot automatizzato
Lo script seguente implementa un monitor automatizzato del crawl Googlebot con logging strutturato e alerting via email. È progettato per essere eseguito come servizio systemd o via cron.
#!/bin/bash
# googlebot-monitor.sh — Monitoraggio crawl Googlebot con alerting
# Uso: ./googlebot-monitor.sh /var/log/nginx/access.log
# Requisiti: mail (mailutils), bash 4+
set -euo pipefail
# === CONFIGURAZIONE ===
LOG_FILE="${1:-/var/log/nginx/access.log}"
OUTPUT_DIR="/var/log/googlebot-monitor"
ALERT_EMAIL="[email protected]"
ERROR_THRESHOLD=50 # Soglia errori 5xx per finestra
WINDOW_SECONDS=300 # Finestra di monitoraggio (5 minuti)
REPORT_INTERVAL=3600 # Report orario
mkdir -p "$OUTPUT_DIR"
# File di output
CRAWL_LOG="$OUTPUT_DIR/crawl_$(date +%Y%m%d).log"
ERROR_LOG="$OUTPUT_DIR/errors_$(date +%Y%m%d).log"
STATS_LOG="$OUTPUT_DIR/stats_$(date +%Y%m%d).log"
# Contatori
declare -A status_counts
declare -A url_counts
error_count=0
total_count=0
window_start=$(date +%s)
log_stats() {
local now=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$now] Total: $total_count | 200: ${status_counts[200]:-0} | 301: ${status_counts[301]:-0} | 404: ${status_counts[404]:-0} | 5xx: $error_count" >> "$STATS_LOG"
# Top 10 URL crawlate
echo "[$now] Top URL:" >> "$STATS_LOG"
for url in "${!url_counts[@]}"; do
echo " ${url_counts[$url]} $url"
done | sort -rn | head -10 >> "$STATS_LOG"
# Reset contatori
declare -gA status_counts=()
declare -gA url_counts=()
error_count=0
total_count=0
}
send_alert() {
local message="$1"
local now=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$now] ALERT: $message" >> "$ERROR_LOG"
echo "$message" | mail -s "[Googlebot Monitor] ALERT - $(hostname)" "$ALERT_EMAIL" 2>/dev/null || true
}
echo "[$(date)] Monitor avviato su $LOG_FILE" >> "$CRAWL_LOG"
# Pipeline principale
tail -F "$LOG_FILE" | grep --line-buffered "Googlebot" | while IFS= read -r line; do
# Estrai campi
url=$(echo "$line" | awk '{print $7}')
status=$(echo "$line" | awk '{print $9}')
timestamp=$(echo "$line" | awk '{print $4}' | tr -d '[')
# Log ogni richiesta
echo "$timestamp $status $url" >> "$CRAWL_LOG"
# Aggiorna contatori
((total_count++)) || true
((status_counts[$status]++)) || true
((url_counts[$url]++)) || true
# Monitora errori 5xx
if [[ "$status" =~ ^5 ]]; then
((error_count++)) || true
echo "$timestamp 5xx: $status $url" >> "$ERROR_LOG"
fi
# Controlla soglia errori nella finestra
now=$(date +%s)
elapsed=$((now - window_start))
if (( elapsed >= WINDOW_SECONDS )); then
if (( error_count > ERROR_THRESHOLD )); then
send_alert "$error_count errori 5xx negli ultimi $WINDOW_SECONDS secondi. Ultimo: $status $url"
fi
window_start=$now
error_count=0
fi
# Report periodico
if (( total_count % 1000 == 0 )); then
log_stats
fi
done
Per l’esecuzione automatizzata, lo script può essere integrato come servizio systemd:
# /etc/systemd/system/googlebot-monitor.service
[Unit]
Description=Googlebot Crawl Monitor
After=nginx.service
[Service]
Type=simple
ExecStart=/opt/scripts/googlebot-monitor.sh /var/log/nginx/access.log
Restart=always
RestartSec=10
User=www-data
[Install]
WantedBy=multi-user.target
# Attivazione del servizio
sudo systemctl daemon-reload
sudo systemctl enable --now googlebot-monitor.service
# Verificare lo stato
sudo systemctl status googlebot-monitor.service
tail -f /var/log/googlebot-monitor/crawl_$(date +%Y%m%d).log
In alternativa, per un’analisi batch giornaliera via cron:
# Crontab: report giornaliero del crawl Googlebot alle 06:00
0 6 * * * /opt/scripts/googlebot-daily-report.sh | mail -s "Googlebot Daily Report - $(hostname)" [email protected]
Checklist operativa per l’analisi log SEO
Dieci verifiche da eseguire sistematicamente su ogni analisi log orientata alla SEO tecnica:
- Verificare il formato del log. Eseguire
head -1 access.loge confermare che sia Combined Log Format o identificare i campi del formato custom/JSON in uso. - Autenticare Googlebot. Eseguire reverse DNS su un campione di IP che dichiarano user-agent Googlebot. Filtrare i fake bot prima di qualsiasi analisi quantitativa.
- Analizzare la distribuzione degli status code. Calcolare le percentuali di 200, 301, 302, 304, 404, 410, 5xx restituiti a Googlebot. Un profilo sano ha >80% di 200 e 0% di 5xx.
- Identificare i top URL per crawl frequency. Le URL più crawlate devono corrispondere alle pagine di maggior valore. Se la homepage, pagine tag o paginazione dominano, c’è un problema di distribuzione del crawl budget.
- Quantificare il crawl waste da URL parametrizzati. Calcolare la percentuale di richieste Googlebot a URL con parametri query string. Valori superiori al 20% richiedono intervento.
- Verificare il rapporto mobile/desktop. Con il mobile-first indexing, il crawl Googlebot mobile dovrebbe essere >90%. Rapporti diversi indicano anomalie nella configurazione mobile del sito.
- Mappare la distribuzione oraria. Identificare le finestre di crawl intenso e verificare che il server abbia capacità sufficiente in quelle fasce.
- Controllare le risorse di rendering. Verificare che JS, CSS e font critici per il rendering restituiscano 200 a Googlebot. Un 403 su un file JS critico compromette l’indicizzazione di tutte le pagine che lo referenziano.
- Cercare catene e loop di redirect. Isolare le URL con status 301/302 e tracciare le catene. Catene con più di 2 hop sprecano crawl budget e diluiscono il link equity.
- Automatizzare il monitoraggio. Implementare uno script di monitoraggio continuo con alerting per errori 5xx e anomalie nel crawl rate. Un problema rilevato in minuti anziché in giorni previene danni significativi all’indicizzazione.
Questa checklist è il punto di partenza per qualsiasi analisi log SEO. Per ogni anomalia rilevata, la diagnosi approfondita richiede di incrociare i dati del log con quelli di Google Search Console (copertura dell’indice, errori di scansione) e con la configurazione del sito (robots.txt, sitemap, canonical, redirect rules). L’analisi dei log non è un esercizio isolato: è il layer diagnostico più vicino alla realtà del crawl, quello che mostra cosa succede davvero quando Googlebot visita il sito.
Articoli correlati
Autore
Mi chiamo Giovanni Sacheli e dal 2009 aiuto le aziende a farsi trovare online. Sono specializzato in SEO tecnica e PPC, competenze che applico quotidianamente nella mia agenzia, Searcus Swiss Sagl. Mi piace sviluppare strumenti a supporto del mio lavoro, ho creato SEOdata.app e cluster.army e co-scritto il libro SEO Audit Avanzato. Curo maniacalmente questo blog per colleghi e appassionati, dove mi "appunto" quello che imparo. Sono un NERD anni '80, motociclista e orgoglioso papà di due bambini.
Link:
Giovanni Sacheli
SEO Audit Avanzato
Searcus Swiss Sagl
SEOdata.app
cluster.army