Riferimento: mod_rewrite, riscrittura dell'URL e "graziosi collegamenti" spiegati


142

"Pretty links" è un argomento spesso richiesto, ma raramente è completamente spiegato. mod_rewrite è un modo per creare "graziosi collegamenti", ma è complesso e la sua sintassi è molto concisa, difficile da identificare e la documentazione assume un certo livello di competenza in HTTP. Qualcuno può spiegare in termini semplici come funzionano i "link graziosi" e come mod_rewrite può essere usato per crearli?

Altri nomi comuni, alias, termini per URL puliti: URL RESTful , URL intuitivi , URL SEO -friendly , slugging e URL MVC (probabilmente un termine improprio)


2
Slug o Slugging è un altro alias / termine comune per graziosi URL.
Mike B,

2
@Mike Sort of, ma le lumache fanno spesso parte di URL piuttosto carini. Una lumaca è piuttosto specifica quando, ad esempio, il titolo di un articolo viene trasformato in un modulo compatibile con URL che funge quindi da identificatore di quell'articolo. Quindi reference-mod-rewrite-url-rewriting-explainedè la lumaca, /questions/20563772/reference-mod-rewrite-url-rewriting-explainedè l'URL carino.
ingannare

2
Penso che i tag .htaccesse mod-rewritedovrebbero essere aggiornati per includere un link a questa domanda, poiché copre gran parte di ciò che viene chiesto su base regolare. Pensieri?
Mike Rockétt,

Risposte:


110

Per capire di quale mod_rewrite devi prima capire come funziona un web server. Un server Web risponde alle richieste HTTP . Una richiesta HTTP al suo livello più elementare è simile alla seguente:

GET /foo/bar.html HTTP/1.1

Questa è la semplice richiesta di un browser a un server Web che richiede l' URL /foo/bar.html da esso. È importante sottolineare che non richiede un file , ma richiede solo un URL arbitrario. La richiesta potrebbe anche apparire così:

GET /foo/bar?baz=42 HTTP/1.1

Questa è una richiesta altrettanto valida per un URL e ovviamente non ha più nulla a che fare con i file.

Il web server è un'applicazione in ascolto su una porta, che accetta richieste HTTP in arrivo su quella porta e restituisce una risposta. Un web server è completamente libero di rispondere a qualsiasi richiesta in qualsiasi modo ritenga opportuno / in qualsiasi modo sia stato configurato per rispondere. Questa risposta non è un file, è una risposta HTTP che può o meno avere a che fare con file fisici su qualsiasi disco. Un web server non deve essere Apache, ci sono molti altri web server che sono tutti solo programmi che funzionano in modo persistente e sono collegati a una porta che risponde alle richieste HTTP. Puoi scriverne uno tu stesso. Questo paragrafo intendeva separarti da qualsiasi idea che gli URL uguagliano direttamente i file, il che è davvero importante da capire. :)

La configurazione predefinita della maggior parte dei server Web è cercare un file che corrisponda all'URL sul disco rigido. Se la radice del documento del server è impostata su, diciamo, /var/wwwpotrebbe sembrare se il file /var/www/foo/bar.htmlesiste e servirlo in tal caso. Se il file termina con ".php" invocherà l'interprete PHP e quindi restituirà il risultato. Tutta questa associazione è completamente configurabile; un file non deve terminare in ".php" affinché il server Web lo esegua tramite l'interprete PHP e l'URL non deve corrispondere a un determinato file sul disco perché accada qualcosa.

mod_rewrite è un modo per riscrivere la gestione delle richieste interne. Quando il server Web riceve una richiesta per l'URL /foo/bar, è possibile riscrivere tale URL in qualcos'altro prima che il server Web cercherà un file sul disco corrispondente. Esempio semplice:

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

Questa regola dice ogni volta che una richiesta corrisponde a "/ foo / bar", riscrivila in "/ foo / baz". La richiesta verrà quindi gestita come se /foo/bazfosse stata invece richiesta. Questo può essere usato per vari effetti, ad esempio:

RewriteRule (.*) $1.html

Questa regola corrisponde a qualunque cosa ( .*) e la cattura ( (..)), quindi la riscrive per aggiungere ".html". In altre parole, se /foo/barera l'URL richiesto, verrà gestito come se /foo/bar.htmlfosse stato richiesto. Vedere http://regular-expressions.info per ulteriori informazioni sulla corrispondenza, l'acquisizione e le sostituzioni di espressioni regolari.

Un'altra regola spesso riscontrata è questa:

RewriteRule (.*) index.php?url=$1

Questo, ancora una volta, corrisponde a qualsiasi cosa e lo riscrive nel file index.php con l'URL originariamente richiesto aggiunto nel urlparametro query. Cioè, per tutte le richieste in arrivo, il file index.php viene eseguito e questo file avrà accesso alla richiesta originale in $_GET['url'], quindi può fare tutto ciò che vuole con esso.

In primo luogo, inserisci queste regole di riscrittura nel file di configurazione del tuo server web . Apache consente inoltre * di inserirli in un file chiamato .htaccessall'interno della radice del documento (ovvero accanto ai file .php).

