SEO con Cloudflare Workers

Come applicare modifiche SEO tecniche su Cloudflare Workers: l’handler fetch, HTMLRewriter per iniettare canonical/hreflang/JSON-LD, i redirect basati su KV, la Cache API contro la cache edge contro Cache-Control, il confine del cloaking e il modo in cui Bot Fight Mode può bloccare Googlebot.

Prima pubblicazione: 3 lug 2026 · Ultimo aggiornamento: 20 ago 2026 · Advanced
Lingue
1 segnale di evidenza in questa pagina

La SEO con Cloudflare Workers consiste nel fare SEO tecnica sul runtime serverless di Cloudflare: un Worker vede solo le richieste corrispondenti alla sua route e ciascuna entra in un handler fetch dove riscrivi richiesta, header della risposta e corpo della risposta. La riscrittura del corpo passa da HTMLRewriter, il meccanismo reale per iniettare un canonical, correggere hreflang o aggiungere JSON-LD senza un deployment del CMS, e deve essere idempotente nei casi di risposta mancante, duplicata e non HTML. I redirect su larga scala appartengono a KV o D1; Bulk Redirects/Rules sono più semplici per piccoli insiemi, ma scegli un solo proprietario per URL così i sistemi non entrano in conflitto. Tre cose diverse condividono la parola cache — Workers Cache API, cache edge Cloudflare e Cache-Control dell’origine — e confonderle è il motivo per cui un tag iniettato sembra non comparire: diagnostica per chiave della cache, livello, TTL e invalidazione. La regola ferrea è il cloaking: applica logica identica a Googlebot e agli utenti. Il rischio più specifico dei Workers è Bot Fight Mode, che può bloccare Googlebot su una pipeline che le allow-rule WAF non raggiungono. Pubblica ogni modifica SEO con metadati di versione registrati e rollback testato, poi verifica con GSC URL Inspection e l’header CF-Cache-Status. Vedi l’hub Edge SEO per il concetto generale.

TL;DR — Un Cloudflare Worker vede solo le richieste che corrispondono alla route configurata e intercetta ciascuna di esse in un unico handler fetch, dove esegui tre operazioni in sequenza: riscrivere la richiesta, riscrivere gli header della risposta e riscrivere il corpo della risposta tramite HTMLRewriter. Questo è il meccanismo reale dietro “iniettare un canonical” o “correggere un title”: deve essere idempotente e testato con risposte mancanti, duplicate e non HTML, non solo con il caso ideale. I redirect su larga scala vivono in KV (ricerca rapida per chiave) o D1 (relazionale); Bulk Redirects/Rules gestiscono più semplicemente gli insiemi piccoli e in genere preferisco i redirect a livello edge a quelli sul server, ma scegli un solo proprietario per ogni URL, perché un redirect del Worker, un Bulk Redirect e un redirect dell’origine possono attivarsi tutti sullo stesso percorso. Tre cose diverse condividono la parola “cache”: la Workers Cache API (caches.default), la cache edge di Cloudflare e Cache-Control dell’origine; confonderle è la causa abituale del “mio tag non è comparso”. Diagnostica la stalezza per chiave della cache, livello, TTL e invalidazione invece di andare a tentativi. Le indicazioni di Google su ETag / If-None-Match / 304 sono direttamente applicabili a un Worker che possiede la risposta. Il confine del cloaking: stessa logica per ogni richiedente. Il rischio più specifico dei Workers è la Bot Fight Mode, che gira fuori dal WAF Ruleset Engine, quindi le normali regole “allow” non la raggiungono. Pubblica ogni modifica che incide sulla SEO con metadati di versione registrati, un rollback testato e una condizione di arresto; poi verifica con GSC URL Inspection e l’header CF-Cache-Status.

Cosa è e cosa non è questo articolo

