Funzionalità nascoste di mod_rewrite

Sembra che ci sia un discreto numero di thread di mod_rewrite che fluttuano ultimamente con un po ‘di confusione su come certi aspetti di esso funzionano. Di conseguenza ho compilato alcune note sulla funzionalità comune e forse alcune sfumature fastidiose.

Quali altre caratteristiche / problemi comuni hai incontrato usando mod_rewrite ?

Dove posizionare le regole mod_rewrite

mod_rewrite regole mod_rewrite possono essere collocate all’interno del file httpd.conf o all’interno del file .htaccess . se si ha accesso a httpd.conf , l’inserimento delle regole qui offrirà un vantaggio in termini di prestazioni (poiché le regole vengono elaborate una volta, a differenza di ogni volta che viene chiamato il file .htaccess ).

Registrazione delle richieste di mod_rewrite

La registrazione può essere abilitata dal file httpd.conf (incluso ):

 # logs can't be enabled from .htaccess # loglevel > 2 is really spammy! RewriteLog /path/to/rewrite.log RewriteLogLevel 2 

Casi d’uso comune

  1. Per incanalare tutte le richieste in un singolo punto:

     RewriteEngine on # ignore existing files RewriteCond %{REQUEST_FILENAME} !-f # ignore existing directories RewriteCond %{REQUEST_FILENAME} !-d # map requests to index.php and append as a query string RewriteRule ^(.*)$ index.php?query=$1 

    Dal momento che Apache 2.2.16 è ansible utilizzare anche FallbackResource .

  2. Gestione dei reindirizzamenti 301/302:

     RewriteEngine on # 302 Temporary Redirect (302 is the default, but can be specified for clarity) RewriteRule ^oldpage\.html$ /newpage.html [R=302] # 301 Permanent Redirect RewriteRule ^oldpage2\.html$ /newpage.html [R=301] 

    Nota : i reindirizzamenti esterni implicitamente sono 302 reindirizzamenti:

     # this rule: RewriteRule ^somepage\.html$ http://google.com # is equivalent to: RewriteRule ^somepage\.html$ http://google.com [R] # and: RewriteRule ^somepage\.html$ http://google.com [R=302] 
  3. Forzare SSL

     RewriteEngine on RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://example.com/$1 [R,L] 
  4. Bandiere comuni:

    • [R] o [redirect] – forza un reindirizzamento (il valore predefinito è un reindirizzamento temporaneo 302)
    • [R=301] o [redirect=301] – forza un 301 reindirizzamento permanente
    • [L] o [last] – interrompe il processo di riscrittura (vedi la nota sotto nelle insidie ​​più comuni)
    • [NC] o [nocase] – specifica che la corrispondenza deve essere senza distinzione tra maiuscole e minuscole

    L’uso della lunga forma di flag è spesso più leggibile e aiuterà gli altri a leggere il tuo codice in un secondo momento.

    Puoi separare più flag con una virgola:

     RewriteRule ^olddir(.*)$ /newdir$1 [L,NC] 

Insidie ​​comuni

  1. Mixando mod_alias stile mod_alias reindirizza con mod_rewrite

     # Bad Redirect 302 /somepage.html http://example.com/otherpage.html RewriteEngine on RewriteRule ^(.*)$ index.php?query=$1 # Good (use mod_rewrite for both) RewriteEngine on # 302 redirect and stop processing RewriteRule ^somepage.html$ /otherpage.html [R=302,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # handle other redirects RewriteRule ^(.*)$ index.php?query=$1 

    Nota : puoi mescolare mod_alias con mod_rewrite , ma richiede più lavoro rispetto alla semplice gestione dei reindirizzamenti di base come sopra.

  2. Il contesto influisce sulla syntax

    All’interno dei file .htaccess , una barra iniziale non viene utilizzata nel modello RewriteRule:

     # given: GET /directory/file.html # .htaccess # result: /newdirectory/file.html RewriteRule ^directory(.*)$ /newdirectory$1 # .htaccess # result: no match! RewriteRule ^/directory(.*)$ /newdirectory$1 # httpd.conf # result: /newdirectory/file.html RewriteRule ^/directory(.*)$ /newdirectory$1 # Putting a "?" after the slash will allow it to work in both contexts: RewriteRule ^/?directory(.*)$ /newdirectory$1 
  3. [L] non è l’ultimo! (a volte)

    Il flag [L] interrompe l’elaborazione di ulteriori regole di riscrittura per quel passaggio attraverso il set di regole . Tuttavia, se l’URL è stato modificato in tale passaggio e ci si trova nel contesto .htaccess o nella sezione , la richiesta modificata verrà nuovamente passata attraverso il motore di analisi degli URL. E al prossimo passaggio, questa volta potrebbe corrispondere a una regola diversa. Se non lo capisci, spesso sembra che la tua bandiera [L] non abbia avuto alcun effetto.

     # processing does not stop here RewriteRule ^dirA$ /dirB [L] # /dirC will be the final result RewriteRule ^dirB$ /dirC 

    Il nostro registro di riscrittura mostra che le regole vengono eseguite due volte e l’URL viene aggiornato due volte:

     rewrite 'dirA' -> '/dirB' internal redirect with /dirB [INTERNAL REDIRECT] rewrite 'dirB' -> '/dirC' 

    Il modo migliore per farlo è utilizzare il flag [END] ( consultare i documenti Apache ) anziché il flag [L] , se si vuole veramente interrompere tutte le ulteriori elaborazioni delle regole (e passaggi successivi). Tuttavia, il flag [END] è disponibile solo per Apache v2.3.9 + , quindi se hai la v2.2 o inferiore, sei bloccato solo con il flag [L] .

    Per le versioni precedenti, è necessario affidarsi alle istruzioni RewriteCond per impedire la corrispondenza delle regole sui passaggi successivi del motore di analisi degli URL.

     # Only process the following RewriteRule if on the first pass RewriteCond %{ENV:REDIRECT_STATUS} ^$ RewriteRule ... 

    O devi assicurarti che i tuoi RewriteRule si trovino in un contesto (es. httpd.conf ) che non farà ri-analizzare la tua richiesta.