* Se consentito dal file di configurazione principale di Apache; è facoltativo, ma spesso abilitato.

Ciò che mod_rewrite fa non lo fanno

mod_rewrite non rende magicamente tutti i tuoi URL "belli". Questo è un malinteso comune. Se hai questo link nel tuo sito web:

<a href="https://stackoverflow.com/my/ugly/link.php?is=not&amp;very=pretty">

non c'è niente che mod_rewrite possa fare per renderlo carino. Per rendere questo un bel link, devi:

  1. Cambia il link in un link carino:

    <a href="https://stackoverflow.com/my/pretty/link">
    
  2. Utilizzare mod_rewrite sul server per gestire la richiesta all'URL /my/pretty/linkutilizzando uno dei metodi sopra descritti.

(Si potrebbe usare mod_substitutecongiuntamente per trasformare le pagine HTML in uscita e i loro collegamenti contenuti. Anche se questo è generalmente uno sforzo maggiore rispetto al semplice aggiornamento delle risorse HTML.)

Mod_rewrite può fare molto e regole di abbinamento molto complesse che puoi creare, tra cui concatenare diverse riscritture, inviare richieste a un servizio o macchina completamente diverso, restituire codici di stato HTTP specifici come risposte, reindirizzare richieste ecc. È molto potente e può essere utilizzato per ottimo se si capisce il meccanismo fondamentale di richiesta-risposta HTTP. Essa non rende automaticamente i vostri collegamenti abbastanza.

Vedi la documentazione ufficiale per tutte le possibili bandiere e opzioni.


6
Forse menzionare la direttiva FallbackResource introdotta nella versione 2.2.16 come il modo preferito di riscrivere a un dispatcher.
Darsstar,

78

Per espandere la risposta all'inganno , volevo fornire alcuni esempi e spiegazioni di alcune altre funzionalità di mod_rewrite.

Tutti gli esempi seguenti presuppongono che tu abbia già incluso RewriteEngine Onnel tuo .htaccessfile.

Esempio di riscrittura

Facciamo questo esempio:

RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]

La regola è divisa in 4 sezioni:

  1. RewriteRule - avvia la regola di riscrittura
  2. ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - Questo si chiama modello, tuttavia mi limiterò a chiamarlo come il lato sinistro della regola - da cosa vuoi riscrivere
  3. blog/index.php?id=$1&title=$2 - ha chiamato la sostituzione o il lato destro di una regola di riscrittura - a cosa vuoi riscrivere
  4. [NC,L,QSA] sono flag per la regola di riscrittura, separati da una virgola, che spiegherò più avanti

La riscrittura sopra ti consentirebbe di collegarti a qualcosa di simile /blog/1/foo/e verrebbe effettivamente caricato /blog/index.php?id=1&title=foo.

Lato sinistro della regola

  • ^indica l'inizio del nome della pagina, quindi riscriverà example.com/blog/...ma nonexample.com/foo/blog/...
  • Ogni serie di (…)parentesi rappresenta un'espressione regolare che possiamo acquisire come variabile nella parte destra della regola. In questo esempio:
    • La prima serie di parentesi - ([0-9]+)- corrisponde a una stringa con una lunghezza minima di 1 carattere e solo con valori numerici (ovvero 0-9). È possibile fare riferimento a questo $1nella parte destra della regola
    • La seconda serie di parentesi corrisponde a una stringa con una lunghezza minima di 1 carattere, contenente solo caratteri alfanumerici (AZ, az o 0-9) oppure -oppure +(la nota +viene salvata con una barra rovesciata poiché senza la sua fuga questa verrà eseguita come regex carattere ripetitivo ). È possibile fare riferimento a questo $2nella parte destra della regola
  • ?significa che il carattere precedente è facoltativo, quindi in questo caso entrambi /blog/1/foo/e /blog/1/fooriscriverebbero nello stesso posto
  • $ indica che questa è la fine della stringa che vogliamo abbinare

bandiere

Queste sono opzioni che vengono aggiunte tra parentesi quadre alla fine della regola di riscrittura per specificare determinate condizioni. Ancora una volta, ci sono molte bandiere diverse che puoi leggere nella documentazione , ma esaminerò alcune delle bandiere più comuni:

NC

Il flag no case significa che la regola di riscrittura non fa distinzione tra maiuscole e minuscole, quindi per la regola di esempio sopra ciò significherebbe che entrambi /blog/1/foo/e /BLOG/1/foo/(o qualsiasi variazione di questo) sarebbero abbinati.

L

L'ultimo flag indica che questa è l'ultima regola che deve essere elaborata. Ciò significa che se e solo se questa regola corrisponde, non verranno valutate ulteriori regole nell'esecuzione corrente dell'elaborazione della riscrittura. Se la regola non corrisponde, tutte le altre regole verranno provate come al solito. Se non si imposta il Lflag, tutte le seguenti regole verranno applicate all'URL riscritto in seguito.

END