Questo è il compagno pratico, a livello di codice, dell’hub Edge SEO. L’hub contiene la definizione generale, la tabella di confronto tra piattaforme (Workers, Akamai, Fastly, Lambda@Edge, Vercel, Netlify), la decisione Snippets contro Workers e il trattamento completo della regola sul cloaking. Qui non la sto riscrivendo. Questa pagina approfondisce di un livello Cloudflare Workers: il runtime su cui gira proprio il Worker di questo sito, collegato tramite run_worker_first in wrangler.toml, con API reali invece di affermazioni vaghe sul fatto che l’edge compute possa iniettare tag.

Una nota prima del codice: Google non ha documentazione specifica per Cloudflare Workers. Le indicazioni ufficiali che governano questo tema (policy sul cloaking, caching HTTP, crawling tramite CDN) sono generali e valgono per qualunque implementazione edge. Preferisco dirlo chiaramente invece di far intendere che esista un documento Google che non esiste.

Come si colloca un Worker nel percorso richiesta/risposta

Un Worker è uno script serverless che gira su isolate V8. Ogni richiesta instradata entra tramite un handler fetch. Evidence for this claim A Cloudflare Worker receives HTTP requests through a fetch handler. Scope: Cloudflare Workers handlers. Confidence: high · Verified: Cloudflare Workers: Fetch handler L’espressione “instradata verso” in quella frase ha un significato concreto: un Worker vede solo le richieste che corrispondono alla sua route o al suo custom domain; tutto il resto non raggiunge mai l’handler fetch. Se due route possono corrispondere allo stesso URL, prevale il pattern più specifico. Prima di fidarti del comportamento di un Worker per un determinato URL, verifica quindi che la route corrisponda davvero e controlla quale versione pubblicata sia attiva su quella route (gli ambienti Wrangler e i rollout graduali fanno sì che la versione che serve il traffico non sia sempre quella nell’editor). Dentro l’handler puoi eseguire tre operazioni distinte, in ordine:

  1. Riscrivere la richiesta prima che raggiunga l’origine.
  2. Riscrivere gli header della risposta mentre torna indietro.
  3. Riscrivere il corpo della risposta tramite HTMLRewriter.

Ecco la forma minima:

export default {
  async fetch(request, env, ctx) {
    // 1. (optionally) inspect/modify the request
    const response = await fetch(request);   // hit the origin

    // 2. rewrite headers
    const headers = new Headers(response.headers);
    headers.set("X-Robots-Tag", "index, follow");

    // 3. rewrite the body with HTMLRewriter (see next section)
    return new Response(response.body, { ...response, headers });
  },
};

Il team di SALT.agency, che ha coniato “edge SEO” a partire dalla ricerca sui Cloudflare Workers, ha costruito i propri strumenti come una catena di filtri: un filtro per la richiesta, uno per la risposta e uno per il corpo. È lo stesso modello in tre fasi, solo con nomi diversi. Tenere separate nella mente queste tre fasi mantiene leggibile un Worker.

Riscrivere l’HTML con HTMLRewriter

HTMLRewriter è il parser HTML in streaming di Cloudflare ed è l’API reale dietro ogni trucco del tipo “inietta un tag”. Evidence for this claim Cloudflare HTMLRewriter provides selector-based handlers that can transform streamed HTML elements. Scope: Cloudflare Workers HTMLRewriter API. Confidence: high · Verified: Cloudflare Workers: HTMLRewriter Registri handler di elementi con .on(selector, handler) e l’handler riceve getAttribute / setAttribute, prepend / append, setInnerContent e replace. Poiché lavora in streaming, non devi mantenere in memoria l’intero documento.

Iniettare o correggere un tag canonical

class CanonicalHandler {
  constructor(url) { this.url = url; }
  element(el) { el.setAttribute("href", this.url); }
}

const rewriter = new HTMLRewriter()
  .on('link[rel="canonical"]', new CanonicalHandler("https://example.com/preferred/"));

return rewriter.transform(response);

