Caratteristiche nascoste di mod_rewrite


119

Sembra che ci sia un discreto numero di mod_rewritethread in giro ultimamente con un po 'di confusione su come funzionano certi aspetti. Di conseguenza ho compilato alcune note sulle funzionalità comuni e forse alcune fastidiose sfumature.

Quali altre funzionalità / problemi comuni hai riscontrato durante l'utilizzo mod_rewrite?


Risposte:


203

Dove posizionare le regole mod_rewrite

mod_rewritele regole possono essere inserite all'interno del httpd.conffile o all'interno del .htaccessfile. se si ha accesso a httpd.conf, inserire le regole qui offrirà un vantaggio in termini di prestazioni (poiché le regole vengono elaborate una volta, invece di ogni volta .htaccessche viene chiamato il file).

Registrazione delle richieste mod_rewrite

La registrazione può essere abilitata dall'interno del httpd.conffile (incluso <Virtual Host>):

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

Casi d'uso comuni

  1. Per incanalare tutte le richieste in un unico 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 
    

    A partire da Apache 2.2.16 puoi anche usare 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 sono reindirizzamenti 302 impliciti:

    # 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]oppure [redirect]- forza un reindirizzamento (il valore predefinito è un reindirizzamento temporaneo 302)
    • [R=301]oppure [redirect=301]- forza un reindirizzamento permanente 301
    • [L]oppure [last]- interrompere il processo di riscrittura (vedere la nota sotto nelle insidie ​​comuni)
    • [NC]oppure [nocase]- specificare che la corrispondenza deve essere senza distinzione tra maiuscole e minuscole


    L'uso della forma lunga di flag è spesso più leggibile e aiuterà gli altri che verranno 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. Lo mod_aliasstile di missaggio reindirizza conmod_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 combinarlo mod_aliascon mod_rewrite, ma richiede più lavoro rispetto alla semplice gestione dei reindirizzamenti di base come sopra.

  2. Il contesto influisce sulla sintassi

    All'interno dei .htaccessfile, 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'ultima! (qualche volta)

    Il [L]flag interrompe l'elaborazione di qualsiasi ulteriore regola di riscrittura per quel passaggio attraverso il set di regole . Tuttavia, se l'URL è stato modificato in quel passaggio e ci si trova nel .htaccesscontesto o nella <Directory>sezione, la richiesta modificata verrà ritrasmessa attraverso il motore di analisi dell'URL. E al passaggio successivo, questa volta potrebbe corrispondere a una regola diversa. Se non lo capisci, spesso sembra che la tua [L]bandiera non abbia avuto 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 aggirare questo è usare il [END]flag ( vedere la documentazione di Apache ) invece del [L]flag, se si vuole veramente interrompere tutte le ulteriori elaborazioni delle regole (e i passaggi successivi). Tuttavia, il [END]flag è disponibile solo per Apache v2.3.9 + , quindi se hai v2.2 o inferiore, sei bloccato solo con il [L]flag.

    Per le versioni precedenti, è necessario fare affidamento sulle RewriteCondistruzioni per impedire la corrispondenza delle regole nei passaggi successivi del motore di analisi degli URL.

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

    Oppure devi assicurarti che le tue RewriteRule siano in un contesto (cioè httpd.conf) che non provochi il riesame della tua richiesta.


10
Amico, in assoluto il miglior articolo su Internet ora sulla riscrittura mod. Odio quella cosa. Sono un eretico lighttpd a causa di quanto odio mod_rewrite.
Kent Fredric

3
Questa è stata la guida più utile che ho trovato finora su mod_rewrite. Il solo fatto di scoprire RewriteLog mi ha aiutato a risolvere così tanti problemi che ciò che mi ci voleva giorni per rintracciarlo si è trasformato in pochi minuti. (Voglio dire che le regole sono state scritte ma non sono riuscito a capire perché non funzionassero)
Joe Chin,

Post di 1 anno fa, ma una delle cose più utili che ho trovato su SO - per me.
Erik

3
Il [L]flag significa che una regola è l' ultima nell'elaborazione corrente, questo non interromperà la riscrittura, perché sono reindirizzamenti interni, quindi la tua dirBdomanda dirCnella prossima elaborazione htaccess. Da solo RewriteRule ^(.*)$ index.php?query=$1sarà un ciclo infinito di reindirizzamenti interni (in pratica viene terminato dopo 10 iterazioni). -1 perché suggerisci che [L] non è ultimo . Non sta terminando il processo di riscrittura, ma è l'ultimo .
kbec