Da Apache 2.4 puoi anche usare il [END]flag. Una regola corrispondente con essa terminerà completamente l' ulteriore elaborazione di alias / riscrittura. (Mentre la [L]bandiera può spesso innescare un secondo round, ad esempio quando si riscrive dentro o fuori le sottodirectory.)

QSA

Il flag append della stringa di query ci consente di passare variabili extra all'URL specificato che verrà aggiunto ai parametri get originali. Per il nostro esempio, ciò significa che /blog/1/foo/?comments=15dovrebbe caricarsi qualcosa del genere/blog/index.php?id=1&title=foo&comments=15

R

Questa bandiera non è quella che ho usato nell'esempio sopra, ma è una che ho pensato valga la pena menzionare. Ciò consente di specificare un reindirizzamento http, con l'opzione di includere un codice di stato (ad es R=301.). Ad esempio, se si desidera eseguire un reindirizzamento 301 su / myblog / su / blog / scrivere semplicemente una regola in questo modo:

RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]

Riscrivi condizioni

Le condizioni di riscrittura rendono le riscritture ancora più potenti, consentendo di specificare le riscritture per situazioni più specifiche. Ci sono molte condizioni che puoi leggere nella documentazione , ma toccherò alcuni esempi comuni e li spiegherò:

# if the host doesn't start with www. then add it and redirect
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Questa è una pratica molto comune, che anteporrà il tuo dominio www.(se non è già presente) ed eseguirà un reindirizzamento 301. Ad esempio, caricarlo http://example.com/blog/ti reindirizzerebbe ahttp://www.example.com/blog/

# if it cant find the image, try find the image on another domain
RewriteCond %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)$ http://www.example.com/$1 [L]

Questo è leggermente meno comune, ma è un buon esempio di una regola che non viene eseguita se il nome file è una directory o un file esistente sul server.

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] eseguirà la riscrittura solo per i file con estensione jpg, jpeg, gif o png (senza distinzione tra maiuscole e minuscole).
  • %{REQUEST_FILENAME} !-f verificherà se il file esiste sul server corrente ed eseguirà la riscrittura solo in caso contrario
  • %{REQUEST_FILENAME} !-d verificherà se il file esiste sul server corrente ed eseguirà la riscrittura solo in caso contrario
  • La riscrittura tenterà di caricare lo stesso file su un altro dominio

39

Riferimenti

Stack Overflow ha molte altre grandi risorse per iniziare:

E le panoramiche regex per i nuovi arrivati ​​anche:

Segnaposto usati spesso

  • .*corrisponde a qualsiasi cosa, anche una stringa vuota. Non si desidera utilizzare questo modello ovunque, ma spesso nell'ultima regola di fallback.
  • [^/]+è più spesso usato per i segmenti di percorso. Corrisponde a tutto tranne che alla barra.
  • \d+ corrisponde solo alle stringhe numeriche.
  • \w+corrisponde a caratteri alfanumerici. È praticamente una scorciatoia per [A-Za-z0-9_].
  • [\w\-]+per segmenti di percorso in stile "lumaca", usando lettere, numeri, trattino - e _
  • [\w\-.,]+aggiunge punti e virgole. Preferisci un \-trattino evaso nelle […]classi.
  • \.indica un periodo letterale. Altrimenti .al di fuori di […]è il segnaposto per qualsiasi simbolo.

Ognuno di questi segnaposto è solitamente racchiuso tra (…)parentesi come gruppo di acquisizione. E l'intero modello spesso nei ^………$marcatori inizio + fine. La citazione di "motivi" è facoltativa.

RewriteRules