Se la pagina non ha alcun canonical, colleghi un handler a head e usi append per aggiungerne uno invece di modificare un tag esistente. In ogni caso, ricorda l’insegnamento della parte sulla canonicalizzazione: rel=canonical è un suggerimento, non un comando. Un Worker ti permette di impostarlo in modo coerente su un’intera piattaforma, ma la decisione resta a Google.

Il CanonicalHandler qui sopra presume che un tag esista già e che la risposta sia HTML. In produzione nessuna delle due cose è garantita e sbagliare significa ritrovarsi con due tag canonical sulla stessa pagina invece di uno. Prima di pubblicare una riscrittura di questo tipo, rendila idempotente e testala con:

  • Nessun canonical esistente — il tuo handler deve rilevare il caso mancante e aggiungerne uno con append a head, non ignorare silenziosamente il caso in cui nessun link[rel="canonical"] corrisponda.
  • Un canonical duplicato o malformato già presente — decidi se rimuovere il tag estraneo o lasciare che la riscrittura ne aggiunga un secondo (la seconda opzione è un bug reale, non un caso limite: i canonical duplicati sono un problema comune creato dagli stessi implementatori).
  • Una risposta non HTML — una route API, un’immagine o una risposta di redirect passata dallo stesso Worker non dovrebbe essere elaborata da HTMLRewriter; limita la trasformazione alle route e ai tipi di contenuto che hai verificato davvero.
  • Eseguire due volte la trasformazione sulla stessa risposta (un retry, un fetch annidato) — conferma che non aggiunga nuovamente un secondo tag.

Aggiungere o correggere gli alternate hreflang

Lo stesso meccanismo, guidato dalla configurazione. Aggiungi un link[rel="alternate"] per ogni locale a head. Se gli alternate sono per locale e relazionali, quella configurazione appartiene a D1; se è una semplice tabella di ricerca, KV è sufficiente. Il punto è che HTMLRewriter li inietta nello stesso modo per ogni richiedente: non devi diramare in base allo user agent.

Iniettare dati strutturati JSON-LD

new HTMLRewriter().on("head", {
  element(head) {
    head.append(
      `<script type="application/ld+json">${JSON.stringify(schema)}</script>`,
      { html: true }
    );
  },
});

Limiti CPU che diventano rilevanti su larga scala

Una dichiarazione di un concorrente che contesterei è “sub-millisecondo, senza vincoli”. Il vero limite superiore è il tempo CPU: 10 ms nel piano gratuito, 30 ms nei piani a pagamento (il tempo di attesa di fetch secondo l’orologio non conta: conta il tempo CPU). Per le riscritture tipiche non te ne accorgerai mai. Per passaggi HTMLRewriter pesanti su pagine molto grandi è un vincolo reale intorno a cui progettare, non allarmismo.

Redirect al livello edge: KV contro D1 contro Rules

In genere preferisco avere i redirect al livello edge (CDN) invece che sul server: scaricano il lavoro dall’origine e si applicano prima che la pagina venga generata. Nello specifico di Cloudflare, nella mia guida Ahrefs sui redirect per la SEO ho spiegato che hai diverse opzioni: redirect singoli o bulk, redirect rules, page rules oppure Workers con coppie chiave-valore, o ancora un Worker che modifica gli header per aggiungere un redirect.

Per una tabella gestita da un Worker, KV è la sede naturale: una ricerca rapida, eventualmente consistente, con chiave basata sull’URL.

export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const target = await env.REDIRECTS.get(url.pathname);   // KV namespace
    if (target) return Response.redirect(target, 301);
    return fetch(request);
  },
};

Scegli D1 quando i redirect sono relazionali (SQL per locale o per segmento che vuoi interrogare). E riconosci quando un Worker è eccessivo: per un insieme piccolo e statico di redirect, i Bulk Redirects o le Redirect Rules di Cloudflare sono più semplici e non richiedono codice. Non costruire a mano un Worker KV per cinquanta redirect.

