Header HTTP e SEO: quali legge Google e come controllarli

Header HTTP e SEO: quali legge Google e come controllarli

Quando Googlebot scarica una pagina, il codice HTML non è la prima cosa che riceve: prima arriva la risposta HTTP, e in quella risposta ci sono header che decidono se la pagina verrà scaricata di nuovo, se verrà trattata come duplicata, se verrà indicizzata affatto. Cache-Control, Last-Modified, ETag, Vary, Content-Type e l'header Link con rel=canonical sono la parte invisibile del protocollo che quasi nessuno controlla, eppure influenzano direttamente crawling e indicizzazione.

Questo articolo tratta esattamente questi header: cosa fa ciascuno dal punto di vista di un motore di ricerca, come ispezionarli con strumenti reali e quali errori sprecano crawl budget o creano segnali contraddittori. Non tratta X-Robots-Tag, che è un header a parte con una funzione specifica sui file non HTML, né la velocità di risposta del server, che è un tema di performance a monte di questi header.

Cosa sono gli header HTTP e perché contano per la SEO

Ogni risposta HTTP è composta da uno status code, un blocco di header e il corpo del documento. Gli header sono coppie chiave-valore inviate prima del contenuto: dicono al client, che sia un browser o Googlebot, come trattare quello che sta per arrivare. Non sono visibili nella pagina resa, quindi le checklist SEO più comuni li ignorano quasi del tutto.

Il problema è che alcuni di questi header hanno un impatto diretto su cosa e come Google indicizza. Un Content-Type sbagliato può far trattare una pagina HTML come un file generico da scaricare invece che da rendere. Un Cache-Control troppo aggressivo può far sì che una modifica reale resti invisibile per settimane. Un Vary mal configurato può far sembrare la stessa pagina contenuti diversi a seconda di chi la richiede.

  • Cache-Control: quanto e come una risposta può essere conservata in cache
  • Last-Modified ed ETag: i segnali che abilitano le richieste condizionali (304)
  • Vary: quali altri header cambiano il contenuto della risposta
  • Content-Type: il formato dichiarato del documento
  • Link con rel=canonical: il canonical inviato fuori dall'HTML

Cache-Control: quanto vive una risposta in cache

Cache-Control usa direttive testuali per dire a chi riceve la risposta per quanto tempo può considerarla valida senza richiederla di nuovo. Le più comuni sono max-age (secondi di validità), no-cache (si può conservare ma va rivalidata prima di riusarla), no-store (non conservare affatto) e public o private, cioè se la risposta può essere condivisa da cache intermedie come un CDN o è riservata al singolo client.

Google non tratta Cache-Control come una scadenza rigida per l'indice: la pagina resta candidata a essere ricrawlata secondo la stima di quanto cambia. Ma un max-age lungo su HTML che cambia spesso, se una cache intermedia lo rispetta alla lettera, può far arrivare a Googlebot per settimane una versione vecchia servita dalla cache invece che dall'origine: il problema non è l'indice di Google, è il livello di cache tra il server e il crawler.

La decisione pratica è separare gli asset statici, come immagini, CSS e JS con un hash nel nome file, dagli URL HTML. Sui primi un max-age lungo con immutable ha senso, perché il contenuto sotto quell'URL non cambia mai. Sugli URL HTML un TTL corto o un no-cache con rivalidazione evita che una CDN blocchi la propagazione degli aggiornamenti. Esempio realistico per un asset versionato: Cache-Control: public, max-age=31536000, immutable. Per una pagina di contenuto che cambia: Cache-Control: no-cache oppure max-age=0, must-revalidate.

Last-Modified, ETag e le richieste condizionali (304)

Quando un crawler ha già visto una pagina, può inviare una richiesta condizionale: If-Modified-Since con la data ricevuta nell'header Last-Modified, oppure If-None-Match con il valore ricevuto nell'header ETag. Se il server determina che il contenuto non è cambiato, risponde 304 Not Modified senza corpo, invece di rispedire l'intera pagina: il crawler verifica così molti più URL con lo stesso budget, perché una 304 costa una frazione di una 200 completa.

ETag e Last-Modified non sono alternative equivalenti. Last-Modified ha una risoluzione di un secondo e descrive quando il contenuto è cambiato, mentre ETag è un identificatore opaco, spesso un hash del contenuto, e può distinguere anche modifiche più piccole di un secondo. Quando entrambi sono presenti, la validazione tramite ETag viene generalmente considerata più affidabile, perché non dipende dalla precisione dell'orologio del server.