3
Credo RewriteCond %{HTTPS} offsia il modo preferito per verificare la presenza di una connessione HTTPS (nel tuo esempio di forzare il traffico non SSL a HTTPS)
Madbreaks

22

se hai bisogno di 'bloccare' i reindirizzamenti / riscritture interni che si verificano nel .htaccess, dai un'occhiata al

RewriteCond %{ENV:REDIRECT_STATUS} ^$

condizione, come discusso qui .


Grazie, è appena stato risolto il mio problema!
Matteo

Grazie anche per me, salvavita!
Benjamin

Questo è davvero un salvavita! Le persone dovrebbero esserne più consapevoli. In realtà, io sto andando a suggerire questo ad ogni domanda su .*con [L]la bandiera che ho letto prima che arrivassi qui.
Qwerty

Ho visto diverse modifiche a questo 200, !=200, ^., ^$. Apparentemente la variabile viene impostata su 200per un reindirizzamento, ma anche altre pagine (errori e cose del genere) la impostano su un valore. Ora che significa che si verifica sia se is empty, is not empty, is 200o is not 200, a seconda di quello che ti serve.
Qwerty

18

L'accordo con RewriteBase:

È quasi sempre necessario impostare RewriteBase. In caso contrario, apache ipotizza che la base sia il percorso del disco fisico della directory. Quindi inizia con questo:

RewriteBase /

Ah. Questo ha risolto completamente il problema che stavo avendo. Grazie per quello!
Tom Savage

3
Qualche modo per dire RewriteBase ., o qualcosa per indicare che dovrebbe mantenere l'URL lo stesso, cambiando semplicemente ciò che hai specificato?
Jay K

Grazie, questa è stata un'informazione inestimabile. :)
AturSams

2
È necessario impostare solo RewriteBasese si utilizza la sostituzione del percorso relativo nella RewriteRuledirettiva. È meglio evitare di utilizzare percorsi relativi.
MrWhite

2
Non sono d'accordo con questa risposta. Nel nostro team di sviluppo evitiamo del RewriteBasetutto poiché quasi tutti gli sviluppatori fraintendono ciò che fa. Come ha detto @ w3d, ne hai bisogno solo se vuoi salvare i caratteri e vuoi applicare la stessa base a tutte le tue RewriteRules in un file. Il tuo codice sarà probabilmente più chiaro agli altri se lo eviti.
Simon East

13

Altre insidie:

1- A volte è una buona idea disabilitare MultiViews

Options -MultiViews

Non conosco bene tutte le funzionalità di MultiViews, ma so che rovina le mie regole mod_rewrite quando è attivo, perché una delle sue proprietà è provare a "indovinare" un'estensione di un file che pensa che io stia cercando .

Ti spiego: 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 

Assumi che tutti gli URL che non corrispondono a un file oa una directory verranno acquisiti da file1.php. Sorpresa! Questa regola non viene rispettata per l'URL http: // myhost / file2 / somepath . Invece sei portato dentro file2.php.

Quello che sta succedendo è che MultiViews ha indovinato automaticamente che l'URL che volevi effettivamente era http: //myhost/file2.php/somepath e ti ha portato volentieri lì.

Ora, non hai idea di cosa sia appena successo e stai 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 testando meno ha senso.

Ok, in breve, se vuoi che mod_rewrite funzioni in un modo che si avvicini alla logica, disattivare MultiViews è un passo nella giusta direzione.

2- abilita FollowSymlinks

Options +FollowSymLinks 

Quello di cui non conosco i dettagli, ma l'ho visto menzionato molte volte, quindi fallo e basta.


Grazie :) Ho notato sorprese inaspettate come / log / activity trasformarsi in /log.txt/activity .. Grazie per il suggerimento :) .. peccato che i computer non si divertano mai a fare cose inaspettate come sedurre accidentalmente tutte le tue colleghe su Facebook :)
AturSams

1
+FollowSymLinksè menzionato nella documentazione come obbligatorio per mod_rewritelavorare, per vaghi motivi di sicurezza.
Joey

Due affermazioni qui mi preoccupano, immensamente: "Non sono d'accordo su tutte le funzionalità di MultiViews, ma so che rovina le mie regole mod_rewrite quando è attivo" e questa "Quella, non conosco i dettagli di , ma l'ho visto menzionato molte volte, quindi fallo e basta. " Vorrei che persone come te non scrivessero risposte su SO su cose di cui non sei sicuro.
TheCarver

