Vai al contenuto
Server e .htaccess

Regole di redirect .htaccess in WordPress: 301 fatte bene

Sposta un URL senza perdere posizionamento: quale direttiva di redirect usare nel .htaccess di WordPress, dove vanno le regole e come testarle senza restare chiuso fuori.

Pubblicato

Stai spostando un URL e vuoi che il vecchio mantenga il posizionamento? Ti serve un 301 in .htaccess, messo dove WordPress non lo cancellerà, scritto con la direttiva giusta e puntato all’URL esatto che WordPress serve davvero. Sbaglia uno di questi tre punti e ottieni una catena di redirect, un loop o un 500 che ti chiude fuori da wp-admin.

La scelta, la regola sulla posizione e il test, in quest’ordine.

Quale direttiva: Redirect, RedirectMatch o RewriteRule

Ci sono due moduli Apache in gioco e non sono intercambiabili.

Redirect (mod_alias) — un percorso noto verso una destinazione. La cosa più semplice che funziona:

Redirect 301 /old-page/ https://example.com/new-page/

Due comportamenti da conoscere. La corrispondenza avviene sul prefisso del percorso, non sulla stringa esatta, quindi /old-page/ cattura anche /old-page/anything/ e aggiunge /anything/ alla destinazione. E porta con sé la query string in automatico.

RedirectMatch (mod_alias) — una singola espressione regolare che copre molti URL. È così che si sposta un’intera sezione:

RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1

Il gruppo di cattura (.*) diventa $1 nella destinazione, quindi /blog/hello-world/ finisce su /articles/hello-world/. Nota la barra iniziale: i pattern di mod_alias corrispondono al percorso completo dell’URL.

RewriteRule (mod_rewrite) — necessaria quando il redirect dipende da una condizione: l’hostname, il protocollo, una query string, uno user agent. Niente in mod_alias è in grado di valutarle.

RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

In un .htaccess per-directory al pattern viene tolta la barra iniziale: ecco perché qui è ^(.*)$ e nell’esempio con mod_alias è ^/blog/. Confondere questa cosa è di gran lunga il motivo più comune per cui una regola incollata non fa silenziosamente nulla.

Parti da mod_alias come impostazione predefinita. Ricorri a RewriteRule solo quando ti serve una condizione. Meno espressioni regolari, meno modi di sbagliare.

Dove devono stare le regole

I redirect personalizzati vanno sopra la riga # BEGIN WordPress. Non al suo interno.

# --- custom redirects ---
Redirect 301 /old-page/ https://example.com/new-page/
RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1

# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Due motivi indipendenti:

  1. WordPress riscrive il proprio blocco. Salvare Impostazioni → Permalink richiama flush_rewrite_rules(), che scarta tutto ciò che sta tra i marcatori e lo rigenera. Una regola messa lì dentro sparisce la volta successiva in cui qualcuno tocca quella schermata: magari mesi dopo, senza che nessuno colleghi i due eventi.
  2. L’ordine decide chi vince. Il blocco di WordPress si chiude con una regola generica che manda ogni richiesta che non sia un file o una directory a index.php. Una RewriteRule messa dopo non viene mai eseguita.

Una complicazione onesta: mod_alias e mod_rewrite non vengono eseguiti nell’ordine del file. Apache elabora mod_alias durante la traduzione dell’URL e il mod_rewrite per-directory più tardi, in fase di fixup, quindi una riga Redirect può prevalere su una RewriteRule che compare più in alto nel file. Se ti accorgi di aver bisogno di entrambi su percorsi che si sovrappongono, scegli un modulo per quel percorso e restaci dentro. Fare il debug di uno scontro tra mod_alias e mod_rewrite non vale l’ora che costa.

Le barre finali

Il redirect canonico di WordPress aggiunge una barra finale alla maggior parte dei permalink. Quindi questo:

Redirect 301 /old-page/ https://example.com/new-page

produce due salti: il tuo 301 verso l’URL senza barra e poi il 301 di WordPress che la aggiunge. Le catene continuano a trasmettere i segnali di posizionamento, ma sprecano crawl budget e aggiungono un round trip per ogni visitatore.

Carica prima la destinazione in un browser, copia l’URL esattamente come si assesta nella barra degli indirizzi e usa quello. Se la tua struttura dei permalink termina con .html o non ha barra finale, adeguati a quella. Qui non esiste una risposta universalmente corretta, solo «fai corrispondere ciò che WordPress serve».

Testare senza chiudersi fuori

.htaccess viene letto a ogni richiesta. Un errore di sintassi restituisce un 500 su tutto il sito, wp-admin compreso, quindi la via di recupero deve esistere prima che ti serva.

Fai prima un backup del file. Via SSH:

cp /path/to/wordpress/.htaccess /path/to/wordpress/.htaccess.bak