Un errore frequente nei setup con più server dietro un load balancer è generare l'ETag da qualcosa che varia per processo, come un timestamp di avvio o un id di istanza, invece che dal contenuto effettivo. Il risultato è che ogni richiesta può arrivare a un server diverso, l'ETag non corrisponde mai a quello già visto dal crawler, e ogni visita sembra una modifica reale anche se la pagina è identica: crawl budget sprecato su rivisite inutili. Lo stesso vale per un Last-Modified impostato al momento della richiesta invece che a quello della modifica reale: forzarlo non accelera l'indicizzazione, perché Google incrocia il segnale con altri e lo ignora nel tempo se si rivela inaffidabile.

Chi usa una sitemap XML con il tag lastmod dovrebbe farlo corrispondere a quello che il server dichiara nell'header Last-Modified per lo stesso URL. Se la sitemap dice che una pagina è cambiata ieri ma l'header HTTP non cambia mai, uno dei due segnali è sbagliato, e Google tende a fidarsi meno di entrambi quando sono in conflitto tra loro.

Vary: quando cambia il contenuto della risposta

Vary elenca quali header della richiesta possono far cambiare il corpo della risposta per lo stesso URL. Vary: Accept-Encoding è l'uso più comune e sicuro: dice a una cache che deve conservare versioni separate per i client che accettano compressione gzip o brotli e per quelli che non la accettano, evitando di servire contenuto compresso a chi non sa decomprimerlo.

Diventa delicato con Vary: User-Agent o Vary: Cookie. Servire markup diverso in base allo user agent, come nel dynamic serving per mobile e desktop, richiede Vary: User-Agent per dire alle cache di non mischiare le due versioni. Oggi Google raccomanda il design responsive e tratta differenze sostanziali tra ciò che vede il crawler e ciò che vede un utente come un segnale di cloaking, indipendentemente dalle intenzioni. Se un sito serve ancora contenuto differenziato per motivi tecnici legittimi, Vary: User-Agent evita almeno che una cache condivisa serva la versione sbagliata al pubblico sbagliato, ma non elimina il rischio di cloaking a monte.

Un errore opposto è dimenticare Vary: Cookie su risposte che effettivamente cambiano in base al cookie, come contenuto personalizzato, un banner di consenso che altera il DOM o una versione loggata contro una pubblica. Senza quel Vary, una cache intermedia può salvare la risposta vista da un utente con un certo stato del cookie e servirla a tutti gli altri, incluso Googlebot. Nella pratica questo produce contenuto instabile in Search Console: la stessa URL sembra restituire cose diverse a visite successive.

Content-Type: il formato che Google deve poter riconoscere

Content-Type dichiara il tipo MIME del documento e, per il testo, la codifica dei caratteri: text/html; charset=utf-8 è il valore corretto per una pagina web normale. Se il valore dichiarato non corrisponde al contenuto reale, per esempio HTML dichiarato come application/octet-stream o come application/xml, il client tratta la risposta secondo l'header, non secondo quello che sembra essere: un browser può proporre di scaricare il file invece di renderlo, e un crawler può decidere di non trattarlo come una pagina indicizzabile.

Il charset merita attenzione separata dal tipo. Se manca, o è dichiarato in modo diverso da come il testo è effettivamente codificato, per esempio UTF-8 servito con un header che dichiara ISO-8859-1, caratteri accentati e simboli possono essere interpretati male. Non è un problema puramente estetico: un motore di ricerca che deve decidere in che lingua è scritta una pagina si basa sul testo effettivamente decodificato, e testo mal decodificato è testo diverso da quello che l'autore ha scritto.

Per i file non HTML, come PDF, immagini o feed, il Content-Type corretto è quello che descrive davvero il formato, per esempio application/pdf o image/webp. Per quei file entra in gioco anche l'header X-Robots-Tag, che controlla la loro indicizzazione con regole proprie diverse da quelle delle pagine HTML e che merita un trattamento a parte.

L'header Link con rel=canonical: canonical senza toccare l'HTML

Oltre al tag link rel canonical nell'head HTML, lo stesso segnale può arrivare come header HTTP: Link: <https://esempio.it/pagina>; rel="canonical". Il vantaggio è che funziona anche su risposte che non hanno un head HTML modificabile, come un PDF servito direttamente, un'immagine o un endpoint generato da un sistema dove non si controlla facilmente il markup, perché l'header viaggia con la risposta indipendentemente dal formato del corpo.