1
@PaparazzoKid: Penso che tu stia scambiando COSÌ per un'enciclopedia. È una comunità di persone che si uniscono per ricucire una comprensione della tecnologia con cui stanno lavorando. A differenza di AW White e Joey prima di te, il tuo commento è quasi privo di valore. MV e FSL sono 2 delle molte opzioni di Apache. La mia risposta riguarda le insidie ​​quando si lavora con mod_rw in particolare, un modulo separato, che è in conflitto con alcune opzioni e funziona con altre. Ho spiegato come MV influisce su mod_rw e ho detto che + FSL è una raccomandazione popolare. Joey ha confermato che in effetti è obbligatorio. Cosa porti a tavola?
Michael Ekoka

Grazie. Ho appena passato la maggior parte di un'ora a far funzionare un sito legacy e provare a eseguire il debug delle regole di riscrittura, solo per scoprire che MultiViews aveva la precedenza su tutto.
Andrew McCombe

5

L'equazione può essere eseguita 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 dinamico del carico:

Se utilizzi mod_proxy per bilanciare il tuo sistema, è possibile aggiungere un intervallo dinamico di server worker.

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

4

È necessaria una migliore comprensione del flag [L]. Il flag [L] è l' ultimo, devi solo capire cosa farà in modo che la tua richiesta venga nuovamente instradata attraverso il motore di analisi degli URL. Dai documenti ( http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l ) (enfasi mia):

Il flag [L] fa sì 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. Questo corrisponde all'ultimo comando in Perl, o al comando break in C. Usa questo flag per indicare che la regola corrente dovrebbe essere applicata immediatamente senza considerare ulteriori regole.

Se stai usando RewriteRule nei file .htaccess o nelle <Directory>sezioni , è 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 essa. È possibile che mentre la richiesta riscritta viene gestita, il file o la<Directory> sezione.htaccesssi ritrovi di nuovo, e quindi il set di regole possa essere eseguito di nuovo dall'inizio. Più comunemente ciò accadrà se una delle regole provoca un reindirizzamento, interno o esterno, che fa ricominciare il processo di richiesta.

Così il [L] bandiera fa arrestare l'elaborazione di ulteriori regole di riscrittura per che passano 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 <Directory>sezione, la tua richiesta modificata verrà ritrasmessa attraverso il motore di analisi degli URL. E al passaggio successivo, 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 effetto.

Il modo migliore per aggirare questo problema è usare il flag [END] ( http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end ) invece del flag [L], se vuoi davvero fermarti ogni ulteriore elaborazione delle regole (e successiva revisione). Tuttavia, il flag [END] è disponibile solo per Apache v2.3.9 +, quindi se hai v2.2 o inferiore, sei bloccato solo con il flag [L]. In questo caso, è necessario fare affidamento sulle istruzioni RewriteCond per impedire la corrispondenza delle regole nei passaggi successivi del motore di analisi degli URL. Oppure devi assicurarti che le tue RewriteRule siano in un contesto (es. Httpd.conf) che non provochi il riesame della tua richiesta.


3

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

Sono come una sostituzione del valore-chiave:

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

Quindi puoi utilizzare una mappatura nelle tue regole come:

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

Ulteriori informazioni su questo argomento sono disponibili qui:

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


Ignora questa funzione se stai usando .htaccessriscritture basate su. Non funziona in questo contesto.
TerryE

2
La direttiva RewriteMap deve essere utilizzata nel contesto del server (httpd.conf), ma una volta definita lì, è possibile utilizzare la mappa tramite RewriteRule in un file .htaccess.
JaredC

2

mod_rewrite può modificare aspetti della gestione delle richieste senza alterare l'URL, ad esempio l'impostazione delle variabili d'ambiente, l'impostazione dei cookie, ecc. Questo è incredibilmente utile.

Imposta in modo condizionale una variabile d'ambiente:

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

Restituisce una risposta 503: RewriteRuleil [R]flag di può assumere un valore diverso da 3xx e restituire una risposta senza reindirizzamento, ad esempio per tempi di inattività / manutenzione gestiti:

RewriteRule .* - [R=503,L]

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

Inoltre, mod_rewrite può agire come un'interfaccia super potente per mod_proxy, quindi puoi farlo invece di scrivere ProxyPassdirettive:

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

Opinione: l'utilizzo di RewriteRuleses RewriteCondper instradare le richieste a diverse applicazioni o bilanciatori del carico in base a qualsiasi aspetto immaginabile della richiesta è semplicemente immensamente potente. Il controllo delle richieste nel loro percorso verso il backend e la possibilità di modificare le risposte durante il loro ritorno, rende mod_rewrite il luogo ideale per centralizzare tutta la configurazione relativa al routing.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.