I seguenti esempi sono incentrati su PHP e leggermente più incrementali, più facili da adattare per casi simili. Sono solo dei riassunti, spesso collegati a più varianti o domande e risposte dettagliate.

  • Mappatura statica
    /contact,/about

    Accorciare alcuni nomi di pagina in schemi di file interni è molto semplice:

     RewriteRule ^contact$  templ/contact.html
     RewriteRule ^about$    about.php
    
  • Identificatori numerici
    /object/123

    Anche introdurre scorciatoie come http://example.com/article/531negli script PHP esistenti è facile. Il segnaposto numerico può essere semplicemente rimappato in un $_GETparametro:

     RewriteRule ^article/(\d+)$    article-show.php?id=$1
     #                      └───────────────────────────┘
    
  • Segnaposto stile lumaca
    /article/with-some-title-slug

    Puoi facilmente estendere quella regola per consentire ai /article/title-stringsegnaposto:

     RewriteRule ^article/([\w-]+)$    article-show.php?title=$1
     #                       └────────────────────────────────┘
    

    Nota che il tuo script deve essere in grado (o essere adattato) per mappare quei titoli agli ID del database. RewriteRules da solo non può creare o indovinare informazioni dal nulla.

  • Lumache con prefissi numerici
    /readable/123-plus-title

    Pertanto vedrai spesso /article/529-title-slugpercorsi misti utilizzati nella pratica:

     RewriteRule ^article/(\d+)-([\w-]+)$    article.php?id=$1&title=$2
     #                      └───────────────────────────────┘
    

    Ora puoi semplicemente saltare il passaggio title=$2comunque, perché lo script in genere si baserà comunque sull'ID database. La -title-slugdecorazione URL è diventata arbitraria.

  • Uniformità con elenchi alternativi
    /foo/… /bar/… /baz/…

    Se hai regole simili per più percorsi di pagine virtuali, puoi abbinarli e compattarli con |elenchi alternativi. E di nuovo semplicemente riassegnali ai parametri GET interni:

     #                               ┌─────────────────────────┐
     RewriteRule ^(blog|post|user)/(\w+)$  disp.php?type=$1&id=$2
     #               └───────────────────────────────────┘
    

    Puoi dividerli in singoli RewriteRulese questo diventa troppo complesso.

  • Invio di URL correlati a diversi backend
    /date/SWITCH/backend

    Un uso più pratico di elenchi alternativi è la mappatura dei percorsi delle richieste su script distinti. Ad esempio per fornire URL uniformi per un'applicazione Web più vecchia e più recente in base alle date:

     #                   ┌─────────────────────────────┐
     #                   │                 ┌───────────┼───────────────┐
     RewriteRule ^blog/(2009|2010|2011)/([\d-]+)/?$ old/blog.php?date=$2
     RewriteRule ^blog/(\d+)/([\d-]+)/?$  modern/blog/index.php?start=$2
     #                          └──────────────────────────────────────┘
    

    Ciò rimappa semplicemente i post 2009-2011 su una sceneggiatura e tutti gli altri anni implicitamente su un altro gestore. Nota la regola più specifica che viene prima . Ogni script potrebbe utilizzare parametri GET diversi.

  • Altri delimitatori oltre alle semplici /barre dei percorsi
    /user-123-name

    Più comunemente vedi RewriteRules per simulare una struttura di directory virtuale. Ma non sei costretto ad essere non creativo. Puoi anche usare -trattini per la segmentazione o la struttura.

     RewriteRule ^user-(\d+)$    show.php?what=user&id=$1
     #                   └──────────────────────────────┘
     # This could use `(\w+)` alternatively for user names instead of ids.
    

    Per lo /wiki:section:Page_Nameschema anche comune :

     RewriteRule ^wiki:(\w+):(\w+)$  wiki.php?sect=$1&page=$2 
     #                   └─────┼────────────────────┘       │
     #                         └────────────────────────────┘
    

    Occasionalmente è adatto per alternare tra /-delimiters e :o .nella stessa regola. O hai di nuovo due RewriteRules per mappare le varianti su diversi script.

  • Finale opzionale /barra
    /dir=/dir/

    Quando si optano per percorsi in stile directory, è possibile renderlo raggiungibile con e senza un finale /

     RewriteRule ^blog/([\w-]+)/?$  blog/show.php?id=$1
     #                         ┗┛
    

    Ora questo gestisce sia http://example.com/blog/123e /blog/123/. E l' /?$approccio è facile da aggiungere a qualsiasi altra RewriteRule.

  • Segmenti flessibili per percorsi virtuali
    .*/.*/.*/.*

    La maggior parte delle regole che incontrerai associano un insieme vincolato di /…/segmenti del percorso delle risorse ai singoli parametri GET. Alcuni script gestiscono comunque un numero variabile di opzioni . Il motore regexp di Apache non consente di facoltarne un numero arbitrario. Ma puoi facilmente espanderlo in un blocco di regole:

     Rewriterule ^(\w+)/?$                in.php?a=$1
     Rewriterule ^(\w+)/(\w+)/?$          in.php?a=$1&b=$2
     Rewriterule ^(\w+)/(\w+)/(\w+)/?$    in.php?a=$1&b=$2&c=$3
     #              └─────┴─────┴───────────────────┴────┴────┘
    

    Se hai bisogno di un massimo di cinque segmenti di percorso, copia questo schema in cinque regole. Ovviamente puoi usare un [^/]+segnaposto più specifico ciascuno. Qui l'ordinamento non è così importante, in quanto non si sovrappone. Quindi avere prima i percorsi più frequentemente utilizzati va bene.

    In alternativa, puoi utilizzare i parametri dell'array PHP tramite la ?p[]=$1&p[]=$2&p[]=3stringa di query qui, se lo script li preferisce semplicemente suddivisi. (Anche se è più comune usare solo una regola generale e lasciare che lo script stesso espanda i segmenti fuori da REQUEST_URI.)

    Vedi anche: Come posso trasformare i segmenti del mio percorso URL in coppie chiave-valore della stringa di query?

  • Segmenti opzionali
    prefix/opt?/.*

    Una variante comune è avere prefissi opzionali all'interno di una regola. Questo di solito ha senso se hai stringhe statiche o più segnaposto vincolati in giro:

      RewriteRule ^(\w+)(?:/([^/]+))?/(\w+)$  ?main=$1&opt=$2&suffix=$3
    

    Ora il modello più complesso racchiude (?:/([^/])+)?semplicemente un gruppo non di acquisizione (?:…) e lo rende facoltativo )?. Il segnaposto contenuto ([^/]+)sarebbe un modello di sostituzione $2, ma sarebbe vuoto se non esiste un /…/percorso intermedio .

  • Cattura il resto
    /prefix/123-capture/…/*/…whatever…

    Come detto prima, spesso non si vogliono schemi di riscrittura troppo generici. Ha tuttavia senso combinare confronti statici e specifici con un a .*volte.

     RewriteRule ^(specific)/prefix/(\d+)(/.*)?$  speci.php?id=$2&otherparams=$2
    

    Ciò ha facoltato tutti /…/…/…i segmenti del percorso finale. Il che, ovviamente, richiede che lo script di gestione li divida e che i parametri siano estratti in modo variabile (che è ciò che fanno i framework Web "MVC" ).

  • File "estensioni" finali
    /old/path.HTML

    Gli URL non hanno davvero estensioni di file. Di cosa tratta l'intero riferimento (= gli URL sono localizzatori virtuali, non necessariamente un'immagine diretta del filesystem). Tuttavia, se in precedenza avevi un mapping di file 1: 1, puoi creare regole più semplici:

     RewriteRule  ^styles/([\w\.\-]+)\.css$  sass-cache.php?old_fn_base=$1
     RewriteRule  ^images/([\w\.\-]+)\.gif$  png-converter.php?load_from=$2
    

    Altri usi comuni sono la rimappatura di .htmlpercorsi obsoleti per i .phpgestori più recenti o il semplice aliasing dei nomi di directory solo per singoli file (reali / reali).

  • Ping-Pong (reindirizza e riscrive all'unisono)
    /ugly.html← →/pretty

    Quindi ad un certo punto stai riscrivendo le tue pagine HTML per portare solo graziosi link, come indicato dall'inganno . Nel frattempo riceverai comunque richieste per i vecchi percorsi, a volte anche dai segnalibri. Per ovviare al problema , è possibile eseguire il ping-pong dei browser per visualizzare / stabilire i nuovi URL.

    Questo trucco comune prevede l'invio di un reindirizzamento 30x / Posizione ogni volta che un URL in arrivo segue lo schema di denominazione obsoleto / brutto. I browser sarà poi rerequest il nuovo / abbastanza URL, che in seguito viene riscritto (solo internamente) nella posizione originale o nuova.

     # redirect browser for old/ugly incoming paths
     RewriteRule ^old/teams\.html$ /teams [R=301,QSA,END]
    
     # internally remap already-pretty incoming request
     RewriteRule ^teams$ teams.php        [QSA,END]
    

    Nota come questo esempio utilizza solo [END]invece di [L]alternare in modo sicuro. Per le versioni precedenti di Apache 2.2 è possibile utilizzare altre soluzioni alternative, oltre a rimappare i parametri della stringa di query, ad esempio: reindirizzare l'URL da brutto a grazioso, rimappare al percorso brutto, senza loop infiniti

  • Spazi nei modelli
    /this+that+

    Non è così carino nelle barre degli indirizzi del browser, ma puoi usare spazi negli URL. Per i pattern di riscrittura utilizzare \␣spazi con escape backslash . Altrimenti basta "citare l'intero schema o la sostituzione:

     RewriteRule  "^this [\w ]+/(.*)$"  "index.php?id=$1"  [L]
    

    I client serializzano gli URL con +o %20per gli spazi. Eppure in RewriteRules sono interpretati con caratteri letterali per tutti i segmenti di percorso relativi.

Duplicati frequenti:

prevalenti .htaccessinsidie

Ora prendilo con un granello di sale. Non tutti i consigli possono essere generalizzati in tutti i contesti. Questo è solo un semplice riassunto di noti ostacoli e alcuni ostacoli incombenti:

  • Abilita mod_rewritee.htaccess

    Per utilizzare effettivamente RewriteRules nei file di configurazione per directory, è necessario:

    • Verifica che il tuo server sia AllowOverride Allabilitato . In caso contrario, le .htaccessdirettive per directory verranno ignorate e RewriteRules non funzionerà.

    • Ovviamente hai mod_rewriteabilitato nella httpd.confsezione dei tuoi moduli.

    • Prepara ogni elenco di regole con RewriteEngine Onancora. Mentre mod_rewrite è implicitamente attivo in <VirtualHost>e <Directory>sezioni, i .htaccessfile per directory devono essere convocati singolarmente.

  • La barra iniziale ^/non corrisponderà

    Non dovresti iniziare i tuoi .htaccessschemi RewriteRule ^/normalmente:

     RewriteRule ^/article/\d+$  …
                  ↑
    

    Questo è spesso visto nei vecchi tutorial. Ed era corretto per le antiche versioni Apache 1.x. Al giorno d'oggi i percorsi di richiesta sono comodamente completamente directory relativa a .htaccessRewriteRules. Basta lasciare il comando /.

    · Notare che la barra iniziale è comunque corretta nelle <VirtualHost>sezioni. Ecco perché lo vedi spesso ^/?opzionale per la parità delle regole.
    · O quando usi un RewriteCond %{REQUEST_URI}abbinerai comunque per un vantaggio /.
    · Vedi anche Webmaster.SE: quando è necessaria la barra iniziale (/) nei modelli mod_rewrite?

  • <IfModule *> involucri generati!

    Probabilmente hai visto questo in molti esempi:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • Si fa avere senso in <VirtualHost>sezioni - se è stata combinata con un'altra opzione di ripiego, come ad esempio ScriptAliasMatch. (Ma nessuno lo fa mai).
    • Ed è comunemente distribuito per .htaccessset di regole predefiniti con molti progetti open source. Lì è solo inteso come fallback e mantiene gli URL "brutti" come predefiniti.

    Comunque non lo vuoi di solito nei tuoi .htaccessfile.

    • Innanzitutto, mod_rewrite non si disattiva casualmente. (Se lo facesse, avresti grossi problemi).
    • Se fosse davvero disabilitato, le tue RewriteRules non funzionerebbero comunque.
    • Ha lo scopo di prevenire 500errori HTTP . Ciò che di solito realizza è invece rendere gli utenti con 404errori HTTP . (Non molto più user-friendly se ci pensate.)
    • Praticamente sopprime solo le voci di registro più utili o le e-mail di notifica del server. Non saresti più saggio sul motivo per cui le tue RewriteRules non funzionano mai.

    Ciò che sembra allettante come salvaguardia generalizzata, si rivela spesso un ostacolo nella pratica.

  • Non usare a RewriteBasemeno che non sia necessario

    Molti esempi di copia + incolla contengono una RewriteBase /direttiva. Che sembra essere comunque il default implicito. Quindi non ti serve davvero. È una soluzione alternativa per fantasiosi schemi di riscrittura VirtualHost e percorsi DOCUMENT_ROOT errati per alcuni hoster condivisi.

    Ha senso utilizzare con singole applicazioni Web in sottodirectory più profonde. In questi casi può accorciare i pattern RewriteRule. In genere è preferibile preferire gli identificatori di percorso relativi nei set di regole per directory.

    Vedi anche Come funziona RewriteBase in .htaccess

  • Disabilita MultiViewsquando i percorsi virtuali si sovrappongono

    La riscrittura degli URL viene utilizzata principalmente per supportare percorsi di ingresso virtuali . Comunemente v'è solo un copione dispatcher ( index.php) o un paio di gestori di singoli ( articles.php, blog.php, wiki.php, ...). Quest'ultimo potrebbe scontrarsi con percorsi RewriteRule virtuali simili.

    Ad /article/123esempio, una richiesta potrebbe essere mappata implicitamente article.phpcon un /123PATH_INFO. Dovresti o proteggere le tue regole con commonplace RewriteCond !-f+ !-d, e / o disabilitare il supporto PATH_INFO, o forse semplicemente disabilitare Options -MultiViews.

    Il che non vuol dire che sempre necessario . La negoziazione del contenuto è solo un automatismo delle risorse virtuali.

  • L'ordine è importante

    Vedi tutto quello che avresti sempre voluto sapere su mod_rewrite se non l'hai già fatto. La combinazione di più RewriteRules spesso porta all'interazione. Questo non è qualcosa che previene abitualmente per [L]bandiera, ma uno schema che abbraccerai una volta versato. È possibile ri-ri-ri scrittura percorsi virtuali da una regola a un altro, fino a raggiungere un gestore obiettivo reale.

    Tuttavia, spesso vorrai avere le regole più specifiche ( /forum/…schemi di stringa fissi o segnaposto più restrittivi [^/.]+) nelle prime regole. Le regole generiche di slurp-all ( .*) sono meglio lasciate a quelle successive . (Un'eccezione è una RewriteCond -f/-dguardia come blocco primario.)

  • Fogli di stile e immagini smettono di funzionare

    Quando si introducono strutture di directory virtuali, /blog/article/123questo ha un impatto sui relativi riferimenti di risorse in HTML (come <img src=mouse.png>). Che può essere risolto da:

    • Utilizzando solo riferimenti assoluti server href="https://stackoverflow.com/old.html"osrc="/logo.png"
    • Spesso semplicemente aggiungendo <base href="https://stackoverflow.com/index">nella tua <head>sezione HTML . Ciò abbina implicitamente riferimenti relativi a ciò che erano prima.

    In alternativa, puoi creare ulteriori RewriteRules per ricollegare .csso tracciare .pngle posizioni originali. Ma non è necessario o comporta ulteriori reindirizzamenti e ostacola la memorizzazione nella cache.

    Vedi anche: CSS, JS e le immagini non vengono visualizzate con abbastanza URL

  • RewriteConds maschera solo una RewriteRule

    Una comune interpretazione errata è che un RewriteCond blocca più RewriteRules (perché sono disposti visivamente insieme):

     RewriteCond %{SERVER_NAME} localhost
     RewriteRule ^secret  admin/tools.php
     RewriteRule ^hidden  sqladmin.cgi
    

    Quale non per impostazione predefinita. Puoi incatenarli usando la [S=2]bandiera. Altrimenti dovrai ripeterli. Mentre a volte è possibile creare una regola primaria "invertita" per [END] l'elaborazione di riscrittura in anticipo.

  • QUERY_STRING esente da RewriteRules

    Non puoi abbinare RewriteRule index.php\?x=y, perché mod_rewrite confronta solo con percorsi relativi per impostazione predefinita. Puoi abbinarli separatamente tuttavia tramite:

     RewriteCond %{QUERY_STRING} \b(?:param)=([^&]+)(?:&|$)
     RewriteRule ^add/(.+)$  add/%1/$1  # ←──﹪₁──┘
    

    Vedi anche Come posso abbinare le variabili della stringa di query con mod_rewrite?

  • .htaccess vs. <VirtualHost>

    Se stai usando RewriteRules in un file di configurazione per directory, preoccuparti delle prestazioni di regex è inutile. Apache mantiene i modelli PCRE compilati più a lungo di un processo PHP con un framework di routing comune. Per i siti ad alto traffico dovresti comunque considerare di spostare i set di regole nella configurazione del server vhost, una volta che sono stati testati in battaglia.

    In questo caso, preferisci il ^/?prefisso del separatore di directory facoltativo . Ciò consente di spostare liberamente RewriteRules tra i file di configurazione di PerDir e del server.

  • Ogni volta che qualcosa non funziona

    Non preoccuparti.

    • Confronta access.logeerror.log

      Spesso puoi capire come una regola di riscrittura si comporti semplicemente osservando il tuo error.loge access.log. Correlare i tempi di accesso per vedere quale percorso di richiesta originariamente è arrivato e in quale percorso / file Apache non è stato in grado di risolvere (errore 404/500).

      Questo non ti dice quale RewriteRule è il colpevole. Ma percorsi finali inaccessibili come /docroot/21-.itle?index.phppossono dare via dove ispezionare ulteriormente. Altrimenti disabilita le regole fino a quando non ottieni alcuni percorsi prevedibili.

    • Abilita RewriteLog

      Vedi i documenti Apache RewriteLog . Per il debug è possibile abilitarlo nelle sezioni vhost:

      # Apache 2.2
      RewriteLogLevel 5
      RewriteLog /tmp/rewrite.log
      
      # Apache 2.4
      LogLevel alert rewrite:trace5
      #ErrorLog /tmp/rewrite.log
      

      Ciò fornisce un riepilogo dettagliato di come i percorsi delle richieste in entrata vengono modificati da ciascuna regola:

      [..] applying pattern '^test_.*$' to uri 'index.php'
      [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
      [..] applying pattern '^index\.php$' to uri 'index.php'
      

      Ciò aiuta a restringere le regole eccessivamente generiche e regex disavventure.

      Vedi anche:
      · .htaccess non funzionante (mod_rewrite)
      · Suggerimenti per il debug delle regole di riscrittura .htaccess

    • Prima di porre la tua domanda

      Come forse saprai, Stack Overflow è molto adatto per porre domande su mod_rewrite. Renderli in tema includendo ricerche e tentativi precedenti (evitare risposte ridondanti), dimostrare di base comprensione e:

      • Includi esempi completi di URL di input, percorsi target riscritti erroneamente, la tua struttura di directory reale.
      • Il set completo RewriteRule, ma anche quello presunto difettoso.
      • Versioni di Apache e PHP, tipo di sistema operativo, filesystem, DOCUMENT_ROOT e $_SERVERambiente PHP se si tratta di una mancata corrispondenza dei parametri.
      • Un estratto dal tuo access.loge error.logper verificare a cosa sono state risolte le regole esistenti. Meglio ancora, un rewrite.logriassunto.

      Ciò consente di ottenere risposte più rapide ed esatte e le rende più utili agli altri.

  • Commenta il tuo .htaccess

    Se copi esempi da qualche parte, assicurati di includere a # comment and origin link. Mentre è semplicemente cattiva educazione omettere l'attribuzione, spesso fa male alla manutenzione in seguito. Documentare qualsiasi codice o fonte tutorial. In particolare, mentre non sei sorpreso, dovresti essere ancora più interessato a non trattarli come scatole magiche.

  • Non è "SEO" -URL

    Disclaimer: solo una pipì per animali domestici. Spesso senti schemi di riscrittura di URL piuttosto chiamati collegamenti "SEO" o qualcosa del genere. Anche se questo è utile per gli esempi su Google, è un termine improprio datato.

    Nessuno dei moderni motori di ricerca è realmente disturbato da .htmle .phpin segmenti di percorso, o ?id=123stringhe di query per quella materia. Motori di ricerca di vecchi, come AltaVista, ha fatto evitare di strisciare siti web con i percorsi di accesso potenzialmente ambigua. I crawler moderni sono spesso persino desiderosi di risorse web profonde.

    A quali URL "carini" dovrebbe essere usato concettualmente è rendere i siti web facili da usare .

    1. Avere schemi di risorse leggibili e ovvi.
    2. Garantire la durata degli URL ( permalink di AKA ).
    3. Fornire rilevabilità attraverso /common/tree/nesting.

    Tuttavia, non sacrificare requisiti unici per il conformismo.

Utensili

Esistono vari strumenti online per generare RewriteRules per la maggior parte degli URL con parametri GET:

Principalmente produce solo [^/]+segnaposto generici, ma probabilmente è sufficiente per siti banali.


Serve ancora un po 'di riscrittura, più collegamenti e le molte sottotitoli sono in qualche modo odiose. C'è qualche sovrapposizione con le altre risposte qui, quindi può essere ridotta forse. Si tratta principalmente degli esempi visivi e di quella lista di gotcha comuni.
mario,

3
Non vedevo una tale bellezza di risposta da molto tempo! I miei occhi brillano mentre lo leggo. Per favore, non smettere di pubblicare tali risposte :)
Rizier123

1
Post eccellente. Mi ha fatto capire molto rapidamente i concetti di base di mod_rewrite!
breez,

6

Alternative a mod_rewrite

Molti schemi di URL virtuali di base possono essere raggiunti senza usare RewriteRules. Apache consente di invocare gli script PHP senza .phpestensione e con un PATH_INFOargomento virtuale .

  1. Usa PATH_INFO , Luke

    Oggi AcceptPathInfo Onè spesso abilitato per impostazione predefinita. Ciò che sostanzialmente consente .phpe altri URL di risorse di portare un argomento virtuale:

    http://example.com/script.php/virtual/path
    

    Ora questo /virtual/pathappare in PHP come $_SERVER["PATH_INFO"]dove puoi gestire qualsiasi argomento extra come preferisci.

    Questo non è così conveniente come avente Apache segmenti di percorso di ingresso distinto in $1, $2, $3e passandoli come distinte $_GETvariabili a PHP. Sta semplicemente emulando "graziosi URL" con meno sforzo di configurazione.

  2. Abilita MultiView per nascondere l' .phpestensione

    L'opzione più semplice per evitare anche le .php"estensioni di file" negli URL è abilitare:

    Options +MultiViews
    

    Questo ha Apache selezionare article.phpper le richieste HTTP su a /articlecausa del nome di base corrispondente. E questo funziona bene insieme alla funzione PATH_INFO di cui sopra. Quindi puoi semplicemente usare URL come http://example.com/article/virtual/title. Ciò ha senso se si dispone di un'applicazione Web tradizionale con più punti / script di invocazione PHP.

    Si noti che MultiViews ha comunque uno scopo diverso / più ampio. Presenta una penalità di prestazione molto minore , perché Apache cerca sempre altri file con nomi di base corrispondenti. In realtà è pensato per Content-negoziazione , in modo da browser ricevono la migliore alternativa tra le risorse disponibili (come ad esempio article.en.php, article.fr.php, article.jp.mp4).

  3. SetType o SetHandler per .phpscript senza estensione

    Un approccio più diretto per evitare il trasporto di .phpsuffissi negli URL è la configurazione del gestore PHP per altri schemi di file. L'opzione più semplice ha la precedenza sul tipo MIME / gestore predefinito tramite .htaccess:

    DefaultType application/x-httpd-php
    

    In questo modo potresti semplicemente rinominare il tuo article.phpscript in solo article(senza estensione), ma comunque elaborarlo come script PHP.

    Ora questo può avere delle implicazioni in termini di sicurezza e prestazioni, perché tutti i file senza estensione verrebbero trasferiti tramite PHP ora. Pertanto è possibile in alternativa impostare questo comportamento solo per i singoli file:

    <Files article>
      SetHandler application/x-httpd-php
      # or SetType 
    </Files>
    

    Ciò dipende in qualche modo dalla configurazione del server e dal PHI SAPI utilizzato. Le alternative comuni includono ForceType application/x-httpd-phpo AddHandler php5-script.

    Notare ancora che tali impostazioni si propagano da una .htaccessalle sottocartelle. Dovresti sempre disabilitare l'esecuzione degli script ( SetHandler Nonee Options -Execo php_flag engine offecc.) Per le risorse statiche e upload / directory ecc.

  4. Altri schemi di riscrittura di Apache

    Tra le sue numerose opzioni, Apache offre mod_aliasfunzionalità che a volte funzionano come mod_rewriteRewriteRules. Notare che la maggior parte di questi deve essere impostata in una <VirtualHost>sezione, non nei .htaccessfile di configurazione per directory.

    • ScriptAliasMatchè principalmente per gli script CGI, ma dovrebbe anche funzionare per PHP. Permette regexps come qualsiasi altro RewriteRule. In effetti è forse l'opzione più robusta per configurare un controller frontale accattivante.

    • E un piano Aliasaiuta anche con alcuni semplici schemi di riscrittura.

    • Anche una semplice ErrorDocumentdirettiva potrebbe essere utilizzata per consentire a uno script PHP di gestire percorsi virtuali. Si noti che questa è una soluzione alternativa kludgy tuttavia, vieta qualsiasi cosa tranne GET richieste e inonda il file error.log per definizione.

    Vedi http://httpd.apache.org/docs/2.2/urlmapping.html per ulteriori suggerimenti.

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.