Scegli un solo proprietario per un determinato URL e non lasciare che un redirect del Worker, un Bulk Redirect, una Redirect Rule e un redirect dell’origine si applichino tutti allo stesso percorso: sono sistemi separati che possono attivarsi sulla stessa richiesta e, quando corrispondono più sistemi, invece di avere un redirect pulito devi fare il debug della precedenza. Prima di aggiungere un redirect in qualunque punto, verifica se ne esiste già uno per quel percorso negli altri sistemi e scegli il livello in base alla complessità della corrispondenza (1:1 semplice contro pattern), alla scala e a chi deve osservarlo o ripristinarlo. Un redirect del Worker vive nel codice e nei log; un Bulk Redirect o una Rule vive nella dashboard ed è più facile da controllare o ripristinare per chi non sviluppa.

Caching: tre cose diverse con un solo nome fuorviante

Questa è la sezione che gli articoli della concorrenza saltano e quella che genera più confusione del tipo “perché la mia modifica non è comparsa”. Tre livelli separati condividono la parola cache:

  • Workers Cache APIcaches.default e caches.open(). È una cache programmabile, con ambito Worker, che leggi e scrivi nel codice.
  • Cache edge di Cloudflare — la cache CDN che serve i tuoi asset, distinta dalla Cache API.
  • Cache-Control dell’origine — gli header impostati dall’origine (o dal tuo Worker), che influenzano entrambi i livelli precedenti e ciò che fa Googlebot.

Confondili e giurerai che una modifica non è stata pubblicata, quando in realtà viene servita da un livello che non hai svuotato.

Quando una modifica non compare davvero, non tirare a indovinare: diagnosticala livello per livello:

  1. Chiave della cache. Quali attributi della richiesta determinano se due richieste colpiscono la stessa voce in cache (l’URL e, a volte, header o cookie se la chiave della cache li include)? Una riscrittura che varia in base a qualcosa che non fa parte della chiave può servire la variante sbagliata.
  2. Quale livello ha servito la risposta. Controlla CF-Cache-Status (HIT/MISS/EXPIRED/DYNAMIC) per capire se ha risposto la cache edge o se la richiesta ha raggiunto il tuo Worker.
  3. Posizione/stato. La cache Cloudflare è distribuita tra data center: uno svuotamento o un deployment nuovo non invalida necessariamente e istantaneamente ogni posizione edge.
  4. TTL e regola che lo ha impostato. Conferma se il TTL è controllato da una regola di cache, da un header Cache-Control dell’origine o da un header impostato direttamente dal Worker.
  5. Invalidazione. Hai svuotato l’URL specifico, svuotato tutto o fatto affidamento sulla scadenza del TTL? Una voce della Cache API posseduta dal Worker (caches.default) ha bisogno del proprio delete() esplicito: svuotare la cache CDN non la tocca.

Cosa fa Googlebot con ETag / If-None-Match / 304

Se il tuo Worker genera o riscrive la risposta, possiede gli header di caching: per questo le indicazioni di Google sul caching HTTP del dicembre 2024 sono direttamente operative per te. Google supporta il caching HTTP euristico tramite ETag/If-None-Match e Last-Modified/If-Modified-Since, raccomanda fortemente ETag perché è meno soggetto a errori e afferma che, quando l’ETag del crawler corrisponde, il server deve restituire 304 Not Modified senza corpo. Un Worker che genera la risposta può implementare esattamente questo: calcolare un ETag, confrontarlo con If-None-Match e interrompere direttamente la richiesta con un 304, risparmiando calcolo e fornendo a Googlebot un segnale rapido e memorizzabile nella cache.

Il compromesso del recrawl con max-age

Google dice anche di valutare l’impostazione di Cache-Control: max-age per aiutare i crawler a decidere quando eseguire nuovamente il crawling. Il problema, per un Worker che riscrive l’HTML, è che un max-age aggressivo su una pagina i cui tag iniettati dal Worker sono appena cambiati può ritardare la visualizzazione dell’aggiornamento appena pubblicato da parte di Googlebot. Non applicare una lunga durata di cache all’HTML riscritto per poi dimenticartene.

