Connect.gt

Condividiamo idee e conoscenza dal 2003...
...dopo 17 anni una Nuova Storia è nata
Scopri di più
Nonostante le tonnellate di esempi e documentazione, mod_rewrite è voodoo.
Voodoo dannatamente figo, ma sempre voodoo.

Brian Moore


Piccola guida al mod_rewrite - contiene le informazioni base da sapere per utilizzare questo utilissimo modulo. La guida anche se orientata ad Apache è ovviamente valida (per la parte teorica) ad IIS / ISAPI rewrite.

Prima di cominciare

Requisiti


Uno sguardo da vicino

Cosa è il mod_rewrite

mod_rewrite è un modulo per Apache, il webserver più usato nel mondo Unix. Il modulo associa "al volo" un URL virtuale (non residente sul filesystem) ad uno reale, tramite delle regole definite nei file .htaccess

Come si attiva

Se sei in un hosting condiviso, non devi fare niente: il modulo è attivo di default nella maggior parte dei casi. In ogni caso prima di acquistare un piano d'hosting chiedi esplicitamente se il modulo è installato!

Se lavori invece su un tuo server dedicato / VPS / localhost, molto probabilmente devi attivare il modulo a mano:

  • per Apache2 a2enmod rewrite come root
  • per Apache1 è necessario decommentare la linea LoadModule mod_rewrite nel file di configurazione

Apache di default ignora i file .htaccess (e di conseguenza le tue regole!) - per farglieli leggere devi modificare la direttiva AllowOverride all'interno del file di configurazione. Ricorda di riavviare il webserver.

La prova del nove

Per controllare se il modulo funziona:
Livello 1) crea una pagina .php

<?php phpinfo(); ?>

Richiamala dal browser e cerca nella pagina mod_rewrite sotto Loaded Modules. Se trovi la stringa, vuol dire che il modulo è caricato.

Attenzione!! Caricato in memoria non vuol dire utilizzabile - infatti se il modulo è presente, ma i file .htaccess vengono ignorati (vedi sopra), le regole di riscrittura non funzioneranno! Per questo entra in gioco il..

Livello 2) testare se effettivamente gli indirizzi vengono riscritti: scarica questo file e segui le istruzioni contenute nell'archivo stesso.

Struttura di un file .htaccess

Riporto la struttura "base" di un file .htaccess contenente una semplice regola

RewriteEngine On
RewriteRule ^index.html$ fooindex.php [L]

La prima linea attiva l'engine (va sempre inserita)
La seconda linea specifica una regola (RewriteRule) composta da:

  • l'URL da riscrivere, tra ^ (inizio stringa) e $ (fine stringa), quasi sempre formato da espressioni regolari
  • l'URL al quale inviare la richiesta
  • parametri (tra parentesi quadre, separati da virgole)
    • L "ultimo" -> paragonabile al break, dice all'engine di fermarsi e di non controllare le altre regole
    • R=301 -> invia un header di contenuto spostato in modo permanente e crea il redirect
    • R=302 -> invia un header di contenuto spostato in modo temporaneo e crea il redirect
    • QSA "accoda query string" -> se l'URL richiesto contiene più parametri di quelli previsti, vengono accodati - utile se si vogliono mettere parametri a pagine con indirizzi riscritti (!!).

Inserendo queste due righe in domain.tld/.htaccess, quando un utente visita domain.tld/index.html il webserver invia la pagina domain.tld/fooindex.php. L'utente non si accorge di niente.

Deep focus:

- se fooindex.php non esiste, l'utente vede "404 fooindex.php", anche se ha richiesto index.html

- index.html può anche non esistere: il webserver invierà al client direttamente il file php senza neppure curarsi del file html

mod_rewrite in pratica

Let's check it out!

Procediamo per casi analizzando alcuni URL-tipo che ognuno di noi ha incontrato almeno una volta. Sono solo degli esempi, riadattabili alle proprie esigenze. Il consiglio è di leggere in modo lineare tutti i casi, e non solo la parte finale.

Lo scenario è la sezione news di un sito. Ricordate che in ogni file htaccess dovete inserire, prima delle regole, la direttiva RewriteEngine On.


Riscrittura "base"

URL prima dell'intervento: www.domain.tld/news/index.php?id=1234
URL dopo l'intervento: www.domain.tld/news/notizia-1234.html

