Guida completa ai comandi CLI per l’analisi, l’ottimizzazione e il monitoraggio SEO
In questi anni di vibe-coding va molto di moda usare il terminale, allora ho pensato: perché non fare una guida SEO da terminale?
I tool SEO con interfaccia grafica astraggono complessità, ma a un costo che raramente viene dichiarato: opacità sui dati grezzi, zero riproducibilità delle analisi, lock-in funzionale verso un singolo vendor. Il terminale opera al livello opposto. Ogni operazione è trasparente, scriptabile, verificabile. Non c’è magia dietro un output: c’è un comando documentato che produce un risultato deterministico. Per chi conduce audit tecnici su siti con architetture complesse — rendering ibrido, migliaia di template, infrastrutture multi-CDN — il terminale non è un’alternativa ai tool GUI. È il livello sottostante su cui quei tool sono costruiti.
Googlebot è un client HTTP. curl replica esattamente la sua logica di fetch dell’HTML iniziale — stessi header, stesso protocollo, stessa negoziazione TLS. Le pipeline composabili (curl | grep | awk | jq) permettono di costruire analisi custom impossibili con qualsiasi tool preconfezionato, perché non sei vincolato a ciò che qualcun altro ha deciso di mostrarti. E soprattutto: uno script bash è un audit documentato. È replicabile su qualsiasi macchina, versionabile con git, condivisibile con il team senza licenze o abbonamenti. Quando un cliente chiede “come hai ottenuto quel dato?”, la risposta è il comando stesso.
Questa guida raccoglie comandi e workflow consolidati in anni di audit tecnici su siti con architetture eterogenee — da e-commerce con milioni di URL e filtri faceted navigation a portali editoriali con rendering ibrido SSR/CSR, passando per piattaforme SaaS multi-tenant con decine di sottodomini. Ogni comando è stato testato in contesti di produzione reali, non in ambienti di laboratorio. Le soglie, le interpretazioni e i casi d’uso riportati derivano da problemi concreti risolti sul campo.
L’articolo è organizzato per fasi di un audit tecnico: si parte dall’analisi di rete e prestazioni, si passa a DNS, contenuti, crawling, header HTTP, sicurezza, log server, fino ad automazione, reporting e strumenti avanzati. Ogni sezione è indipendente — il lettore può consultare solo ciò che gli serve senza leggere l’intero documento. L’indice che segue permette di navigare direttamente alla sezione di interesse.
La guida si rivolge a professionisti SEO con esperienza su audit tecnici, sviluppatori full-stack che gestiscono aspetti SEO dell’infrastruttura e data analyst che lavorano con dati di crawling e log. Tutti i comandi sono testati su Linux e macOS (per Windows si usa WSL). Si assume familiarità con il terminale, il protocollo HTTP e i concetti SEO fondamentali: crawling, indexing, rendering, canonicalization.
Ogni affermazione tecnica presente in questa guida è supportata da fonti primarie: documentazione ufficiale Google Search Central, RFC IETF, dichiarazioni pubbliche di ingegneri Google (Gary Illyes, John Mueller, Martin Splitt), report di settore verificabili. Le soglie di riferimento per metriche come TTFB, LCP e dimensioni delle risorse sono aggiornate a febbraio 2026.
Come usare questa guida
Questa sezione spiega come eseguire i comandi e gli script presenti nel documento. Se hai già familiarità con il terminale, puoi saltarla.
Sistema operativo e ambiente
Tutti i comandi di questa guida sono scritti per terminali Unix/Linux e funzionano su:
- Linux (Ubuntu, Debian, CentOS, Arch, ecc.) — ambiente nativo, tutto funziona direttamente.
- macOS — funziona quasi tutto. Alcune utility GNU (come
grep -Pper le regex Perl) richiedono l’installazione della versione GNU tramite Homebrew:brew install grep coreutils gnu-sed. - Windows — non funziona nativamente nel Prompt dei comandi o PowerShell. Hai tre opzioni:
- WSL (Windows Subsystem for Linux) — la scelta consigliata. Installa WSL2 con Ubuntu dal Microsoft Store, poi apri il terminale Ubuntu e sei in un ambiente Linux completo.Git Bash — funziona per i comandi semplici (
curl,grep), ma ha limitazioni su script complessi.Docker — avvia un container Linux (docker run -it ubuntu bash) per un ambiente isolato e riproducibile.
- WSL (Windows Subsystem for Linux) — la scelta consigliata. Installa WSL2 con Ubuntu dal Microsoft Store, poi apri il terminale Ubuntu e sei in un ambiente Linux completo.Git Bash — funziona per i comandi semplici (
Per aprire il terminale: su macOS cerca “Terminal” in Spotlight; su Linux usa Ctrl+Alt+T; su Windows con WSL cerca “Ubuntu” nel menu Start.
Tre tipi di codice in questa guida
Nel documento trovi tre tipi di blocchi di codice con utilizzi diversi.
Tipo 1 — Comando singolo (copia-incolla diretto)
La maggior parte dei comandi nella guida sono singole righe eseguibili direttamente. Copia la riga, incollala nel terminale, premi Invio.
curl -sI https://example.com | grep -i "content-encoding"
Funzionamento: copi quella riga esatta, la incolli nel terminale, sostituisci example.com con il dominio che vuoi analizzare, premi Invio. Il risultato appare immediatamente a schermo.
Tipo 2 — Blocco multi-riga (copia-incolla dell’intero blocco)
Alcuni comandi si estendono su più righe, collegati dal carattere \ a fine riga (che dice al terminale “il comando continua nella riga successiva”) oppure racchiusi in un ciclo for/while.
curl -o /dev/null -s -w "\
DNS Lookup: %{time_namelookup}s\n\
TCP Connect: %{time_connect}s\n\
Time to First Byte:%{time_starttransfer}s\n" \
https://example.com
Funzionamento: seleziona e copia tutto il blocco (da curl fino a example.com), incollalo nel terminale in un’unica operazione, premi Invio. Il terminale riconosce i \ e tratta il tutto come un singolo comando.
Tipo 3 — Script bash (salvare come file, poi eseguire)
Gli script più lunghi iniziano con #!/bin/bash e sono pensati per essere salvati come file .sh, resi eseguibili e lanciati. Non vanno incollati riga per riga nel terminale.
Procedura:
# 1. Crea il file con un editor di testo (nano è il più semplice)
nano seo-audit.sh
# 2. Incolla l'intero contenuto dello script nell'editor
# (su nano: Ctrl+Shift+V per incollare, Ctrl+O per salvare, Ctrl+X per uscire)
# 3. Rendi il file eseguibile
chmod +x seo-audit.sh
# 4. Esegui lo script
./seo-audit.sh example.com
In alternativa, puoi creare il file in un solo passaggio senza aprire un editor:
cat > seo-audit.sh << 'FINE'
#!/bin/bash
# Qui va incollato l'intero contenuto dello script
FINE
chmod +x seo-audit.sh
./seo-audit.sh example.com
Sostituire i placeholder
In tutti i comandi della guida, i valori da personalizzare sono:
| Placeholder | Cosa inserire |
|---|---|
example.com | Il dominio del sito da analizzare |
https://example.com | L’URL completo della pagina target |
urls.txt | Un file di testo con un URL per riga, da creare tu |
API_KEY | La tua chiave API (es. Google PageSpeed Insights) |
/var/log/nginx/access.log | Il percorso reale dei log del tuo server |
Per creare un file urls.txt:
# Manualmente
nano urls.txt
# Scrivi un URL per riga, salva con Ctrl+O, esci con Ctrl+X
# Oppure in un solo comando
cat > urls.txt << 'FINE'
https://miosito.it/
https://miosito.it/chi-siamo
https://miosito.it/servizi
https://miosito.it/blog
FINE
Dipendenze e strumenti necessari
La maggior parte dei comandi usa utility preinstallate su Linux/macOS (curl, grep, awk, sed, dig, openssl). Alcuni strumenti richiedono un’installazione separata:
# Verificare se un comando è disponibile
which curl # Se non restituisce nulla, il comando non è installato
curl --version # Verificare versione e supporto (es. SSL, HTTP/2)
# Installare i tool mancanti (Ubuntu/Debian)
sudo apt update && sudo apt install -y curl wget jq nmap mtr whois dnsutils
# macOS con Homebrew
brew install curl wget jq nmap mtr whois pup
Per gli strumenti avanzati citati nella guida (Sezione 3 e 10):
# pup — parser HTML con selettori CSS (consigliato)
go install github.com/ericchiang/pup@latest # richiede Go
# oppure su macOS:
brew install pup
# Lighthouse — audit Core Web Vitals
npm install -g lighthouse # richiede Node.js
# httpie — alternativa leggibile a curl
pip install httpie # richiede Python
Per una reference più ampia dei comandi Linux utilizzabili in ambito server e web, consultare la lista dei comandi Ubuntu per la gestione di un web server.
Dove finiscono i risultati
I comandi di questa guida producono output in tre modi diversi:
- A schermo — la maggior parte dei comandi stampa il risultato direttamente nel terminale. Lo vedi e basta.
- In un file — i comandi con
>(sovrascrive) o>>(accoda) salvano l’output in un file. Esempio:> report.txtcrea il filereport.txtnella cartella corrente. - Tramite pipe
|— il simbolo|passa l’output di un comando come input al successivo. Esempio:curl -s https://example.com | grep "title"scarica la pagina e poi cerca “title” nel contenuto.
Per sapere in quale cartella ti trovi e dove verranno creati i file:
pwd # Mostra la cartella corrente
ls -la # Elenca i file nella cartella corrente
cat report.txt # Visualizza il contenuto di un file
1. Analisi di Rete e Prestazioni
L’analisi di rete è il punto di partenza di qualsiasi audit tecnico. Prima di valutare contenuti, struttura o indicizzazione, è necessario verificare che il sito sia raggiungibile, che i tempi di risposta rientrino nelle soglie accettabili e che l’infrastruttura regga il carico previsto. I dati raccolti in questa fase — TTFB, latenza, catene di redirect, comportamento sotto stress — costituiscono il baseline quantitativo su cui costruire tutte le analisi successive.
1.1 curl — Il coltellino svizzero dell’HTTP
curl è lo strumento fondamentale per qualsiasi analisi SEO da terminale. Permette di simulare richieste HTTP, misurare tempi di risposta, seguire redirect e ispezionare header.
Limite importante: curl recupera solo l’HTML iniziale (Initial HTML) restituito dal server. Su siti che utilizzano framework JavaScript (React, Vue, Angular, Next.js in modalità CSR) il contenuto visibile dall’utente (e da Googlebot) viene generato lato client dopo l’esecuzione di JS. Per l’analisi del DOM renderizzato, vedi la Sezione 3.0 — Initial HTML vs Rendered DOM.
Misurare il TTFB (Time to First Byte)
Il TTFB è una metrica diagnostica fondamentale che precede tutti i Core Web Vitals legati al caricamento. Un TTFB elevato impatta direttamente il Largest Contentful Paint (LCP). Questo comando misura ogni fase della connessione.
curl -o /dev/null -s -w "\
DNS Lookup: %{time_namelookup}s\n\
TCP Connect: %{time_connect}s\n\
TLS Handshake: %{time_appconnect}s\n\
Time to First Byte:%{time_starttransfer}s\n\
Total Time: %{time_total}s\n\
Download Speed: %{speed_download} bytes/sec\n\
HTTP Code: %{http_code}\n\
Size Download: %{size_download} bytes\n\
Redirect URL: %{redirect_url}\n" \
https://example.com
Output spiegato:
time_namelookup— Tempo di risoluzione DNS. Valori >100ms indicano DNS lento (considera Cloudflare DNS o Route53).time_connect— Tempo per stabilire la connessione TCP. Dipende dalla distanza geografica dal server.time_appconnect— Tempo per completare l’handshake TLS. Valori alti suggeriscono certificati pesanti o mancanza di TLS session resumption.time_starttransfer— Questo è il TTFB. Le soglie ufficiali Google (web.dev/articles/ttfb, aggiornato novembre 2025) sono: ≤ 800ms buono, 800ms–1.8s da migliorare, > 1.8s critico. Attenzione: il TTFB non è un Core Web Vital. Google lo classifica come metrica diagnostica. La stessa documentazione precisa: “it’s not absolutely necessary that sites meet the ‘good’ TTFB threshold, provided that it doesn’t impede their ability to score well on the metrics that matter”. Detto questo, un TTFB elevato impatta direttamente il Largest Contentful Paint, quindi va ottimizzato.time_total— Tempo totale di download. Impatta l’esperienza utente e indirettamente il crawl budget.
Nota pratica: Il TTFB misurato da curl (lab data, singola richiesta, rete locale) non include redirect e può differire significativamente dal TTFB misurato in campo (CrUX, 75° percentile su 28 giorni di dati reali). Per un confronto completo, affiancare sempre il dato di PageSpeed Insights. In un caso reale, l’analisi TTFB via curl ci ha permesso di isolare un ritardo di 800ms causato unicamente dall’handshake TLS mal configurato su un cluster Nginx — problema invisibile nei dati CrUX aggregati.
Salvare i tempi in formato CSV per analisi comparative
for url in $(cat urls.txt); do
curl -o /dev/null -s -w "$url,%{time_namelookup},%{time_connect},%{time_starttransfer},%{time_total},%{http_code}\n" "$url"
done > ttfb-report.csv
Seguire la catena di redirect
I redirect multipli rallentano il rendering e aumentano la latenza percepita, specialmente su mobile dove ogni hop richiede un nuovo DNS lookup. Questo comando mostra ogni hop.
curl -sIL -o /dev/null -w "%{url_effective}\n" https://example.com
Per vedere tutti gli hop con i rispettivi status code:
curl -sIL https://example.com 2>&1 | grep -E "^(HTTP/|Location:)"
Output esempio:
HTTP/2 301
Location: https://www.example.com/
HTTP/2 200
Interpretazione SEO — Evoluzione della posizione di Google sui redirect e PageRank:
La questione “i redirect perdono link equity?” ha una storia lunga e spesso fraintesa:
- Pre-2013: Era ampiamente accettato che i 301 causassero una perdita di PageRank. Matt Cutts (allora Head of Web Spam, Google) confermò in un video ufficiale Webmasters: “The amount of PageRank that dissipates through a 301 is currently identical to the amount of PageRank that dissipates through a link” — ovvero il decay fisiologico di qualsiasi link, non una penalità aggiuntiva.
- Luglio 2016: Gary Illyes (Google Webmaster Trends Analyst) dichiarò via Twitter che qualsiasi redirect 30x (301, 302, 3xx) non causa più alcuna perdita di PageRank. Search Engine Land titolò: “Google: There is no PageRank dilution when using 301, 302, or 30x redirects anymore”.
- 2022: John Mueller (Google Search Advocate) ribadì il concetto, aggiungendo che per massimizzare il passaggio di segnali è importante aggiornare i link interni che puntano ancora alla vecchia URL.
- Stato attuale (2026): Google usa i redirect come segnale di canonicalizzazione. Il PageRank viene consolidato sulla URL canonica, non disperso.
Conclusione pratica: Le catene di redirect vanno ridotte al minimo (best practice: massimo 1 hop), ma per ragioni di crawl budget, latenza utente (specialmente su mobile, dove ogni hop richiede un nuovo DNS lookup) e semplicità di manutenzione — non per perdita di link equity.
Simulare user-agent specifici
Fondamentale per verificare cloaking e rendering differenziato.
# Simulare Googlebot Desktop
curl -sI -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" https://example.com
# Simulare Googlebot Smartphone
# NOTA: la versione Chrome (W.X.Y.Z) si aggiorna automaticamente con Googlebot "evergreen".
# Per la stringa UA corrente, consultare:
# https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers
curl -sI -A "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/W.X.Y.Z Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" https://example.com
# Simulare Bingbot
curl -sI -A "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)" https://example.com
# Confrontare risposte tra user-agent (Googlebot vs browser reale)
diff <(curl -s -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" https://example.com | head -100) \
<(curl -s -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/131.0.0.0 Safari/537.36" https://example.com | head -100)
Caso d’uso SEO: Se le risposte differiscono significativamente tra Googlebot e un browser reale, potrebbe esserci cloaking — una violazione delle linee guida di Google. Attenzione: questo test verifica solo la risposta server-side all’UA; per il cloaking basato su IP è necessaria un’analisi diversa.
Verificare la compressione
La compressione riduce il peso delle pagine e migliora LCP.
# Verificare supporto gzip/brotli
curl -sI -H "Accept-Encoding: gzip, deflate, br" https://example.com | grep -i content-encoding
# Confrontare dimensione con e senza compressione
echo "Compressa: $(curl -s --compressed https://example.com | wc -c) bytes"
echo "Non compressa: $(curl -s https://example.com | wc -c) bytes"
1.2 wget — Download ricorsivo e mirroring
Creare un mirror locale del sito per analisi
wget --mirror \
--convert-links \
--adjust-extension \
--page-requisites \
--no-parent \
--wait=1 \
--random-wait \
--limit-rate=500k \
-e robots=off \
-U "Mozilla/5.0" \
https://example.com
Parametri spiegati:
--mirror— Attiva download ricorsivo con timestamp, necessario per analisi strutturale completa.--convert-links— Converte i link assoluti in relativi, utile per navigare il sito offline.--page-requisites— Scarica CSS, JS, immagini: essenziale per analizzare il peso reale delle pagine.--wait=1 --random-wait— Pausa casuale tra le richieste per non sovraccaricare il server.--limit-rate=500k— Limita la banda. Importante per siti di clienti: non saturare le loro risorse.
Limite: Come curl, wget scarica solo l’HTML iniziale. I contenuti generati da JavaScript non saranno presenti nel mirror.
Estrarre tutti gli URL di un sito
wget --spider --recursive --no-verbose --output-file=crawl.log https://example.com
grep -oP 'https?://[^\s]+' crawl.log | sort -u > all-urls.txt
Verificare link rotti in blocco
wget --spider --recursive --level=3 --no-verbose --output-file=spider.log https://example.com
grep -B2 "broken link" spider.log
1.3 ping e traceroute — Diagnostica di rete
Misurare la latenza del server
# Latenza base
ping -c 10 example.com
# Salvare statistiche
ping -c 100 example.com | tail -1
# Output: rtt min/avg/max/mdev = 12.345/15.678/23.456/3.210 ms
Interpretazione SEO: Latenza media >50ms per utenti nella stessa regione geografica suggerisce un hosting inadeguato o la necessità di una CDN.
Tracciare il percorso di rete
# Traceroute standard
traceroute example.com
# Con risoluzione DNS disabilitata (più veloce)
traceroute -n example.com
# Versione moderna con MTR (My TraceRoute) — combina ping e traceroute
mtr --report --report-cycles 10 example.com
Output MTR esempio:
HOST Loss% Snt Last Avg Best Wrst StDev
1. gateway.local 0.0% 10 1.2 1.5 1.0 3.2 0.7
2. isp-router.net 0.0% 10 12.3 13.1 11.5 18.2 2.1
3. cdn-edge.example 0.0% 10 15.6 16.2 14.8 22.1 2.3
Caso d’uso SEO: Identifica colli di bottiglia nella rete tra utente e server. Utile per giustificare l’adozione di una CDN a un cliente. In un caso reale, l’analisi MTR ha permesso di isolare 400ms di latenza introdotti da un hop su un peering intermedio, risolvibili spostando il PoP della CDN.
1.4 ab, siege e alternative — Test di carico
Apache Benchmark
# 1000 richieste con 10 connessioni concorrenti
ab -n 1000 -c 10 -H "Accept-Encoding: gzip" https://example.com/
# Output chiave:
# Requests per second: indica la capacità del server
# Time per request: deve restare sotto 500ms per buona UX
# Transfer rate: velocità effettiva di trasferimento
Nota: ab richiede supporto SSL compilato per URL HTTPS. Verificare con ab -h 2>&1 | grep -i ssl. Se non disponibile, usare alternative con supporto HTTPS nativo:
# hey (Go-based, supporto HTTPS nativo)
# Installare: go install github.com/rakyll/hey@latest
hey -n 1000 -c 10 https://example.com/
# wrk (benchmark HTTP ad alte prestazioni)
wrk -t4 -c10 -d30s https://example.com/
Siege — Test di carico con URL multipli
# Creare file con URL da testare
cat > urls-test.txt << 'EOF'
https://example.com/
https://example.com/chi-siamo
https://example.com/servizi
https://example.com/blog
EOF
# Test con 25 utenti concorrenti per 1 minuto
siege -c 25 -t 1M -f urls-test.txt
# Output chiave:
# Availability: deve essere 100% — drop indicano problemi di capacità
# Response time: tempo medio di risposta sotto carico
# Transaction rate: transazioni al secondo
Caso d’uso SEO: Simula cosa succede quando Googlebot effettua crawl intensivo. Se il server non regge, perde pagine nell’indice. In un audit reale, un test con siege ha evidenziato un drop di availability al 78% con soli 50 utenti concorrenti, causato da un pool di connessioni MySQL sottodimensionato.
2. Analisi DNS e Configurazione Dominio
La configurazione DNS è il livello infrastrutturale che precede qualsiasi interazione HTTP. Un DNS lento aggiunge latenza a ogni richiesta — incluso il TTFB misurato nella sezione precedente. Una configurazione errata dei record CNAME, A o delle delegation può causare problemi di canonicalizzazione tra varianti www e non-www che impattano l’indicizzazione. I comandi di questa sezione permettono di ispezionare, validare e monitorare la configurazione DNS di qualsiasi dominio.
2.1 dig — Query DNS avanzate
Verificare tutti i record DNS rilevanti per SEO
# Record A (indirizzo IPv4)
dig +short A example.com
# Record AAAA (indirizzo IPv6)
dig +short AAAA example.com
# Record CNAME
dig +short CNAME www.example.com
# Record MX (mail — rilevante per email deliverability e reputazione dominio)
dig +short MX example.com
# Record TXT (SPF, DKIM, DMARC, site verification)
dig +short TXT example.com
# Record NS (nameserver)
dig +short NS example.com
# Record SOA (Start of Authority)
dig +short SOA example.com
# Record CAA (Certificate Authority Authorization)
dig +short CAA example.com
Verificare la propagazione DNS globale
# Interrogare DNS pubblici specifici
dig @8.8.8.8 example.com # Google DNS
dig @1.1.1.1 example.com # Cloudflare DNS
dig @208.67.222.222 example.com # OpenDNS
# Verificare TTL residuo (colonna 2 dell'output)
dig example.com | awk '/^example/ {print "TTL:", $2, "secondi"}'
Interpretazione SEO: TTL troppo bassi (<300s) causano lookup DNS frequenti che rallentano il TTFB. TTL troppo alti (>86400s) rallentano le migrazioni. Un TTL tra 3600s e 14400s è generalmente il compromesso migliore.
Verificare configurazione DNSSEC
dig +dnssec example.com | grep -E "RRSIG|DNSKEY"
2.2 nslookup — Risoluzione rapida
# Risoluzione diretta
nslookup example.com
# Reverse DNS (da IP a hostname)
nslookup 93.184.216.34
# Verificare se il reverse DNS corrisponde (importante per email reputation)
host $(dig +short example.com)
2.3 whois — Informazioni registrazione dominio
# Dati completi del dominio
whois example.com
# Estrarre solo le date chiave
whois example.com | grep -iE "creation|expir|updated|registrar"
Output esempio:
Creation Date: 1995-08-14T04:00:00Z
Updated Date: 2024-08-14T07:01:44Z
Registry Expiry Date: 2025-08-13T04:00:00Z
Registrar: EXAMPLE REGISTRAR
Interpretazione SEO — La verità sull’età del dominio:
L’idea che “domini più vecchi si posizionano meglio” è uno dei miti SEO più persistenti. Google lo ha negato in più occasioni:
- 2010: Matt Cutts (Head of Web Spam, Google) dichiarò in un video ufficiale: “The difference between a domain that’s six months old versus one year old is really not that big at all”.
- 2019-2022: John Mueller (Google Search Advocate) fu ancora più esplicito, affermando che l’età del dominio ha “zero SEO benefits” e che Google non la utilizza come fattore di ranking.
- 2024: Un data leak interno di Google rivelò l’esistenza di un attributo
hostAgenel codice, riaccendendo il dibattito. Tuttavia, Google ha chiarito che non tutti gli attributi presenti nel codice sono utilizzati attivamente nel ranking.
La correlazione tra età e posizionamento esiste (domini più vecchi hanno avuto più tempo per accumulare backlink, contenuti e autorità), ma non è causalità. Uno studio Adilo su 2.000 keyword (2025) ha confermato che domini più giovani con contenuti migliori superano regolarmente domini più vecchi.
Creation Date— Utile come segnale contestuale nell’analisi competitiva e nella due diligence di domini acquisiti. Non è un fattore di ranking diretto.Expiry Date— Utile per verificare che il dominio non sia in scadenza imminente (rischio operativo). Nessuna evidenza confermata di impatto diretto sul ranking, nonostante un vecchio brevetto Google (2005) suggerisse che domini registrati per periodi lunghi potessero essere considerati più legittimi.Registrar— Informazione utile per la due diligence su domini acquisiti. Registrar specifici possono essere associati a pattern di spam in analisi forensi.
Controllo bulk di domini competitori
for domain in competitor1.com competitor2.com competitor3.com; do
echo "=== $domain ==="
whois "$domain" | grep -iE "creation|expir"
done
3. Analisi dei Contenuti e Struttura del Sito
L’analisi dei contenuti è il cuore dell’audit on-page. I comandi di questa sezione replicano ciò che Googlebot “vede” quando processa una pagina: struttura degli heading, meta tag, immagini, link interni ed esterni, dati strutturati. La distinzione tra Initial HTML e Rendered DOM (approfondita nella sotto-sezione 3.0) è il concetto più critico: determina se i comandi basati su curl sono sufficienti o se serve un headless browser. Per un’analisi complementare lato browser, consultare la guida SEO ai DevTools di Google Chrome.
3.0 Initial HTML vs Rendered DOM — Premessa fondamentale
Questo è il concetto più importante dell’intera sezione. curl (e wget) recuperano esclusivamente l’Initial HTML restituito dal server. Nel web moderno, dominato da framework JavaScript (React, Vue, Angular, Next.js CSR, Astro islands), il contenuto visibile all’utente — e quello che Googlebot indicizza dopo il rendering — può essere radicalmente diverso dall’HTML iniziale.
Contesto Googlebot: Dal 2019, Googlebot utilizza una versione “evergreen” di Chrome per il rendering JavaScript (annuncio Google I/O 2019, confermato da Martin Splitt, Developer Advocate Google). Tuttavia, il rendering avviene in una seconda fase (Web Rendering Service), potenzialmente con ritardo. Per il SEO è quindi cruciale sapere cosa c’è nell’HTML iniziale (che viene processato immediatamente) vs cosa richiede rendering JS (che entra in una coda separata).
Regola: Prima di analizzare qualsiasi sito con curl, verifica la modalità di rendering. Se il sito usa CSR, i comandi basati su curl + grep restituiranno un DOM vuoto o parziale.
Verificare se il contenuto è nel source HTML
# Se questo restituisce il testo atteso, il sito è SSR/statico: curl basta
curl -s https://example.com | grep -c "testo-visibile-nella-pagina"
# Se restituisce 0, il contenuto è renderizzato via JS: serve Headless Chrome
Estrarre il DOM renderizzato via Headless Chrome
# Metodo 1: Headless Chrome nativo (richiede Chrome/Chromium installato)
google-chrome --headless --disable-gpu --dump-dom https://example.com 2>/dev/null | grep -oiP '<title>.*?</title>'
# Metodo 2: Puppeteer CLI one-liner (richiede Node.js)
npx puppeteer-cli screenshot --url https://example.com --html > rendered.html
# Metodo 3: Playwright (alternativa cross-browser)
npx playwright-cli pdf https://example.com rendered.pdf
Nota per l’audit: Quando analizzi un sito client, il primo step è sempre determinare la strategia di rendering (SSR, CSR, SSG, ISR). Questo determina se curl è sufficiente o se serve un Headless browser.
3.1 Analisi dell’HTML e struttura heading
Estrarre la gerarchia degli heading (H1-H6) — metodo robusto
Analizzare HTML con regex (grep -oiP) è notoriamente fragile: basta un attributo multilinea, un commento HTML o un tag annidato per rompere l’output. Per un audit professionale, utilizza parser HTML dedicati.
Metodo consigliato: pup (parser CSS selector da CLI)
# Installare pup: go install github.com/ericchiang/pup@latest
# oppure: brew install pup (macOS)
# Estrarre tutti gli heading con testo
curl -s https://example.com | pup 'h1, h2, h3, h4, h5, h6 text{}'
# Estrarre heading con il tag di livello
curl -s https://example.com | pup 'h1, h2, h3, h4, h5, h6'
# Estrarre solo H1
curl -s https://example.com | pup 'h1 text{}'
Metodo alternativo con htmlq (se pup non disponibile):
# Installare: cargo install htmlq
curl -s https://example.com | htmlq 'h1, h2, h3' --text
Metodo fallback con grep (solo per SSR/HTML semplice):
curl -s https://example.com | grep -oiP '<h[1-6][^>]*>.*?</h[1-6]>' | sed 's/<[^>]*>//g'
Per un output strutturato con indentazione gerarchica:
curl -s https://example.com | grep -oiP '<h[1-6][^>]*>.*?</h[1-6]>' | \
while IFS= read -r line; do
level=$(echo "$line" | grep -oP '(?i)<h\K[1-6]')
text=$(echo "$line" | sed 's/<[^>]*>//g' | xargs)
indent=$(printf '%*s' $((level * 2)) '')
echo "${indent}H${level}: $text"
done
Caso d’uso SEO: Verifica che esista un solo H1, che la gerarchia sia logica (non H1 > H3 saltando H2), e che i titoli contengano le keyword target.
Contare gli heading per livello
curl -s https://example.com | grep -oiP '<h[1-6]' | sort | uniq -c | sort -rn
Output esempio:
1 <h1
5 <h2
12 <h3
3 <h4
3.2 Analisi dei meta tag
Estrarre title e meta description — metodo robusto
# Con pup (consigliato)
curl -s https://example.com | pup 'title text{}'
curl -s https://example.com | pup 'meta[name="description"] attr{content}'
curl -s https://example.com | pup 'link[rel="canonical"] attr{href}'
curl -s https://example.com | pup 'meta[name="robots"] attr{content}'
curl -s https://example.com | pup 'link[hreflang] attr{hreflang}'
curl -s https://example.com | pup 'meta[property^="og:"] json{}'
# Fallback con grep (funziona su HTML semplice/SSR)
curl -s https://example.com | grep -oiP '<title[^>]*>.*?</title>' | sed 's/<[^>]*>//g'
curl -s https://example.com | grep -oiP '<meta[^>]*name="description"[^>]*>' | grep -oP 'content="[^"]*"'
curl -s https://example.com | grep -oiP '<meta[^>]*name="robots"[^>]*>'
curl -s https://example.com | grep -oiP '<link[^>]*rel="canonical"[^>]*>' | grep -oP 'href="[^"]*"'
curl -s https://example.com | grep -oiP '<link[^>]*hreflang[^>]*>'
curl -s https://example.com | grep -oiP '<meta[^>]*property="og:[^"]*"[^>]*>'
Audit meta tag su URL multipli
while IFS= read -r url; do
html=$(curl -s "$url")
title=$(echo "$html" | pup 'title text{}' 2>/dev/null || echo "$html" | grep -oiP '<title>.*?</title>' | sed 's/<[^>]*>//g')
desc=$(echo "$html" | pup 'meta[name="description"] attr{content}' 2>/dev/null || echo "$html" | grep -oiP 'name="description"[^>]*content="[^"]*"' | grep -oP 'content="\K[^"]*')
canonical=$(echo "$html" | pup 'link[rel="canonical"] attr{href}' 2>/dev/null || echo "$html" | grep -oiP 'rel="canonical"[^>]*href="[^"]*"' | grep -oP 'href="\K[^"]*')
title_len=${#title}
desc_len=${#desc}
echo "$url|$title_len|$desc_len|$title|$desc|$canonical"
done < urls.txt > meta-audit.csv
Soglie SEO per il report:
- Title: 50-60 caratteri (ideale), >60 troncato in SERP.
- Description: 150-160 caratteri (ideale), >160 troncata in SERP.
- Canonical: deve puntare a sé stessa o alla versione preferita.
Nota: un BOM (Byte Order Mark) UTF-8 non rimosso nel file HTML può causare problemi di parsing dei meta tag e rendering anomalo dei primi byte della pagina. Per un approfondimento su come identificare e risolvere questa problematica, consultare l’articolo sul BOM UTF-8 nel robots.txt e nei file HTML.
3.3 Analisi delle immagini
Trovare immagini senza attributo alt
# Con pup
curl -s https://example.com | pup 'img:not([alt]) attr{src}'
# Fallback grep
curl -s https://example.com | grep -oiP '<img[^>]*>' | grep -viP 'alt="[^"]+"'
Elencare tutte le immagini con dimensioni
curl -s https://example.com | grep -oiP '<img[^>]*src="[^"]*"[^>]*>' | \
grep -oP 'src="\K[^"]*' | while read -r img; do
# Gestire URL relativi
[[ "$img" != http* ]] && img="https://example.com$img"
size=$(curl -sI "$img" | grep -i content-length | awk '{print $2}' | tr -d '\r')
type=$(curl -sI "$img" | grep -i content-type | awk '{print $2}' | tr -d '\r')
echo "$img | ${size:-N/A} bytes | $type"
done
Verificare immagini WebP/AVIF
# Verificare se il server serve WebP quando richiesto
curl -sI -H "Accept: image/webp" https://example.com/image.jpg | grep -i content-type
# Verificare supporto AVIF
curl -sI -H "Accept: image/avif" https://example.com/image.jpg | grep -i content-type
Caso d’uso SEO: Le immagini in formato next-gen (WebP, AVIF) riducono il peso della pagina fino all’80%, migliorando LCP e il punteggio Core Web Vitals.
3.4 Analisi dei link interni e esterni
Estrarre tutti i link da una pagina
# Con pup (robusto)
curl -s https://example.com | pup 'a attr{href}' | sort -u
# Solo link interni
curl -s https://example.com | pup 'a attr{href}' | grep -i "example.com" | sort -u
# Solo link esterni
curl -s https://example.com | pup 'a attr{href}' | grep -P '^https?://' | grep -vi "example.com" | sort -u
# Fallback grep
curl -s https://example.com | grep -oiP 'href="[^"]*"' | sed 's/href="//;s/"//' | sort -u
# Link con attributo nofollow
curl -s https://example.com | grep -oiP '<a[^>]*rel="[^"]*nofollow[^"]*"[^>]*href="[^"]*"'
Per tecniche di ricerca avanzata basate su operatori Google, che complementano l’analisi dei link da terminale con la verifica diretta dell’indice, consultare la guida al Google Dorking.
Contare link interni vs esterni
html=$(curl -s https://example.com)
internal=$(echo "$html" | grep -oiP 'href="[^"]*"' | grep -ci "example.com")
external=$(echo "$html" | grep -oiP 'href="https?://[^"]*"' | grep -cvi "example.com")
echo "Link interni: $internal | Link esterni: $external | Ratio: $(echo "scale=2; $internal/$external" | bc)"
3.5 Analisi dei dati strutturati (Schema.org)
Estrarre JSON-LD
curl -s https://example.com | grep -oiP '<script type="application/ld\+json">.*?</script>' | \
sed 's/<script type="application\/ld+json">//g;s/<\/script>//g' | python3 -m json.tool
Verificare la presenza di Schema specifici
curl -s https://example.com | grep -oiP '"@type"\s*:\s*"[^"]*"' | sort -u
Output esempio:
"@type": "Organization"
"@type": "WebPage"
"@type": "BreadcrumbList"
Nota (2026): Ad agosto 2023 Google ha drasticamente ridotto la visibilità dei FAQ Rich Results in SERP. L’annuncio ufficiale (Google Search Central Blog, agosto 2023) specificava che i risultati FAQ sarebbero stati mostrati “only for well-known, authoritative government and health websites”. Per tutti gli altri siti, il markup FAQPage viene ancora letto ma non genera più il rich snippet espanso. Questa modifica ha avuto un impatto significativo su CTR e traffico per molti siti che dipendevano dai FAQ snippet. Schema ancora attivi e con rich results confermati: HowTo, Product, LocalBusiness, Article, BreadcrumbList, Review, Event.
4. Crawling e Indicizzazione
Le verifiche di crawling e indicizzazione rappresentano il passaggio dall’analisi ‘statica’ del sito all’analisi di come Googlebot interagisce effettivamente con le pagine. Controllare robots.txt, sitemap XML e direttive meta robots da terminale permette di verificare direttamente — senza intermediazioni di tool terzi — cosa il crawler può raggiungere e cosa gli è precluso. L’analisi dei log del server (Sezione 7, approfondita nell’articolo dedicato all’analisi log e negative SEO) fornisce poi la verifica empirica del comportamento reale di Googlebot.
4.1 Analisi robots.txt
Scaricare e analizzare robots.txt
# Scaricare
curl -s https://example.com/robots.txt
# Verificare se una specifica URL è bloccata
curl -s https://example.com/robots.txt | grep -i "disallow" | while read -r line; do
pattern=$(echo "$line" | awk '{print $2}')
echo "Bloccato: $pattern"
done
# Estrarre le sitemap dichiarate
curl -s https://example.com/robots.txt | grep -i "sitemap:" | awk '{print $2}'
Confrontare robots.txt nel tempo
# Salvare una copia datata
curl -s https://example.com/robots.txt > "robots_$(date +%Y%m%d).txt"
# Confrontare con versione precedente
diff robots_20260101.txt robots_20260201.txt
4.2 Analisi Sitemap XML
Scaricare e contare URL nella sitemap
# Scaricare la sitemap
curl -s https://example.com/sitemap.xml > sitemap.xml
# Contare le URL
grep -c "<loc>" sitemap.xml
# Estrarre tutte le URL
grep -oP '<loc>\K[^<]*' sitemap.xml
# Estrarre URL con lastmod
paste <(grep -oP '<loc>\K[^<]*' sitemap.xml) <(grep -oP '<lastmod>\K[^<]*' sitemap.xml)
Analizzare sitemap index (sitemap di sitemap)
# Estrarre le sotto-sitemap
curl -s https://example.com/sitemap_index.xml | grep -oP '<loc>\K[^<]*' | while read -r sitemap; do
count=$(curl -s "$sitemap" | grep -c "<loc>")
echo "$sitemap: $count URLs"
done
Verificare che tutte le URL della sitemap rispondano 200
grep -oP '<loc>\K[^<]*' sitemap.xml | while read -r url; do
status=$(curl -o /dev/null -s -w "%{http_code}" "$url")
[[ "$status" != "200" ]] && echo "$status $url"
done > sitemap-errors.txt
Caso d’uso SEO: URL non-200 nella sitemap sprecano crawl budget e inviano segnali negativi a Google. Questo audit identifica immediatamente i problemi.
4.3 Verifica indicizzazione
Controllare la meta tag robots e X-Robots-Tag
# Meta robots nell'HTML
curl -s https://example.com/pagina | grep -oiP '<meta[^>]*name="robots"[^>]*>'
# X-Robots-Tag nell'header HTTP
curl -sI https://example.com/pagina | grep -i "x-robots-tag"
# Controllo bulk
while read -r url; do
meta=$(curl -s "$url" | grep -oiP 'name="robots"[^>]*content="\K[^"]*')
header=$(curl -sI "$url" | grep -i "x-robots-tag" | awk '{print $2}' | tr -d '\r')
echo "$url | meta: ${meta:-none} | header: ${header:-none}"
done < urls.txt
4.4 Gestione del crawl budget
Misurare il tempo di crawl per sezione
sections=("/" "/blog/" "/prodotti/" "/servizi/" "/chi-siamo/")
for section in "${sections[@]}"; do
ttfb=$(curl -o /dev/null -s -w "%{time_starttransfer}" "https://example.com${section}")
size=$(curl -s "https://example.com${section}" | wc -c)
echo "Sezione: $section | TTFB: ${ttfb}s | Size: $size bytes"
done
5. Analisi degli Header HTTP
Gli header HTTP sono la ‘conversazione invisibile’ tra server e client che determina caching, sicurezza, redirect e performance — tutti fattori che impattano l’esperienza utente e il comportamento del crawler. Un header Cache-Control mal configurato spreca banda e rallenta il re-crawling; un header HSTS mancante espone a downgrade attack; un Vary errato invalida la cache CDN. Per una trattazione completa degli header HTTP nel contesto SEO, consultare la guida tecnica alle intestazioni HTTP.
5.1 Header di caching
curl -sI https://example.com | grep -iE "cache-control|expires|etag|last-modified|age|vary|cf-cache"
Header e significato SEO:
| Header | Significato SEO |
|---|---|
Cache-Control: max-age=31536000 | Risorsa cacheable per 1 anno. Ideale per asset statici (CSS, JS, immagini). |
Cache-Control: no-cache | La risorsa può essere memorizzata in cache, ma deve essere rivalidata con il server a ogni utilizzo tramite ETag o If-Modified-Since (RFC 7234, sezione 5.2.2.2). Diverso da no-store. Errore comune: confondere no-cache con “non memorizzare” — in realtà il browser conserva la copia, la rivalidata prima di servirla. |
Cache-Control: no-store | Mai memorizzare. Usare solo per dati sensibili (pagine con dati personali, aree riservate). |
ETag | Identificatore univoco della risorsa. Abilita caching condizionale efficiente con rivalidazione 304. |
Vary: Accept-Encoding | Indica che la risposta varia per encoding. Necessario per corretta cache con gzip/brotli e CDN. |
CF-Cache-Status: HIT | La risorsa è servita dalla CDN Cloudflare. Riduce il TTFB. |
5.2 Header di sicurezza
curl -sI https://example.com | grep -iE "strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy|x-xss"
Controllo completo degli header di sicurezza:
url="https://example.com"
headers=$(curl -sI "$url")
check_header() {
local name="$1"
if echo "$headers" | grep -qi "$name"; then
echo "[OK] $name: $(echo "$headers" | grep -i "$name" | head -1 | cut -d: -f2-)"
else
echo "[MISS] $name: non presente"
fi
}
check_header "Strict-Transport-Security"
check_header "Content-Security-Policy"
check_header "X-Frame-Options"
check_header "X-Content-Type-Options"
check_header "Referrer-Policy"
check_header "Permissions-Policy"
Per un approfondimento su come i cookie impattano tracking, privacy e conformità normativa, e su come verificarne il comportamento tramite header HTTP, consultare la guida tecnica ai cookie.
5.3 Header di redirect e canonicalizzazione
# Verificare redirect da HTTP a HTTPS
curl -sI http://example.com | head -5
# Verificare redirect www vs non-www
curl -sI http://www.example.com | head -5
curl -sI http://example.com | head -5
# Test completo delle 4 varianti
for variant in "http://example.com" "https://example.com" "http://www.example.com" "https://www.example.com"; do
final=$(curl -sIL -o /dev/null -w "%{url_effective}" "$variant")
echo "$variant => $final"
done
Caso d’uso SEO: Tutte e 4 le varianti devono convergere su un’unica versione canonica (tipicamente https://www.example.com o https://example.com) con redirect 301.
5.4 Header di performance
# Verificare HTTP/2 o HTTP/3
curl -sI --http2 https://example.com | head -1
# Verificare supporto HTTP/3 tramite header Alt-Svc
curl -sI https://example.com | grep -i "alt-svc"
# Output atteso per HTTP/3: alt-svc: h3=":443"; ma=86400
# Verificare risorse con preload hint
curl -sI https://example.com | grep -i "link:" | grep "preload"
# Verificare 103 Early Hints (sostituto di HTTP/2 Server Push, deprecato in Chrome 106)
curl -sI https://example.com | grep -i "103"
# Header specifici CDN
curl -sI https://example.com | grep -iE "x-served-by|x-cache|server|via|cf-ray"
Nota (2026): HTTP/2 Server Push è stato rimosso da Chrome 106 (settembre 2022) dopo che i dati di utilizzo mostrarono un adoption rate inferiore all’1% e performance peggiori delle alternative nella maggior parte dei casi reali. L’intent message di Chrome (2022) citava: “No evidence of processing the server push responses” nella maggioranza dei siti monitorati. Le alternative moderne sono:
<link rel="preload">nell’HTML — per risorse critiche note a priori (font, hero image, CSS critico).- 103 Early Hints (RFC 8297) nell’header HTTP — supportato da Cloudflare (dal 2022), Fastly e Apache/Nginx recenti. Permette al browser di iniziare il preload prima che il server completi la generazione della risposta HTML.
6. Verifiche di Sicurezza
Le verifiche di sicurezza sono parte integrante di un audit tecnico SEO, non un extra opzionale. Un sito compromesso viene segnalato da Google Safe Browsing e desindicizzato; un certificato SSL scaduto blocca il crawling; protocolli TLS obsoleti aggiungono latenza all’handshake. Queste verifiche rientrano nel framework più ampio della SEO tecnica, dove sicurezza e accessibilità sono prerequisiti non negoziabili per il posizionamento.
6.1 Analisi certificato SSL/TLS
Dettagli certificato completi
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -text | grep -E "Issuer:|Not Before:|Not After:|Subject:|DNS:"
Verificare la scadenza del certificato
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | \
openssl x509 -noout -dates
Output esempio:
notBefore=Jan 1 00:00:00 2026 GMT
notAfter=Apr 1 23:59:59 2026 GMT
Script di monitoraggio scadenza certificati
domains=("example.com" "shop.example.com" "blog.example.com")
for domain in "${domains[@]}"; do
expiry=$(echo | openssl s_client -servername "$domain" -connect "$domain":443 2>/dev/null | \
openssl x509 -noout -enddate | cut -d= -f2)
days_left=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 ))
status="OK"
[[ $days_left -lt 30 ]] && status="WARNING"
[[ $days_left -lt 7 ]] && status="CRITICAL"
echo "[$status] $domain: scade il $expiry ($days_left giorni)"
done
Verificare la catena di certificati
echo | openssl s_client -showcerts -servername example.com -connect example.com:443 2>/dev/null | \
awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{print}' | \
openssl x509 -noout -subject -issuer
Verificare i protocolli TLS supportati
for proto in ssl3 tls1 tls1_1 tls1_2 tls1_3; do
result=$(echo | openssl s_client -"$proto" -connect example.com:443 2>&1)
if echo "$result" | grep -q "CONNECTED"; then
echo "[ATTIVO] $proto"
else
echo "[DISABILITATO] $proto"
fi
done
Interpretazione SEO: TLS 1.0 e 1.1 sono stati formalmente deprecati dall’IETF con la RFC 8996 (marzo 2021). Tutti i principali browser li hanno disabilitati: Chrome 84 (luglio 2020), Firefox 78, Safari 13.1. I siti che ancora li espongono mostrano un warning “Not Secure” e rischiano problemi di compatibilità. Solo TLS 1.2 e 1.3 devono restare attivi. TLS 1.3 (RFC 8446, agosto 2018) è preferibile per il minor overhead sull’handshake (1-RTT vs 2-RTT di TLS 1.2, 0-RTT in sessioni riprese), con impatto misurabile sul TTFB — tipicamente 50-100ms di risparmio per connessione.
6.2 Analisi con nmap
Scan delle porte aperte
# Scan rapido delle porte comuni
nmap -F example.com
# Scan delle porte web con versione servizi
nmap -sV -p 80,443,8080,8443 example.com
# Identificare il server web
nmap -sV --script=http-server-header -p 443 example.com
Caso d’uso SEO: Porte aperte non necessarie (come database o admin panels esposti) sono vulnerabilità. Un sito compromesso viene rapidamente desindicizzato da Google (Safe Browsing).
Verificare le cipher suite TLS
nmap --script ssl-enum-ciphers -p 443 example.com
6.3 Verifica HSTS e preload
# Verificare header HSTS
curl -sI https://example.com | grep -i "strict-transport-security"
# Output ideale:
# Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
# Controllare la preload list
curl -s "https://hstspreload.org/api/v2/status?domain=example.com"
Parametri HSTS consigliati per SEO:
max-age=63072000— 2 anni (63.072.000 secondi), il minimo richiesto per l’inclusione nella preload list di Chrome.includeSubDomains— Protegge tutti i sottodomini. Obbligatorio per la preload list.preload— Richiede l’inclusione nella preload list, che è condivisa tra Chrome, Firefox, Safari, Edge e Opera. Una volta incluso, il browser non tenterà mai una connessione HTTP al dominio — la protezione è hardcoded nel browser stesso.
Attenzione: L’inclusione nella preload list è quasi irreversibile (la rimozione richiede mesi). Assicurarsi che l’intero dominio e tutti i sottodomini siano pronti per HTTPS prima di attivare preload.
6.4 Verifica contenuti misti (Mixed Content)
# Cercare risorse HTTP in pagine HTTPS
curl -s https://example.com | grep -oiP '(src|href|action)="http://[^"]*"'
Caso d’uso SEO: I contenuti misti (risorse HTTP in pagine HTTPS) causano warning nel browser, riducono il trust e possono bloccare il rendering di risorse critiche.
7. Analisi dei Log del Server
I log del server sono la prova empirica di tutto ciò che si è ipotizzato nelle sezioni precedenti. Mostrano esattamente come Googlebot ha interagito con il sito: quali URL ha crawlato, quali ha ignorato, dove ha trovato errori, con quale frequenza è tornato. L’analisi dei log è ciò che differenzia un audit basato su ipotesi da un audit basato su dati. Per un approfondimento completo sulla metodologia, compresa la segmentazione per bot e l’incrocio con i dati GSC, consultare l’articolo dedicato all’analisi log e negative SEO.
7.1 Analisi dei log di accesso
Estrarre i crawl di Googlebot — con validazione IP
Filtrare i log basandosi esclusivamente sulla stringa User-Agent è una pratica incompleta. Fino al 30% del traffico che dichiara di essere “Googlebot” è costituito da scraper, fake bot o tool SEO che inquinano l’analisi del crawl budget.
Step 1 — Filtro base (solo User-Agent):
# Filtrare le richieste che dichiarano di essere Googlebot
grep "Googlebot" /var/log/nginx/access.log
# Contare richieste per status code
grep "Googlebot" /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c | sort -rn
# URL più richieste da Googlebot
grep "Googlebot" /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
# Frequenza di crawl nel tempo (per ora)
grep "Googlebot" /var/log/nginx/access.log | awk '{print $4}' | cut -d: -f1-2 | sort | uniq -c
Step 2 — Validazione IP (differenzia l’esperto dal principiante):
Google pubblica la lista ufficiale dei range IP di Googlebot in formato JSON. Incrociare i log con questa lista elimina i fake bot.
# Scaricare la lista ufficiale degli IP di Googlebot
curl -s https://developers.google.com/search/apis/ipranges/googlebot.json | \
jq -r '.prefixes[] | .ipv4Prefix // .ipv6Prefix' > google_ips.txt
# Estrarre gli IP unici che dichiarano "Googlebot" nei log
grep "Googlebot" /var/log/nginx/access.log | awk '{print $1}' | sort -u > claimed_googlebot_ips.txt
# Metodo Reverse DNS: validare un singolo IP
host 66.249.66.1
# Output atteso: 1.66.249.66.in-addr.arpa domain name pointer crawl-66-249-66-1.googlebot.com.
# Se il reverse DNS non contiene "googlebot.com" o "google.com", è un fake.
# Validazione bulk tramite rDNS
while read -r ip; do
rdns=$(host "$ip" 2>/dev/null | awk '{print $NF}')
if echo "$rdns" | grep -qE "(googlebot|google)\.com\.$"; then
echo "[REALE] $ip -> $rdns"
else
echo "[FAKE] $ip -> $rdns"
fi
done < claimed_googlebot_ips.txt
Nota: Google ha iniziato ad aggiornare le liste IP quotidianamente dal 2025 (annuncio di Gary Illyes, Google Analyst, motivato dal feedback dei grandi operatori di rete che necessitavano dati più freschi per il bot management). Precedentemente l’aggiornamento era settimanale. Mantenere un cron job per aggiornare google_ips.txt. Per Bingbot, il metodo di verifica ufficiale è analogo: reverse DNS deve risolvere a *.search.msn.com.
Analisi crawl per tutti i bot
# Identificare tutti i bot
grep -oiP '(Googlebot|Bingbot|DuckDuckBot|Baiduspider|YandexBot|Applebot|AhrefsBot|SemrushBot|MJ12bot|DotBot|GPTBot|ClaudeBot|Bytespider)[^"]*' \
/var/log/nginx/access.log | cut -d/ -f1 | sort | uniq -c | sort -rn
Nota (2026): Aggiunti GPTBot (OpenAI), ClaudeBot (Anthropic) e Bytespider (ByteDance/TikTok) alla lista dei bot rilevanti. Secondo la telemetria HUMAN Security (report gennaio 2026), il traffico verificato da AI crawler è cresciuto del 6.900% YoY nel 2025, con oltre l’85% delle interazioni a carattere commerciale (pagine prodotto). Inoltre, il 5.7% delle richieste che dichiarano un User-Agent AI è spoofato (fonte: HUMAN Security, analisi su 16 crawler AI). La gestione dei bot AI nel robots.txt è diventata una necessità operativa per il crawl budget.
Report giornaliero del crawl budget
log="/var/log/nginx/access.log"
today=$(date +%d/%b/%Y)
echo "=== Report Crawl Budget — $today ==="
echo ""
echo "Googlebot:"
echo " Richieste totali: $(grep "$today" "$log" | grep -c "Googlebot")"
echo " Pagine HTML: $(grep "$today" "$log" | grep "Googlebot" | grep "text/html" | wc -l)"
echo " Risorse (CSS/JS/img): $(grep "$today" "$log" | grep "Googlebot" | grep -cE "\.(css|js|png|jpg|gif|svg|woff)")"
echo " Errori 4xx: $(grep "$today" "$log" | grep "Googlebot" | awk '$9 ~ /^4/' | wc -l)"
echo " Errori 5xx: $(grep "$today" "$log" | grep "Googlebot" | awk '$9 ~ /^5/' | wc -l)"
echo ""
echo "Top 10 URL crawlate:"
grep "$today" "$log" | grep "Googlebot" | awk '{print $7}' | sort | uniq -c | sort -rn | head -10
7.2 Analisi degli errori
Top pagine con errori 404
awk '$9 == 404 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Errori 5xx nel tempo
awk '$9 ~ /^5/ {print $4}' /var/log/nginx/access.log | cut -d: -f1-2 | sort | uniq -c | sort -rn | head -10
Identificare pagine lente
# Per log con $request_time (Nginx)
awk '$NF > 2.0 {print $NF, $7}' /var/log/nginx/access.log | sort -rn | head -20
7.3 Analisi con awk avanzato
Report completo del traffico bot
awk '
/Googlebot/ { google++ }
/Bingbot/ { bing++ }
/AhrefsBot/ { ahrefs++ }
/SemrushBot/ { semrush++ }
/GPTBot/ { gptbot++ }
/ClaudeBot/ { claudebot++ }
{ total++ }
END {
printf "Traffico totale: %d richieste\n", total
printf "Googlebot: %d (%.1f%%)\n", google, (google/total)*100
printf "Bingbot: %d (%.1f%%)\n", bing, (bing/total)*100
printf "AhrefsBot: %d (%.1f%%)\n", ahrefs, (ahrefs/total)*100
printf "SemrushBot: %d (%.1f%%)\n", semrush, (semrush/total)*100
printf "GPTBot: %d (%.1f%%)\n", gptbot, (gptbot/total)*100
printf "ClaudeBot: %d (%.1f%%)\n", claudebot, (claudebot/total)*100
}' /var/log/nginx/access.log
8. Monitoraggio e Automazione
Le sezioni precedenti coprono l’analisi una tantum — l’audit che identifica i problemi. Il monitoraggio automatizzato è ciò che previene le regressioni. cron trasforma qualsiasi comando visto finora in un controllo schedulato; watch fornisce visibilità in tempo reale durante interventi critici (migrazioni, deploy); diff rileva cambiamenti non autorizzati al sito. La combinazione di questi strumenti trasforma il terminale da tool di analisi a sistema di early warning permanente.
8.1 cron — Automazione dei controlli SEO
Monitoraggio uptime ogni 5 minuti
# Aggiungere a crontab -e
*/5 * * * * curl -o /dev/null -s -w "%{http_code}" https://example.com | grep -q "200" || echo "$(date): SITO DOWN" >> /var/log/seo-monitor.log
Audit SEO giornaliero automatizzato
#!/bin/bash
# File: /usr/local/bin/daily-seo-audit.sh
DOMAIN="example.com"
URL="https://$DOMAIN"
LOG="/var/log/seo-audit/$(date +%Y%m%d).log"
mkdir -p /var/log/seo-audit
echo "=== SEO Audit $DOMAIN — $(date) ===" > "$LOG"
# 1. TTFB
ttfb=$(curl -o /dev/null -s -w "%{time_starttransfer}" "$URL")
echo "TTFB: ${ttfb}s" >> "$LOG"
# 2. Status code
status=$(curl -o /dev/null -s -w "%{http_code}" "$URL")
echo "Status: $status" >> "$LOG"
# 3. Certificato SSL
expiry=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN":443 2>/dev/null | \
openssl x509 -noout -enddate | cut -d= -f2)
days_left=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 ))
echo "SSL scadenza: $expiry ($days_left giorni)" >> "$LOG"
# 4. robots.txt accessibile
robots_status=$(curl -o /dev/null -s -w "%{http_code}" "$URL/robots.txt")
echo "robots.txt: $robots_status" >> "$LOG"
# 5. Sitemap accessibile
sitemap_status=$(curl -o /dev/null -s -w "%{http_code}" "$URL/sitemap.xml")
sitemap_urls=$(curl -s "$URL/sitemap.xml" | grep -c "<loc>")
echo "Sitemap: $sitemap_status ($sitemap_urls URLs)" >> "$LOG"
# 6. Compressione attiva
encoding=$(curl -sI -H "Accept-Encoding: gzip, br" "$URL" | grep -i content-encoding)
echo "Compressione: ${encoding:-MANCANTE}" >> "$LOG"
# 7. HSTS
hsts=$(curl -sI "$URL" | grep -i "strict-transport-security")
echo "HSTS: ${hsts:-MANCANTE}" >> "$LOG"
echo "=== Fine Audit ===" >> "$LOG"
# Crontab: 0 7 * * * /usr/local/bin/daily-seo-audit.sh
8.2 watch — Monitoraggio in tempo reale
# Monitorare status code in tempo reale (ogni 30 secondi)
watch -n 30 'curl -o /dev/null -s -w "Status: %{http_code} | TTFB: %{time_starttransfer}s\n" https://example.com'
# Monitorare la sitemap
watch -n 3600 'curl -s https://example.com/sitemap.xml | grep -c "<loc>"'
8.3 diff e inotifywait — Change detection
Monitorare cambiamenti al sito
#!/bin/bash
# Salva snapshot e confronta
URL="https://example.com"
DIR="/var/log/seo-snapshots"
mkdir -p "$DIR"
TODAY="$DIR/$(date +%Y%m%d).html"
YESTERDAY="$DIR/$(date -d yesterday +%Y%m%d).html"
curl -s "$URL" > "$TODAY"
if [ -f "$YESTERDAY" ]; then
changes=$(diff "$YESTERDAY" "$TODAY" | wc -l)
if [ "$changes" -gt 0 ]; then
echo "$(date): $changes righe cambiate" >> "$DIR/changes.log"
diff "$YESTERDAY" "$TODAY" > "$DIR/diff_$(date +%Y%m%d).txt"
fi
fi
9. Manipolazione Dati e Reporting
I comandi delle sezioni precedenti producono output grezzi — testo non strutturato, header HTTP, log line, risposte JSON. awk, sed, jq, sort e uniq li trasformano in report actionable: tabelle comparative, classifiche ordinate, metriche aggregate. Queste competenze di data wrangling da terminale sono ciò che differenzia un audit professionale da una lista di screenshot.
9.1 awk — Elaborazione dati strutturati
Analizzare un export CSV di Search Console
# Top query per click (da export GSC)
awk -F'\t' 'NR>1 {print $2, $1}' Queries.csv | sort -rn | head -20
# Query con CTR basso ma impression alto (opportunità)
awk -F'\t' 'NR>1 && $3 > 1000 && $4 < 0.02 {printf "%-50s Imp: %d CTR: %.2f%% Pos: %.1f\n", $1, $3, $4*100, $5}' Queries.csv
# Raggruppare per posizione media
awk -F'\t' 'NR>1 {
pos = int($5)
if (pos <= 3) bucket="1-3"
else if (pos <= 10) bucket="4-10"
else if (pos <= 20) bucket="11-20"
else bucket="20+"
clicks[bucket] += $2
imp[bucket] += $3
count[bucket]++
}
END {
for (b in clicks) printf "Pos %s: %d query, %d click, %d imp\n", b, count[b], clicks[b], imp[b]
}' Queries.csv
9.2 sed — Trasformazioni di testo
Pulire URL per analisi
# Rimuovere parametri UTM
sed 's/[?&]utm_[^&]*//g' urls.txt
# Rimuovere trailing slash
sed 's/\/$//' urls.txt
# Normalizzare protocollo
sed 's/^http:/https:/' urls.txt
# Estrarre path da URL completi
sed 's|https\?://[^/]*||' urls.txt
Generare regole di redirect
# Da un CSV "vecchio_url,nuovo_url" generare regole Nginx
awk -F',' '{printf "rewrite ^%s$ %s permanent;\n", $1, $2}' redirect-map.csv
# Generare .htaccess
awk -F',' '{printf "Redirect 301 %s %s\n", $1, $2}' redirect-map.csv
9.3 jq — Elaborazione dati JSON
Analizzare risposte API (es. PageSpeed Insights)
# Ottenere risultati PageSpeed Insights
curl -s "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://example.com&strategy=mobile&key=API_KEY" > psi.json
# Estrarre punteggi principali
jq '.lighthouseResult.categories | {
performance: .performance.score,
accessibility: .accessibility.score,
best_practices: ."best-practices".score,
seo: .seo.score
}' psi.json
# Estrarre metriche Core Web Vitals (LCP, CLS, INP)
jq '.lighthouseResult.audits | {
FCP: .["first-contentful-paint"].displayValue,
LCP: .["largest-contentful-paint"].displayValue,
TBT: .["total-blocking-time"].displayValue,
CLS: .["cumulative-layout-shift"].displayValue,
SI: .["speed-index"].displayValue
}' psi.json
# Estrarre INP e CWV dal campo dati reali (CrUX)
jq '.loadingExperience.metrics | {
LCP: .LARGEST_CONTENTFUL_PAINT_MS,
INP: .INTERACTION_TO_NEXT_PAINT,
CLS: .CUMULATIVE_LAYOUT_SHIFT_SCORE
}' psi.json
Nota importante — INP vs TBT vs FID:
La metrica di responsività ha attraversato un’evoluzione significativa:
- Pre-marzo 2024: Il Core Web Vital per la responsività era FID (First Input Delay), che misurava solo il ritardo del primo input dell’utente.
- Marzo 2024: Google ha ufficialmente sostituito FID con INP (Interaction to Next Paint), una metrica più completa che osserva tutte le interazioni durante l’intera visita (click, tap, keyboard) e riporta la latenza peggiore (al 75° percentile). Le soglie INP: < 200ms buono, 200-500ms da migliorare, > 500ms critico (fonte: web.dev, Google Search Central).
- TBT (Total Blocking Time) è disponibile solo nei dati di laboratorio (Lighthouse). È un buon proxy di INP in fase di sviluppo, ma per l’assessment ufficiale dei Core Web Vitals Google usa i dati di campo (CrUX) con INP.
I tre Core Web Vitals attuali (2026) sono: LCP, INP e CLS. Il superamento dell’assessment richiede che il 75° percentile di tutte e tre le metriche sia nella soglia “buono”.
Analizzare dati strutturati JSON-LD
curl -s https://example.com | \
grep -oP '<script type="application/ld\+json">\K.*?(?=</script>)' | \
jq '.'
9.4 sort, uniq, cut — Pipeline di analisi
Analisi frequenze in file di log
# Top 20 pagine per numero di visite
awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -20
# Distribuzione status code
awk '{print $9}' access.log | sort | uniq -c | sort -rn
# Top referrer
awk -F'"' '{print $4}' access.log | sort | uniq -c | sort -rn | head -20
# IP più attivi (possibili bot o scraper)
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20
9.5 xargs — Elaborazione parallela
Test massivo di URL
# Verificare status code di 1000 URL in parallelo (10 alla volta)
cat urls.txt | xargs -P 10 -I {} curl -o /dev/null -s -w "{} %{http_code}\n" {} > status-report.txt
# Scaricare header di tutte le pagine della sitemap
grep -oP '<loc>\K[^<]*' sitemap.xml | xargs -P 5 -I {} sh -c 'echo "=== {} ===" && curl -sI {}'
10. Strumenti Avanzati e Integrazioni API
I comandi nativi del terminale coprono la maggior parte delle verifiche di un audit tecnico. Questa sezione estende quelle capacità al livello successivo: Python per elaborazioni che superano le possibilità di awk e sed, API REST per interrogare direttamente Google Search Console e PageSpeed Insights, Lighthouse CLI per audit Core Web Vitals automatizzati, Screaming Frog in modalità headless per crawl su scala. Il terminale diventa il punto di orchestrazione di un ecosistema di strumenti professionali. Sullo stesso piano operativo, Claude Code porta un agent LLM dentro al terminale con la stessa filosofia: ho documentato configurazione e memoria di Claude Code per chi vuole capire dove la CLI conserva stato e contesto.
10.1 Python one-liner per SEO
Contare le parole di una pagina
curl -s https://example.com | python3 -c "
import sys, re
html = sys.stdin.read()
text = re.sub(r'<script[^>]*>.*?</script>', '', html, flags=re.DOTALL)
text = re.sub(r'<style[^>]*>.*?</style>', '', text, flags=re.DOTALL)
text = re.sub(r'<[^>]+>', ' ', text)
words = text.split()
print(f'Parole totali: {len(words)}')
"
Nota: Per pagine CSR/JS-rendered, sostituire curl con google-chrome --headless --dump-dom.
Estrarre tutti i link con anchor text
curl -s https://example.com | python3 -c "
import sys, re
html = sys.stdin.read()
links = re.findall(r'<a[^>]*href=\"([^\"]+)\"[^>]*>(.*?)</a>', html, re.DOTALL)
for href, text in links:
clean_text = re.sub(r'<[^>]+>', '', text).strip()
if clean_text:
print(f'{clean_text}\t{href}')
"
Alternativa robusta con parser DOM:
# Con Beautiful Soup (più affidabile delle regex per HTML complesso)
curl -s https://example.com | python3 -c "
import sys
from html.parser import HTMLParser
class LinkExtractor(HTMLParser):
def __init__(self):
super().__init__()
self.in_a = False
self.href = ''
self.text = ''
def handle_starttag(self, tag, attrs):
if tag == 'a':
self.in_a = True
self.href = dict(attrs).get('href', '')
self.text = ''
def handle_data(self, data):
if self.in_a:
self.text += data
def handle_endtag(self, tag):
if tag == 'a' and self.in_a:
self.in_a = False
t = self.text.strip()
if t and self.href:
print(f'{t}\t{self.href}')
parser = LinkExtractor()
parser.feed(sys.stdin.read())
"
Calcolare il rapporto testo/HTML
curl -s https://example.com | python3 -c "
import sys, re
html = sys.stdin.read()
html_size = len(html)
text = re.sub(r'<[^>]+>', '', html)
text = re.sub(r'\s+', ' ', text).strip()
text_size = len(text)
ratio = (text_size / html_size) * 100
print(f'HTML: {html_size} bytes')
print(f'Testo: {text_size} bytes')
print(f'Rapporto testo/HTML: {ratio:.1f}%')
print(f'Valutazione: {\"Buono\" if ratio > 25 else \"Da migliorare\" if ratio > 15 else \"Critico\"} (soglia: >25%)')
"
10.2 Utilizzo di API da terminale
Google Search Console API con curl
# Ottenere token OAuth2 (dopo configurazione iniziale)
# PREREQUISITO: progetto GCP con Search Console API abilitata,
# credenziali OAuth2 configurate e scope
# https://www.googleapis.com/auth/webmasters.readonly autorizzato
TOKEN=$(gcloud auth print-access-token)
# Query performance report
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"startDate": "2026-01-01",
"endDate": "2026-01-31",
"dimensions": ["query"],
"rowLimit": 25000,
"dimensionFilterGroups": [{
"filters": [{
"dimension": "country",
"operator": "equals",
"expression": "ita"
}]
}]
}' \
"https://searchconsole.googleapis.com/webmasters/v3/sites/https%3A%2F%2Fexample.com/searchAnalytics/query" | \
jq '.rows[] | {query: .keys[0], clicks: .clicks, impressions: .impressions, ctr: .ctr, position: .position}'
Google PageSpeed Insights API
# Batch test di URL
while read -r url; do
result=$(curl -s "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=$url&strategy=mobile&key=$API_KEY")
score=$(echo "$result" | jq '.lighthouseResult.categories.performance.score * 100')
lcp=$(echo "$result" | jq -r '.lighthouseResult.audits["largest-contentful-paint"].displayValue')
cls=$(echo "$result" | jq -r '.lighthouseResult.audits["cumulative-layout-shift"].displayValue')
echo "$url,$score,$lcp,$cls"
sleep 2 # Rate limiting
done < urls.txt > psi-report.csv
10.3 Screaming Frog CLI (se installato)
# Crawl da riga di comando
/path/to/ScreamingFrogSEOSpider \
--crawl https://example.com \
--headless \
--output-folder /reports/ \
--export-tabs "Internal:All,Response Codes:All,Page Titles:All" \
--save-crawl
10.4 Lighthouse CLI
# Installare Lighthouse
npm install -g lighthouse
# Audit completo
lighthouse https://example.com --output=json --output-path=./report.json --chrome-flags="--headless"
# Solo categorie specifiche
lighthouse https://example.com --only-categories=performance,seo --output=html --output-path=./seo-report.html
# Batch audit
while read -r url; do
slug=$(echo "$url" | sed 's|https://||;s|/|_|g')
lighthouse "$url" --only-categories=performance,seo --output=json --output-path="./${slug}.json" --chrome-flags="--headless"
sleep 5
done < urls.txt
10.5 httpie — Alternativa moderna a curl
# Installare: pip install httpie
# Header con output colorato e leggibile
https HEAD https://example.com
# Verificare redirect
https --follow --all HEAD http://example.com
# Verificare compressione
https HEAD https://example.com Accept-Encoding:gzip,br
10.6 Comandi combinati — Script completo di audit
#!/bin/bash
# ====================================================
# SEO AUDIT COMPLETO DA TERMINALE
# ====================================================
DOMAIN="${1:-example.com}"
URL="https://$DOMAIN"
REPORT="seo-audit-${DOMAIN}-$(date +%Y%m%d).txt"
echo "============================================" > "$REPORT"
echo " SEO AUDIT: $DOMAIN" >> "$REPORT"
echo " Data: $(date)" >> "$REPORT"
echo "============================================" >> "$REPORT"
# --- PERFORMANCE ---
echo -e "\n## PERFORMANCE\n" >> "$REPORT"
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\nTotal: %{time_total}s\nSize: %{size_download} bytes\nHTTP: %{http_code}\n" "$URL" >> "$REPORT"
# --- DNS ---
echo -e "\n## DNS\n" >> "$REPORT"
echo "A: $(dig +short A $DOMAIN)" >> "$REPORT"
echo "AAAA: $(dig +short AAAA $DOMAIN)" >> "$REPORT"
echo "NS: $(dig +short NS $DOMAIN)" >> "$REPORT"
echo "MX: $(dig +short MX $DOMAIN)" >> "$REPORT"
# --- SSL ---
echo -e "\n## SSL/TLS\n" >> "$REPORT"
expiry=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN":443 2>/dev/null | \
openssl x509 -noout -enddate | cut -d= -f2)
echo "Scadenza: $expiry" >> "$REPORT"
# --- REDIRECT ---
echo -e "\n## REDIRECT CHAIN\n" >> "$REPORT"
for v in "http://$DOMAIN" "https://$DOMAIN" "http://www.$DOMAIN" "https://www.$DOMAIN"; do
final=$(curl -sIL -o /dev/null -w "%{url_effective}" "$v" 2>/dev/null)
echo "$v => $final" >> "$REPORT"
done
# --- ROBOTS & SITEMAP ---
echo -e "\n## ROBOTS.TXT & SITEMAP\n" >> "$REPORT"
echo "robots.txt: $(curl -o /dev/null -s -w '%{http_code}' $URL/robots.txt)" >> "$REPORT"
echo "sitemap.xml: $(curl -o /dev/null -s -w '%{http_code}' $URL/sitemap.xml)" >> "$REPORT"
sitemap_count=$(curl -s "$URL/sitemap.xml" 2>/dev/null | grep -c "<loc>")
echo "URL in sitemap: $sitemap_count" >> "$REPORT"
# --- HEADER SICUREZZA ---
echo -e "\n## SECURITY HEADERS\n" >> "$REPORT"
headers=$(curl -sI "$URL")
for h in "Strict-Transport-Security" "Content-Security-Policy" "X-Frame-Options" "X-Content-Type-Options" "Referrer-Policy"; do
match=$(echo "$headers" | grep -i "$h")
if [ -n "$match" ]; then
echo "[OK] $match" >> "$REPORT"
else
echo "[MANCANTE] $h" >> "$REPORT"
fi
done
# --- COMPRESSIONE ---
echo -e "\n## COMPRESSIONE\n" >> "$REPORT"
encoding=$(curl -sI -H "Accept-Encoding: gzip, br" "$URL" | grep -i "content-encoding")
echo "${encoding:-NESSUNA COMPRESSIONE ATTIVA}" >> "$REPORT"
# --- META TAG (homepage) ---
echo -e "\n## META TAG (homepage)\n" >> "$REPORT"
html=$(curl -s "$URL")
title=$(echo "$html" | grep -oiP '<title>.*?</title>' | sed 's/<[^>]*>//g')
echo "Title: $title (${#title} caratteri)" >> "$REPORT"
echo -e "\n============================================" >> "$REPORT"
echo " Audit completato: $(date)" >> "$REPORT"
echo "============================================" >> "$REPORT"
echo "Report salvato in: $REPORT"
cat "$REPORT"
Utilizzo:
chmod +x seo-audit.sh
./seo-audit.sh example.com
./seo-audit.sh clientsite.it
Terminale e SEO: strumento complementare, non sostitutivo
Il terminale non sostituisce Screaming Frog, Google Search Console o gli altri tool commerciali dell’ecosistema SEO. Li complementa in tre aree specifiche dove nessun tool GUI può competere: verifica indipendente dei dati (i tool hanno bug, interpretazioni proprie, aggiornamenti che cambiano i risultati — il terminale restituisce il dato grezzo senza intermediazioni), automazione custom (cron + bash = monitoraggio costruito esattamente sulle esigenze del progetto, senza pagare licenze per funzionalità non utilizzate), analisi su scala di dati grezzi (log server da gigabyte, export CSV con centinaia di migliaia di righe, risposte API da elaborare in pipeline).
Il vantaggio più sottovalutato è la riproducibilità. Uno script bash è un audit documentato: descrive esattamente cosa viene verificato, come viene verificato e con quali soglie. Può essere versionato con git, condiviso con il team, eseguito su ambienti diversi, schedulato con cron. Nessun tool GUI offre questo livello di trasparenza metodologica — e in contesti enterprise, dove l’audit deve essere replicabile e verificabile da terze parti, questa trasparenza non è opzionale.
Il terminale ha limiti che è opportuno dichiarare. Non gestisce il rendering JavaScript senza un headless browser esterno (Chrome headless, Puppeteer, Playwright). Non ha una GUI per la visualizzazione dei dati — per grafici e dashboard serve esportare verso strumenti dedicati. La curva di apprendimento è ripida per chi non ha background tecnico. E per il crawling su scala (milioni di URL), tool dedicati come Screaming Frog restano più efficienti di uno spider bash artigianale.
I comandi raccolti in questa guida coprono il 90% delle verifiche tecniche di un audit SEO. Il restante 10% richiede strumenti specifici: Screaming Frog per il crawling massivo, Chrome DevTools per il debug di rendering, Google Search Console per i dati di indicizzazione e copertura. Il terminale è il collante che li unisce — estrae, trasforma, automatizza, verifica. Per il professionista SEO che lavora su architetture complesse, è uno strumento non opzionale.
Appendice A — Tabella riassuntiva comandi
| Comando | Categoria | Uso SEO principale |
|---|---|---|
curl | Rete | TTFB, header, redirect, compressione, API |
wget | Rete | Mirror, spider, broken links |
ping | Rete | Latenza server |
traceroute / mtr | Rete | Routing, colli di bottiglia |
ab / siege / hey / wrk | Rete | Stress test, capacità server |
dig | DNS | Record DNS, propagazione, TTL |
nslookup | DNS | Risoluzione rapida, reverse DNS |
whois | DNS | Età dominio, scadenza, registrar |
pup / htmlq | Contenuti | Parsing HTML con selettori CSS |
grep | Contenuti | Estrazione pattern, meta tag, link |
sed | Contenuti | Pulizia URL, trasformazioni testo |
awk | Contenuti | Analisi CSV, log, report strutturati |
jq | Contenuti | Parsing JSON, API response |
openssl | Sicurezza | Certificati SSL, protocolli TLS |
nmap | Sicurezza | Porte aperte, cipher suite |
diff | Monitoraggio | Change detection, confronto versioni |
cron | Automazione | Audit schedulati, monitoraggio |
watch | Monitoraggio | Controlli in tempo reale |
xargs | Automazione | Elaborazione parallela URL |
sort / uniq / cut | Dati | Pipeline di analisi frequenze |
lighthouse | Performance | Audit CWV, punteggio SEO |
httpie | Rete | Alternativa moderna a curl |
google-chrome --headless | Contenuti | Rendered DOM, analisi JS |
Appendice B — Soglie di riferimento SEO (aggiornate 2026)
| Metrica | Buono | Da migliorare | Critico | Note |
|---|---|---|---|---|
| TTFB | ≤ 800ms | 800ms–1.8s | > 1.8s | Non è un CWV. Fonte: web.dev (nov 2025) |
| LCP | < 2.5s | 2.5–4.0s | > 4.0s | Core Web Vital. Fonte: Google Search Central |
| CLS | < 0.1 | 0.1–0.25 | > 0.25 | Core Web Vital. Fonte: Google Search Central |
| INP | < 200ms | 200–500ms | > 500ms | CWV dal marzo 2024 (sostituisce FID) |
| TBT | < 200ms | 200–600ms | > 600ms | Solo lab data (Lighthouse), proxy di INP |
| FCP | < 1.8s | 1.8–3.0s | > 3.0s | Metrica supplementare |
| Title length | 50–60 char | 30–50 / 60–70 | < 30 / > 70 | Pixel-based in SERP, ~580px |
| Meta desc. length | 150–160 char | 120–150 | < 120 / > 160 | Google spesso riscrive |
| Rapporto testo/HTML | > 25% | 15–25% | < 15% | Indicativo, non fattore diretto |
| Redirect chain | 1 hop | 2 hop | 3+ hop | No perdita PageRank dal 2016 (Gary Illyes) |
| SSL scadenza | > 30 giorni | 7–30 giorni | < 7 giorni | Monitorare con cron |
| Tempo risposta server | < 200ms | 200ms–1s | > 1s | Audit Lighthouse |
Appendice C — Strumenti da installare
| Tool | Installazione | Uso |
|---|---|---|
pup | go install github.com/ericchiang/pup@latest | Parser HTML CLI con selettori CSS |
htmlq | cargo install htmlq | Alternativa a pup (Rust) |
jq | apt install jq | Parser JSON |
httpie | pip install httpie | HTTP client leggibile |
hey | go install github.com/rakyll/hey@latest | Benchmark HTTP con HTTPS |
mtr | apt install mtr | Traceroute interattivo |
lighthouse | npm install -g lighthouse | Audit performance/SEO |
Buon divertimento!
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