se devi “bloccare” i reindirizzamenti / riscrittori interni dall’accaduto nel file .htaccess, dai un’occhiata a

 RewriteCond %{ENV:REDIRECT_STATUS} ^$ 

condizione, come discusso qui .

L’accordo con RewriteBase:

È quasi sempre necessario impostare RewriteBase. Se non lo fai, apache suppone che la tua base sia il percorso fisico del disco nella tua directory. Quindi inizia con questo:

 RewriteBase / 

Altre insidie:

1- A volte è una buona idea disabilitare MultiView

 Options -MultiViews 

Non sono ben versato su tutte le funzionalità di MultiViews, ma so che danneggia le mie regole mod_rewrite quando è attivo, perché una delle sue proprietà è provare e ‘indovinare’ un’estensione di un file che pensa stia cercando .

Spiegherò: Supponi di avere 2 file php nella tua web dir, file1.php e file2.php e aggiungi queste condizioni e regola al tuo .htaccess:

 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ file1.php/$1 

Si presuppone che tutti gli URL che non corrispondono a un file o una directory verranno acquisiti da file1.php. Sorpresa! Questa regola non viene rispettata per l’URL http: // myhost / file2 / somepath . Invece sei preso all’interno di file2.php.

Quello che sta succedendo è che MultiViews ha immaginato automagicamente che l’url che volevi veramente fosse http: //myhost/file2.php/somepath e ti ha portato volentieri lì.

Ora, non hai idea di cosa sia appena successo e sei a quel punto mettendo in discussione tutto ciò che pensavi di sapere su mod_rewrite. Quindi inizi a giocare con le regole per cercare di dare un senso alla logica dietro questa nuova situazione, ma più stai provando, meno senso ha.

Ok, in breve, se vuoi che mod_rewrite funzioni in modo approssimativo alla logica, distriggersre MultiView è un passo nella giusta direzione.

2- abilitare FollowSymlinks

 Options +FollowSymLinks 

Quello, non so davvero i dettagli, ma l’ho visto menzionato molte volte, quindi fallo e basta.

L’equazione può essere fatta con il seguente esempio:

 RewriteCond %{REQUEST_URI} ^/(server0|server1).*$ [NC] # %1 is the string that was found above # %1<>%{HTTP_COOKIE} concatenates first macht with mod_rewrite variable -> "test0<>foo=bar;" #RewriteCond search for a (.*) in the second part -> \1 is a reference to (.*) # <> is used as an string separator/indicator, can be replaced by any other character RewriteCond %1<>%{HTTP_COOKIE} !^(.*)<>.*stickysession=\1.*$ [NC] RewriteRule ^(.*)$ https://notmatch.domain.com/ [R=301,L] 

Bilanciamento del carico dinamico:

Se si utilizza mod_proxy per bilanciare il sistema, è ansible aggiungere un intervallo dinamico di server di lavoro.

 RewriteCond %{HTTP_COOKIE} ^.*stickysession=route\.server([0-9]{1,2}).*$ [NC] RewriteRule (.*) https://worker%1.internal.com/$1 [P,L] 