RewriteRule ^notizia-([0-9]+)\.html$ index.php?id=$1
  • ([0-9]+) uno o più (+) caratteri numerici da 0 a 9 ([0-9])
  • \.html indica ".html" (. è un metacarattere che vuol dire qualsiasi carattere, dobbiamo effettuare l'escape).
  • $1 viene sostituito con il valore "trovato" dalla regexp ([0-9]+)

Ok, abbiamo tolto la querystring (hai detto niente!). Ma non è proprio il sistema più raffinato e la semantica è a 0.


Gestire più parametri

URL prima dell'intervento: www.domain.tld/news/index.php?id=1234&cat=recensione
URL dopo l'intervento: www.domain.tld/news/recensione-1234.html

RewriteRule ^(.+)-([0-9]+)\.html$ index.php?id=$2&cat=$1
Consiglio: invece di usare (.+) (qualsiasi carattere in qualsiasi quantità) è più saggio usare ([^/]+) che vuol dire "qualsiasi carattere tranne lo slash in qualsiasi quantità".


Associazioni tra ID e stringhe

URL prima dell'intervento: www.domain.tld/news/categoria.php?id=2
URL dopo l'intervento: www.domain.tld/news/recensioni.html

RewriteRule ^([^/]+)\.html$ index.php?nomecat=$1
$cat = $_GET['nomecat'];
$categoria = mysql_query("SELECT id FROM categorie WHERE nome='$cat'");
if (mysql_num_rows($categoria)==0) {
    Make404(......);
} else {
    $categoria = mysql_num_rows($categoria);
    $news = mysql_query("SELECT .... FROM news WHERE categoria='$categoria[0]'");
    ShowNews(......);
} 

Nota bene: è possibile eliminare l'ID contenuto nell'URL se e solo se la corrispondenza ID<->stringa è biunivoca!


Il caso ideale

URL prima dell'intervento: www.domain.tld/news/index.php?id=1234
URL dopo l'intervento: www.domain.tld/news/recensioni/php6-usera-goto-1234.html

RewriteRule ^([^/]+)/([^/]+)-([0-9]+)\.html$ index.php?id=$3&cat=$1&titolo=$2
$newsid = $_GET['id']; // non serve fare il cast con (int)
$news = mysql_query("SELECT ... FROM news WHERE id='$newsid'");
if (mysql_num_rows($news)==0) {
    Make404(......);
} else {
    .....
    // supponiamo di avere in $cat la categoria letta dal database
    // e in $titolo il.. titolo ovviamente
    if ($cat != $_GET['cat'] || ottimizza($titolo) != $_GET['titolo']) {
       // è stato cambiato titolo o categoria
       make301('http//www.domain.tld/news/' . $cat . '/' . $titolo . '-' . $newsid . '.html');
    }
    NewsClass:ShowNews(......);
} 
  • la funzione ottimizza deve essere la stessa usata per mostrare gli URL nella pagina con l'elenco delle news per evitare problemi di doppi-redirect e serve per eliminare spazi, caratteri accentati, ecc.
  • la funzione make301 deve inviare un header 301, un header location e bloccare lo script (con exit; )
  • la semantica di questi URL è molto buona (un file chiamato come la news contenuto in una directory correlata)
  • se la news viene spostata di categoria o rinominata, viene automaticamente generato un header 301 invece di un 404 (come su questo forum)


Per i più bravi

Quando qualcosa non funziona

La direttiva RewriteLog permette di salvare l'output di debug di mod_rewrite su un file, ma va inserita nel file di configurazione principale di Apache, e in un hosting condiviso ciò non è possibile.

Per vedere quali pezzi di url (e in che modo) vengono riconosciuti, basta usare uno script con questo semplice contenuto

 <?php print_r($_GET);
 
RewriteRule ^(.+)-(.+)\.html$ debugger.php?id=$1&cat=$2

Richiamando nel browser la pagina test-123.html:

Array
(
   [id] => test
   [cat] => 123
)

capiremo di aver invertito $1 con $2.. ;)

Ottimizzazioni

Tre consigli:

  1. se possibile, caricare le regole nella configurazione (ad ogni accesso, il file .htaccess viene aperto, letto, parsato, chiuso..)
  2. evitare trasformazioni stupide (es .php -> .html) - ai fini dell'ottimizzazione non cambia assolutamente niente
  3. usare [L] (va inserito quasi sempre) per non far testare a mod_rewrite le regole successive.


--Osvi 15:30, Lug 30, 2007 (CEST)


  • Questa pagina è stata modificata per l'ultima volta il 29 nov 2009 alle 15:32.
  • Questa pagina è stata letta 32 647 volte.