Il confine del cloaking applicato ai Workers

La regola ferrea, nei termini dei Workers, è: esegui la stessa logica per ogni richiedente. La policy antispam di Google definisce il cloaking come il presentare contenuti diversi agli utenti e ai motori di ricerca per manipolare il ranking e richiama in particolare l’inserimento di testo o parole chiave in una pagina solo quando il richiedente è un motore di ricerca.

Un paio di chiarimenti, perché qui si tende a correggere troppo:

  • Ispezionare lo User-Agent non è automaticamente cloaking. Registrare il traffico dei bot o servire una risposta in cache più rapidamente a qualunque client va bene. Il confine è una differenza di contenuto in base all’identità del richiedente, fatta per manipolare il ranking.
  • Il test A/B diviso per pagina sui Workers è corretto. Dividere gli utenti per URL e trattare ogni richiedente nello stesso modo è legittimo. Dividere in base a chi sta chiedendo, bot contro persona, non lo è.

Un esempio pratico del modello sicuro: il gate di anteprima di questo sito è un Worker che restituisce 404 per qualsiasi percorso /preview/ a meno che un cookie non corrisponda a un segreto. Restituisce quel 404 a tutti quelli che non hanno il cookie, Googlebot compreso. Questa è esattamente la forma sicura: non nasconde una cosa ai bot mostrandone un’altra agli utenti, ma applica una regola uniformemente.

E non affidarti a un passaggio di pre-rendering solo per i bot, anche se lo implementi correttamente sui Workers. Google ha definito il dynamic rendering un workaround e non una soluzione a lungo termine; un Worker che esegue il pre-rendering solo per i bot eredita quella deprecazione.

Come un Worker può bloccare o rallentare accidentalmente Googlebot

Questo è il modo più specifico dei Workers per farti un danno da solo e, di solito, non si trova nel codice del Worker.

La Bot Fight Mode gira fuori dal Ruleset Engine

Bot Fight Mode (e Super Bot Fight Mode) può produrre falsi positivi contro crawler legittimi, incluso Googlebot. La trappola è questa: Bot Fight Mode viene valutata su una pipeline separata dal WAF Ruleset Engine, quindi le normali regole custom WAF “allow” o “skip” non la sovrascrivono. Se Bot Fight Mode sta sottoponendo Googlebot a una verifica, non risolvi con una allow-rule: devi cambiare o disabilitare la modalità stessa. (Conferma i meccanismi attuali nella documentazione Cloudflare su Bot Fight Mode e Super Bot Fight Mode prima di farci affidamento: i prodotti per bot cambiano.)

Il modello di regola custom per i bot verificati

Cloudflare espone un campo cf.client.bot e un modello di allow rule per i bot verificati, così puoi autorizzare i crawler affidabili nelle tue regole custom. È utile per il lato WAF, anche se (come detto sopra) non raggiunge Bot Fight Mode.

La CDN in sé è neutrale o positiva

Per essere chiari sul mito: la CDN Cloudflare non danneggia la SEO. Il lavoro 2024 di Google Crawling December spiega che Google aumenta la velocità di crawling quando rileva una CDN, ma che una CDN può anche bloccare accidentalmente Googlebot tramite regole WAF o per i bot e che una 503 è preferibile a un interstitial di verifica del bot. Il rischio è un Worker o un’impostazione per i bot mal configurata, non l’infrastruttura.

Verificare cosa ha ricevuto davvero Googlebot

Dopo ogni deployment di un Worker, conferma ciò che ha ricevuto davvero un crawler: non darlo per scontato:

  • GSC URL Inspection → Test Live URL. Recupera la pagina come Google e mostra l’HTML renderizzato, così puoi confermare che canonical, hreflang e JSON-LD iniettati siano davvero presenti.
  • Controlla CF-Cache-Status insieme all’HTML. HIT / MISS / EXPIRED indica se stai osservando una risposta fresca del Worker o una risposta dalla cache: è il modo più rapido per scoprire che un “cambiamento non comparso” è in realtà un problema del livello di cache.
  • Recupera la pagina direttamente come Googlebot. Invia la richiesta con lo user agent di Googlebot e confronta i risultati, ma ricorda che la corrispondenza della stringa non prova nulla sull’identità: verifica il Googlebot reale con DNS inverso e diretto rispetto agli intervalli IP pubblicati da Google (vedi la scheda Scripts).