Se il sito va in 500, rimetti il file di backup sopra quello rotto e il sito torna subito. Tieni una sessione SFTP già aperta in un’altra finestra: collegarsi da zero mentre il sito è giù è il punto in cui inizia il panico. Nota che apachectl configtest non analizza .htaccess, quindi segnalerà una configurazione sana mentre il tuo sito è morto.

Testa con curl, non con un browser. I browser mettono in cache le risposte 301 in modo aggressivo e ti mostreranno tranquillamente il risultato di ieri:

curl -sI https://example.com/old-page/ | head -n 5

Leggi due cose: la riga di stato deve dire 301 e l’header Location: deve essere esattamente l’URL finale. Per vedere l’intera catena, seguila:

curl -sIL https://example.com/old-page/ | grep -E '^(HTTP|Location)'

Più di una riga HTTP/1.1 301 significa che hai costruito una catena. Più di cinque circa significa che hai costruito un loop, e curl si fermerà per dirtelo.

Usa un 302 finché non sei sicuro. Un 302 non viene messo in cache allo stesso modo, quindi un errore è reversibile in pochi secondi. Passa a 301 quando curl mostra la destinazione che volevi. Non costa nulla e ha salvato più siti di qualsiasi altra abitudine in questo articolo.

Se una regola sembra non fare proprio nulla, verifica che Apache stia almeno leggendo il file. AllowOverride None sulla directory fa sì che Apache ignori del tutto .htaccess, e nginx lo ignora completamente: controlla l’header Server: con curl -I prima di metterti a fare il debug delle espressioni regolari. Per comporre il blocco con la sintassi giusta per la tua versione di Apache e il tuo percorso di installazione, usa il generatore di .htaccess per WordPress.

Quando non usare affatto .htaccess

Per una manciata di redirect che i redattori devono gestire in autonomia, un plugin di redirect che salva le regole nel database è lo strumento migliore: sopravvive alle migrazioni di hosting e non richiede SSH. Il compromesso è reale: PHP deve avviarsi per servire il redirect, il che è più lento di Apache che risponde direttamente.

Usa .htaccess per gli spostamenti strutturali e definitivi: un cambio di dominio, la rinomina di una sezione, il forzare https o www. Usa un plugin per i redirect editoriali occasionali. Fare entrambe le cose va bene finché sai quale livello possiede quale URL, perché un redirect definito in due punti è un bug in attesa del suo brutto pomeriggio.

FAQ

Domande

Dove vanno i redirect personalizzati nel file .htaccess di WordPress?

Sopra la riga # BEGIN WordPress, mai tra i marcatori. WordPress rigenera tutto ciò che si trova dentro quei marcatori ogni volta che qualcuno salva la schermata dei permalink, quindi una regola inserita all'interno viene cancellata senza preavviso. La posizione conta anche per l'ordine: il blocco di WordPress termina con una regola generica che manda le richieste non gestite a index.php.

Devo usare Redirect, RedirectMatch o RewriteRule per un 301?

Usa Redirect per un singolo percorso noto, RedirectMatch quando un solo pattern copre molti URL e RewriteRule quando il redirect dipende da una condizione come l'hostname, il protocollo o la query string. Redirect e RedirectMatch provengono da mod_alias e sono più semplici. RewriteRule proviene da mod_rewrite ed è l'unica in grado di valutare condizioni.

Perché il mio redirect in .htaccess provoca un loop di redirect?

Di solito la destinazione corrisponde ancora alla regola che vi ha mandato il visitatore, quindi la regola scatta all'infinito. L'altra causa comune è forzare https dietro un load balancer o una CDN, dove il server vede http in chiaro su ogni richiesta anche se il visitatore è già in https. Valuta invece l'header X-Forwarded-Proto.

La barra finale conta in un redirect di WordPress?

Sì. Il redirect canonico di WordPress aggiunge una barra finale alla maggior parte dei permalink, quindi puntare un 301 a un URL senza barra produce due salti invece di uno. I redirect concatenati continuano a trasmettere i segnali di posizionamento, ma sprecano crawl budget e rallentano il visitatore. Fai corrispondere l'URL finale esatto servito da WordPress, barra compresa.

Come testo un redirect in .htaccess senza rompere il sito?

Esegui curl con l'opzione solo header sul vecchio URL e leggi il codice di stato e l'header Location prima di fidarti di un browser. I browser mettono in cache le risposte 301 in modo aggressivo e ti mostreranno un risultato obsoleto. Conserva prima una copia del file funzionante, perché un errore di sintassi in .htaccess restituisce un 500 su ogni pagina, wp-admin compreso.

Perché tutto il sito ha restituito un errore 500 dopo aver aggiunto un redirect?

Una sola direttiva malformata in .htaccess manda giù tutti gli URL sotto quella directory, amministrazione inclusa. Le cause comuni sono un blocco IfModule non chiuso, una riga RewriteEngine On mancante o la sintassi di accesso di Apache 2.2 e 2.4 mescolata nello stesso file. Rimuovi l'ultimo blocco aggiunto e ricarica per confermare.