Una migliore comprensione della [L] bandiera è in ordine. Il flag [L] è l’ ultimo, devi solo capire che cosa causerà il routing della tua richiesta attraverso il motore di analisi degli URL di nuovo. Dai documenti ( http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l ) (sottolineatura mia):

Il flag [L] fa in modo che mod_rewrite interrompa l’elaborazione del set di regole. Nella maggior parte dei contesti, ciò significa che se la regola corrisponde, non verranno elaborate ulteriori regole. Ciò corrisponde all’ultimo comando in Perl o al comando break in C. Utilizzare questo flag per indicare che la regola corrente deve essere applicata immediatamente senza considerare ulteriori regole.

Se si utilizza RewriteRule in entrambi i file .htaccess o in , è importante avere una certa comprensione di come vengono elaborate le regole. La forma semplificata di questo è che una volta che le regole sono state elaborate, la richiesta riscritta viene restituita al motore di analisi degli URL per fare ciò che può con esso. È ansible che, man mano che la richiesta riscritta viene gestita, il file .htaccess o la sezione possano essere incontrati di nuovo, e quindi il set di regole può essere eseguito di nuovo dall’inizio. Più comunemente ciò avverrà se una delle regole provoca un reindirizzamento, interno o esterno, che fa ricominciare il processo di richiesta.

Quindi il flag [L] smette di elaborare ulteriori regole di riscrittura per quel passaggio attraverso il set di regole. Tuttavia, se la tua regola contrassegnata con [L] ha modificato la richiesta e ti trovi nel contesto .htaccess o nella sezione , la richiesta modificata verrà nuovamente passata attraverso il motore di analisi degli URL. E al prossimo passaggio, questa volta potrebbe corrispondere a una regola diversa. Se non capisci cosa è successo, sembra che la tua prima regola di riscrittura con il flag [L] non abbia avuto alcun effetto.

Il modo migliore per farlo è utilizzare il flag [END] ( http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end ) anziché il flag [L], se davvero vuoi fermarti tutti gli ulteriori trattamenti delle regole (e successivi riorganizzazioni). Tuttavia, il flag [END] è disponibile solo per Apache v2.3.9 +, quindi se hai la v2.2 o inferiore, sei bloccato solo con il flag [L]. In questo caso, è necessario affidarsi alle istruzioni RewriteCond per impedire la corrispondenza delle regole sui passaggi successivi del motore di analisi degli URL. O devi assicurarti che i tuoi RewriteRule si trovino in un contesto (es. Httpd.conf) che non farà ri-analizzare la tua richiesta.

Un’altra grande caratteristica sono le espansioni della mappa della riscrittura. Sono particolarmente utili se hai una quantità enorme di host / riscritture da gestire:

Sono come una sostituzione di valore chiave:

 RewriteMap examplemap txt:/path/to/file/map.txt 

Quindi puoi utilizzare una mapping delle tue regole come:

 RewriteRule ^/ex/(.*) ${examplemap:$1} 

Maggiori informazioni su questo argomento possono essere trovate qui:

http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#mapfunc

mod_rewrite può modificare aspetti della gestione delle richieste senza alterare l’URL, ad esempio impostare variabili d’ambiente, impostare cookie, ecc. Ciò è incredibilmente utile.

Imposta condizionatamente una variabile di ambiente:

 RewriteCond %{HTTP_COOKIE} myCookie=(a|b) [NC] RewriteRule .* - [E=MY_ENV_VAR:%b] 

Restituisce una risposta 503: il flag [R] RewriteRule può assumere un valore non-3xx e restituire una risposta non di reindirizzamento, ad esempio per downtime / manutenzione gestiti:

 RewriteRule .* - [R=503,L] 

restituirà una risposta 503 (non un reindirizzamento di per sé).

Inoltre, mod_rewrite può agire come un’interfaccia potenziata a mod_proxy, quindi puoi farlo invece di scrivere le direttive ProxyPass :

 RewriteRule ^/(.*)$ balancer://cluster%{REQUEST_URI} [P,QSA,L] 

Opinione: l’utilizzo di RewriteRule s e RewriteCond per indirizzare le richieste a diverse applicazioni o load balancer in base a qualsiasi aspetto concepibile della richiesta è immensamente potente. Controllando le richieste mentre si dirigono verso il back-end, e potendo modificare le risposte al loro ritorno, rende mod_rewrite il posto ideale per centralizzare tutte le configurazioni relative al routing.

Prenditi il ​​tempo per impararlo, ne vale la pena! 🙂