Quando per lo stesso URL sono presenti più segnali di canonical, cioè il tag HTML, l'header HTTP e l'URL indicato nella sitemap, devono puntare tutti alla stessa destinazione. Se sono in conflitto, con l'header che dice una cosa e il tag che ne dice un'altra, Google sceglie comunque un canonical secondo la propria valutazione, ma il segnale complessivo diventa meno affidabile e il sito perde il controllo su quale versione viene scelta. Chi genera pagine con parametri di tracciamento o varianti di ordinamento sullo stesso URL base dovrebbe impostare l'header Link una sola volta a livello di server o CDN, così resta coerente anche quando il template HTML cambia.

Come controllare davvero gli header di una pagina

Il modo più diretto è una richiesta a riga di comando che mostra solo gli header di risposta: curl -sI https://esempio.it/pagina restituisce lo status code e gli header senza scaricare il corpo. Aggiungendo -A con uno user agent che imita Googlebot, per esempio curl -sI -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" https://esempio.it/pagina, si può verificare se il server si comporta diversamente per quello user agent: è esattamente il controllo da fare per escludere un cloaking involontario lato server o CDN.

Nel browser, il pannello Network degli strumenti per sviluppatori mostra gli header di ogni richiesta, incluse quelle condizionali: ricaricando la pagina una seconda volta si può vedere se la richiesta successiva riceve 304, cioè la cache ha funzionato, o 200, cioè la rivalidazione è fallita o non è implementata. Per una verifica specifica su come Google stessa ha ricevuto la pagina, lo strumento Ispezione URL di Search Console mostra lo status HTTP restituito al momento dell'ultima scansione, utile quando si sospetta che il comportamento verso Googlebot sia diverso da quello verso un browser normale.

Vale la pena controllare periodicamente gli header su un campione di URL diversi, come la homepage, una pagina di categoria, una pagina profonda e un asset statico, perché CDN e regole di caching spesso si applicano per pattern di URL: quello che è corretto sulla homepage non è garantito essere corretto tre livelli più in profondità nel sito.

Errori comuni da controllare

Alcuni problemi ricorrono più di altri perché nascono da configurazioni predefinite di CDN o server che nessuno ha rivisto per il caso specifico del sito:

  • Un CDN che ignora il Cache-Control dell'origine e applica un proprio TTL, per cui gli aggiornamenti non si propagano per ore anche quando l'header di origine è corretto
  • Un ETag generato per processo o per istanza del server invece che per contenuto, che rende inutile ogni richiesta condizionale in un'infrastruttura con più server
  • Vary: User-Agent usato per servire contenuto realmente diverso, invece di markup identico con solo differenze di rendering lato client
  • Content-Type o charset dichiarati in modo incoerente tra origine e CDN, spesso perché solo uno dei due livelli è stato aggiornato dopo una migrazione
  • Canonical HTTP e canonical HTML in conflitto dopo un redesign del template, perché l'header era stato impostato a livello di infrastruttura e nessuno lo ricontrolla quando cambia il markup

Domande frequenti

Cache-Control influisce direttamente sul ranking?

No, non è un fattore di ranking diretto. Un Cache-Control troppo aggressivo può però far sì che una cache intermedia serva a lungo una versione vecchia della pagina, ritardando indirettamente la scoperta degli aggiornamenti da parte del crawler.

Cosa succede se il server non implementa le richieste condizionali con 304?

Il crawler continua a ricevere l'intera pagina a ogni visita anche quando il contenuto non è cambiato. Non impedisce l'indicizzazione, ma consuma più crawl budget per verificare URL che restano identici nel tempo.

Vary: User-Agent basta per evitare problemi di cloaking?

No. Vary: User-Agent dice solo alle cache di non mischiare versioni diverse della risposta. Non cambia il fatto che servire contenuto sistematicamente diverso a Googlebot rispetto agli utenti venga trattato come cloaking, indipendentemente da quell'header.

Se il canonical nell'header HTTP e quello nell'HTML non coincidono, quale vince?

Google sceglie comunque un canonical secondo la propria valutazione dei segnali disponibili, ma il conflitto rende il segnale complessivo meno affidabile. La soluzione è far coincidere sempre i due valori, non sperare che uno prevalga sull'altro.

Tutti gli articoli