Migrazione di siti e regole di rewrite

Ci capita spesso di seguire la migrazione di contenuti di siti editoriali complessi—decine di migliaia di pagine—con l’obiettivo di assicurare che le modifiche alla struttura delle URL siano gestite correttamente. È un passaggio fondamentale per garantire che, al rilascio di un sito, tanto gli utenti quanto i motori di ricerca possano accedere a tutti i contenuti in modo consistente, sia attraverso le nuove URL, sia attraverso quelle precedentemente disponibili.

Il principale strumento di gestione della redirezione delle URL è mod_rewrite e gli equivalenti per i web server diversi da Apache. mod_rewrite è uno strumento molto potente, tanto da essere a volte abusato: troppe regole di rewrite rappresentano un peso rilevante per un server ad alto traffico, perché lo obbligano a scontrare ogni richiesta con la sequenza di espressioni regolari e condizioni di riscrittura che sono state configurate. In termini di performance quindi, è fondamentale usare meno regole possibile, mantenendo il ciclo di riscrittura delle URL breve in modo da poter emettere una risposta in tempi rapidi, sia che si tratti di una redirect, sia che si tratti del contenuto effettivo.

A volte però sembra che questi buoni principi si scontrino con le logiche di riscrittura delle URL di piani di migrazione complessi.


Di recente, abbiamo lavorato su una migrazione particolarmente impegnativa. Si trattava di spostare articoli genericamente classificati sotto una gerarchia temporale sulla base di un criterio definito dalla presenza di particolari termini nella URL. La nuova struttura di URL prevedeva inoltre rami diversi per news e fotogallery, e modificava il termine usato per descrivere queste ultime (dall’inglese photogallery all’italianizzato fotogallery). Le fotogallery le cui URL non contenevano i termini indicati come criteri di migrazione vedevano la propria URL aggiornata esclusivamente rispetto alla denominazione photogallery.

Per evitare catene di redirect e ridurre il numero di regole e di espressioni regolari utilizzate per indirizzare i singoli casi, abbiamo utilizzato un trucco poco intuitivo ma decisamente efficace: riscrivere parzialmente le URL all’interno del ciclo di rewrite.

Un po’ come effettuare un numero di giocoleria particolarmente difficile e scenografico: in parte è pratica e tecnica, in parte semplice illusione.

Cambio d’abito, senza uscire di casa

All’inizio del ciclo di rewrite cambiamo il path delle fotogallery dalla vecchia struttura a quella nuova (da photogallery a fotogallery). Non viene effettuata una redirect, ma la URL utilizzata internamente per i test successivi risulta cambiata e riflette la nuova struttura:

RewriteRule /news/photogallery/(.+)       /news/fotogallery/$2

A questo punto possiamo testare diverse regole che effettuano redirect 301 (permanenti) in funzione dei termini presenti nelle URL. Testando la URL con il percorso delle fotogallery già aggiornato possiamo utilizzare la porzione di URL trovata dalle espressioni regolari come criterio di sostituzione, generalizzando la regola sia per le news che per le fotogallery.

In questo caso le URLs oggetto di redirect sono nella forma /news/2017/02/11/foo-is-the-new-bar.html e /news/photogallery/2017/02/11/foo-is-the-new-bar.html e devono essere aggiornate in /category/foo/news/2017/02/11/foo-is-the-new-bar.html e /category/foo/news/fotogallery/2017/02/11/foo-is-the-new-bar.html