Igiene del deployment specifica per i Workers

Un wrangler deploy completato con successo dice che lo script è stato pubblicato, non che Googlebot riceva l’output renderizzato corretto. Tratta ogni modifica a un Worker che incide sulla SEO come una release registrata, non come un semplice push:

  • Limita l’ambito delle route. Non eseguire un Worker su /* come impostazione predefinita. Associalo ai percorsi che gli servono nei pattern delle route di wrangler.toml, così un bug non può mandare fuori uso l’intero sito.
  • Controlla i limiti attuali prima di promettere la scalabilità. I limiti di tempo CPU, numero di subrequest e dimensione dello script variano in base al piano e cambiano nel tempo: verifica la pagina dei limiti attuali di Cloudflare prima di progettare una riscrittura intorno a una soglia specifica, invece di affidarti a un numero ricordato.
  • Registra i metadati di versione della release. Il modello Cloudflare di versioni e deployment tiene traccia della versione del sorgente, della data di compatibilità, dei binding e delle route per ogni deployment: annota quale versione è attiva su quale route, così l’affermazione “il Worker fa X” può essere verificata rispetto a ciò che è davvero pubblicato, non rispetto a ciò che hai nell’editor.
  • Versiona e fai rollback con gli ambienti Wrangler. Pubblica in un ambiente di staging, distribuisci gradualmente per percentuale e mantieni la possibilità di tornare immediatamente alla versione precedente.
  • Usa log con ambito limitato per monitorare il rollout, tenendo presenti i loro limiti. I Workers Logs e il tail dei log possono aiutare a fare debug di un rollout graduale, ma i log sono campionati e conservati per una finestra limitata: trattali come evidenza circoscritta alle richieste che hanno catturato, non come un registro completo di ogni visita dei crawler.
  • Imposta una condizione di arresto e testa il rollback prima di averne bisogno. Decidi in anticipo quale comportamento osservato (tasso di errore, risposta sbagliata in un controllo a campione, calo del crawl rate) interrompe il rollout e conferma che il percorso di rollback funzioni davvero, invece di presumerlo.
  • Svuota la cache come parte del deployment. Poiché entrano in gioco tre livelli di cache, fai dello svuotamento/invalidazione una fase esplicita della pubblicazione della riscrittura, non un ripensamento.

Una nota su Bing e uno sguardo al futuro

Anche Bing non ha indicazioni specifiche su Cloudflare o sull’edge. Ma poiché un deployment di Worker è immediato mentre i crawl non lo sono, IndexNow è l’abbinamento naturale: attivalo nel momento in cui viene pubblicata una modifica a una tabella di redirect o a un tag gestita dal Worker, così Bing (e gli altri motori partecipanti) esegue rapidamente un nuovo crawling. Inoltre, Cloudflare ha introdotto la canonicalizzazione imposta all’edge come funzionalità di prodotto (“Redirects for AI Training”): i crawler verificati per l’addestramento dell’AI ricevono un 301 verso il tuo URL canonical con un’unica impostazione. È un utile contrasto rispetto alla logica canonical costruita a mano nel tuo Worker e un promemoria del fatto che “servire ai crawler qualcosa di diverso dagli utenti” è un modello su cui Microsoft ha espresso pubblicamente scetticismo per altre funzionalità Cloudflare rivolte ai crawler AI: un buon controllo intuitivo per qualsiasi Worker condizionale in base al bot.

Per il quadro più ampio — confronto tra piattaforme, Snippets contro Workers e aspetti di coda di sviluppo e governance — torna all’hub Edge SEO.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.