RewriteCond $3 foo
RewriteRule ^/news/(fotogallery/)?(2017/[0–9]{2}/[0–9]{2}/.+\.html$ /category1/foo/news/$2$3 [R=301,L]
RewriteCond $3 space
RewriteRule ^/news/(fotogallery/)?(2016/[1–9]{2}/[0–9]{2}/.+\.html$ /category2/space/news/$2$3 [R=301,L]
...

(se le espressioni regolari non fanno per voi, provate https://regexone.com/. La stringa qui sopra corrisponde sostanzialmente a “Riscrivi le URL che cominciano con /news/ seguite non obbligatoriamente da fotogallery/— memorizzando questo termine nel parametro $1 — seguite ancora da2017/ , due numeri, uno slash / e almeno un qualsiasi altro carattere, per finire infine con .html )

Come Fregoli

La migrazione era basata su una nuova architettura informativa che in alcune sezioni promuoveva le fotogallery allo stesso livello degli articoli. Anche in questo caso aggiorniamo la struttura della URL internamente, semplificando il criterio di corrispondenza con i diversi casi da testare nelle regole di rewrite:

RewriteRule /news/fotogallery/(.+)     /fotogallery/$1

Detto, fatto. Le gallery sono appena state promosse di un livello nella gerarchia dei contenuti! Adesso possiamo utilizzare una singola regola per spostare sia le pagine degli articoli che le fotogallery nella nuova struttura di contenuti. In questo caso, le URL originali avevano la forma /news/2017/02/11/cannes-2017-tutti-i-vincitori.html e/news/photogallery/2017/02/11/giuria-cannes-2017.html
e dovevano essere redirette su /eventi/cannes-2017/news/2017/02/11/cannes-2017-tutti-i-vincitori.html e /eventi/cannes-2017/fotogallery/2017/02/11/giuria-cannes-2017.html

RewriteCond %{REQUEST_URI}   !^/eventi/
RewriteCond $2 cannes-2017
RewriteRule ^/(.+\.html$) /eventi/festival-cannes/$2 [R=301,L]

Anche in questo caso, le regole sono ripetute più volte per coprire casistiche diverse, sempre basate sui termini contenuti nelle URL, ma è comunque possibile utilizzare una singola regola senza doversi preoccupare del fatto che le URL originali, nel caso delle fotogallery, abbiano un ulteriore livello di gerarchia.

Una volta esaurite queste regole, se nessuna ha trovato una corrispondenza, ripristiniamo la URL nella sua forma precedente:

RewriteRule ^/fotogallery/(.+)    /news/fotogallery/$1

e testiamo altri termini e condizioni per gerarchie che non richiedono la promozione delle fotogallery di un livello.

E dopo tutti i cambi d’abito…

Le regole di rewrite sono lette in ordine, dall’alto in basso. È importante uscire dal ciclo di riscrittura non appena si è trovata una corrispondenza che genera un redirect. Lo strumento per cortocircuitare il ciclo è il flag [L] che avrete notato nelle regole precedenti. Se una regola trova una corrispondenza, la URL viene riscritta e viene emessa una redirect, uscendo dal ciclo.

Questo significa che al fondo del set di regole, se nessuna delle regole precedenti ha rediretto a una nuova URL, resta ancora da coprire l’ultima casistica, quella per cui l’unica parte della URL che cambia è quella che abbiamo già cambiato (photogallery in fotogallery). Ma questa volta dobbiamo forzare la redirect, in modo che utenti e crawler ricevano un 301 verso la URL aggiornata:

RewriteCond %{REQUEST_URI} !^/news/fotogallery/
RewriteRule ^/news/fotogallery/(.+) /news/fotogallery/$1 [R=301,L]

Basta riscrivere la URL così com’è — visto che l’avevamo già cambiata a monte—ma questa volta aggiungendo il flag che determina la redirect. La condizione aggiuntivaRewriteCond impedisce un ciclo infinito di redirect testando che la richiesta iniziale non sia quella già riscritta.


La prossima volta che dovrete gestire una migrazione complessa con parecchie rewrite, valutate se riscrivere la URL all’interno del ciclo di rewrite vi aiuta a eliminare catene di redirect o ridurre il numero delle regole o la loro complessità.

(Questo articolo è disponibile anche in inglese https://medium.com/@pecus/complex-url-rewriting-on-site-migration-4e1291a521f)