Źródła: mod_rewrite, przepisywanie adresów URL i wyjaśnienie „ładnych linków”

142

„Ładne linki” to często poszukiwany temat, ale rzadko jest on w pełni wyjaśniony. mod_rewrite to jeden ze sposobów tworzenia „ładnych linków”, ale jest złożony, a jego składnia jest bardzo zwięzła, trudna do zrozumienia, a dokumentacja zakłada pewien poziom biegłości w HTTP. Czy ktoś może w prosty sposób wyjaśnić, jak działają „ładne linki” i jak można użyć mod_rewrite do ich tworzenia?

Inne popularne nazwy, aliasy, terminy dotyczące czystych adresów URL: adresy URL zgodne z REST, adresy URL przyjazne dla użytkownika, adresy URL przyjazne dla SEO, adresy URL typu slugging i MVC (prawdopodobnie błędna nazwa)

zamrozić
źródło
2
Slug lub Slugging to kolejny popularny alias / termin dla ładnych adresów URL.
Mike B
2
@Mike W pewnym sensie, ale slugi są często częścią ładnych adresów URL. Slug jest dość specyficzny, gdy na przykład nagłówek artykułu jest zamieniany na format przyjazny dla adresu URL, który następnie działa jako identyfikator tego artykułu. Tak reference-mod-rewrite-url-rewriting-explainedsamo jak ślimak, /questions/20563772/reference-mod-rewrite-url-rewriting-explainedto ładny adres URL.
deceze
2
Myślę, że tagi .htaccessi mod-rewritepowinny zostać zaktualizowane, aby zawierały link do tego pytania, ponieważ obejmuje on wiele z tego, co jest zadawane regularnie. Myśli?
Mike Rockétt

Odpowiedzi:

110

Aby zrozumieć, czym jest mod_rewrite, musisz najpierw zrozumieć, jak działa serwer WWW. Serwer WWW odpowiada na żądania HTTP . Żądanie HTTP na najbardziej podstawowym poziomie wygląda następująco:

GET /foo/bar.html HTTP/1.1

Jest to proste żądanie przeglądarki skierowane do serwera WWW, które żąda od niego adresu URL /foo/bar.html . Ważne jest, aby podkreślić, że nie żąda pliku , żąda tylko jakiegoś dowolnego adresu URL. Żądanie może również wyglądać tak:

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

Jest to równie ważne żądanie adresu URL, a bardziej oczywiste nie ma nic wspólnego z plikami.

Serwer WWW to aplikacja nasłuchująca na porcie, przyjmująca żądania HTTP przychodzące na tym porcie i zwracająca odpowiedź. Serwer WWW może odpowiadać na każde żądanie w dowolny sposób, jaki uzna za stosowny / w dowolny sposób, w jaki go skonfigurowałeś. Ta odpowiedź nie jest plikiem, jest odpowiedzią HTTP, która może, ale nie musi mieć nic wspólnego z fizycznymi plikami na dowolnym dysku. Serwer WWW nie musi być serwerem Apache, jest wiele innych serwerów WWW, które są po prostu programami działającymi trwale i podłączonymi do portu odpowiadającego na żądania HTTP. Możesz sam napisać. Ten akapit miał na celu oderwanie Cię od poglądu, że adresy URL są bezpośrednio równe plikom, co jest naprawdę ważne do zrozumienia. :)

Domyślna konfiguracja większości serwerów WWW polega na wyszukiwaniu pliku zgodnego z adresem URL na dysku twardym. Jeśli katalog główny serwera jest ustawiony na, powiedzmy, /var/wwwmoże sprawdzić, czy plik /var/www/foo/bar.htmlistnieje i jeśli tak jest, może go obsłużyć. Jeśli plik kończy się na „.php”, wywoła interpreter PHP, a następnie zwróci wynik. Całe to powiązanie jest w pełni konfigurowalne; plik nie musi kończyć się rozszerzeniem „.php”, aby serwer WWW mógł go uruchomić przez interpreter PHP, a adres URL nie musi pasować do żadnego konkretnego pliku na dysku, aby coś się wydarzyło.

mod_rewrite to sposób na przepisanie wewnętrznej obsługi żądań. Gdy serwer WWW otrzyma żądanie adresu URL /foo/bar, możesz przepisać ten adres URL na coś innego, zanim serwer WWW będzie szukał pliku na dysku, aby go dopasować. Prosty przykład:

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

Ta reguła mówi, że za każdym razem , gdy żądanie pasuje do „/ foo / bar”, przepisz je do „/ foo / baz”. Żądanie będzie wówczas obsługiwane tak, jakby /foo/bazzamiast tego zostało poproszone. Można to wykorzystać do różnych efektów, na przykład:

RewriteRule (.*) $1.html

Ta reguła dopasowuje cokolwiek ( .*) i przechwytuje to ( (..)), a następnie przepisuje, aby dodać „.html”. Innymi słowy, jeśli /foo/barżądany adres URL byłby obsługiwany tak, jakby /foo/bar.htmlzostał poproszony. Widzieć http://regular-expressions.info, aby uzyskać więcej informacji na temat dopasowywania, przechwytywania i zastępowania wyrażeń regularnych.

Inną często spotykaną zasadą jest:

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

To znowu dopasowuje wszystko i przepisuje go do pliku index.php z pierwotnie żądanym adresem URL dołączonym do urlparametru zapytania. Oznacza to, że dla wszystkich przychodzących żądań wykonywany jest plik index.php i ten plik będzie miał dostęp do pierwotnego żądania w$_GET['url'] , dzięki czemu może zrobić z nim wszystko, co zechce.

Przede wszystkim umieszczasz te reguły przepisywania w pliku konfiguracyjnym serwera WWW . Apache pozwala także * umieścić je w pliku o nazwie .htaccessw katalogu głównym twojego dokumentu (tj. Obok plików .php).

* Jeśli pozwala na to podstawowy plik konfiguracyjny Apache; jest opcjonalny, ale często jest włączony.

Co mod_rewrite czy nie zrobić

mod_rewrite nie sprawia, że ​​wszystkie adresy URL są „ładne”. To powszechne nieporozumienie. Jeśli masz to łącze w swojej witrynie internetowej:

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

mod_rewrite nie może nic zrobić, aby było to ładne. Aby był to ładny link, musisz:

  1. Zmień link na ładny link:

    <a href="https://stackoverflow.com/my/pretty/link">
    
  2. Użyj mod_rewrite na serwerze, aby obsłużyć żądanie do adresu URL /my/pretty/linkza pomocą jednej z metod opisanych powyżej.

(Można go użyć mod_substitutew połączeniu do przekształcenia wychodzących stron HTML i zawartych w nich linków. Chociaż jest to zwykle więcej wysiłku niż tylko aktualizacja zasobów HTML.)

Jest wiele możliwości mod_rewrite i bardzo złożone reguły dopasowywania, które możesz utworzyć, w tym łączenie kilku poprawek, przesyłanie żądań do zupełnie innej usługi lub maszyny, zwracanie określonych kodów stanu HTTP jako odpowiedzi, przekierowywanie żądań itp. Jest bardzo potężny i może być używany do świetnie, jeśli rozumiesz podstawowy mechanizm odpowiedzi na żądanie HTTP. Tak nie jest automatycznie dokonać linki ładna.

Zobacz oficjalną dokumentację dla wszystkich możliwych flag i opcji.

zamrozić
źródło
6
Może wspomnij o dyrektywie FallbackResource wprowadzonej w wersji 2.2.16 jako preferowanym sposobie przepisywania do dispatchera.
Darsstar
78

Aby rozwinąć odpowiedź deceze , chciałem podać kilka przykładów i wyjaśnić inne funkcje mod_rewrite.

RewriteEngine OnWe wszystkich poniższych przykładach założono, że .htaccessplik został już uwzględniony .

Przepis na przykład

Weźmy ten przykład:

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

Reguła jest podzielona na 4 sekcje:

  1. RewriteRule - uruchamia regułę przepisywania
  2. ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - Nazywa się to wzorcem, ale będę nazywać go lewą stroną reguły - z czego chcesz przepisać
  3. blog/index.php?id=$1&title=$2 - nazwał substytucję lub prawą stronę przepisywania reguły - do czego chcesz przepisać
  4. [NC,L,QSA] są flagami dla reguły przepisywania, oddzielonymi przecinkiem, co wyjaśnię później

Powyższe przepisanie pozwoliłoby na link do czegoś podobnego /blog/1/foo/i faktycznie by się załadowało /blog/index.php?id=1&title=foo.

Lewa strona reguły

  • ^wskazuje początek nazwy strony - więc zostanie przepisana, example.com/blog/...ale nieexample.com/foo/blog/...
  • Każdy zestaw (…) nawiasów reprezentuje wyrażenie regularne, które możemy przechwycić jako zmienną po prawej stronie reguły. W tym przykładzie:
    • Pierwszy zestaw nawiasów - ([0-9]+) - odpowiada ciągowi o długości co najmniej 1 znaku i zawierającym tylko wartości liczbowe (tj. 0-9). Można się do tego odwołać za pomocą$1 po prawej stronie reguły
    • Drugi zestaw nawiasów pasuje do ciągu o długości co najmniej 1 znaku, zawierającego tylko znaki alfanumeryczne (AZ, az lub 0-9) lub -lub +(uwaga +jest poprzedzona ukośnikiem odwrotnym, ponieważ bez zmiany jego znaczenia zostanie wykonana jako wyrażenie regularne znak powtórzenia ). Można się do tego odwołać za pomocą$2 po prawej stronie reguły
  • ? oznacza, że ​​poprzedni znak jest opcjonalny, więc w tym przypadku oba /blog/1/foo/ i /blog/1/fooprzepisałyby w to samo miejsce
  • $ wskazuje, że jest to koniec ciągu, który chcemy dopasować

Flagi

Są to opcje, które są dodawane w nawiasach kwadratowych na końcu reguły przepisywania, aby określić pewne warunki. Ponownie, istnieje wiele różnych flag, o których możesz przeczytać w dokumentacji , ale omówię niektóre z bardziej powszechnych flag:

NC

Flaga no case oznacza, że ​​w regule przepisywania nie jest rozróżniana wielkość liter, więc w przypadku powyższej reguły przykładowej oznaczałoby to, że zarówno /blog/1/foo/i /BLOG/1/foo/(lub jakakolwiek ich odmiana) zostaną dopasowane.

L

Ostatnia flaga wskazuje, że jest to ostatnia reguła, która powinna zostać przetworzona. Oznacza to, że wtedy i tylko wtedy, gdy ta reguła będzie pasować, żadne dalsze reguły nie będą oceniane w bieżącym przebiegu przetwarzania przepisywania. Jeśli reguła nie pasuje, wszystkie inne reguły zostaną wypróbowane w zwykłej kolejności. Jeśli nie ustawisz Lflagi, wszystkie poniższe reguły zostaną później zastosowane do przepisanego adresu URL.

END

Od Apache 2.4 możesz także używać [END]flagi. Pasująca reguła całkowicie zakończy dalsze przetwarzanie aliasów / przepisywania. (Podczas gdy [L]flaga może często wywołać drugą rundę, na przykład podczas przepisywania do lub z podkatalogów).

QSA

Flaga dołączania ciągu zapytania pozwala nam przekazać dodatkowe zmienne do określonego adresu URL, które zostaną dodane do oryginalnych parametrów get. W naszym przykładzie oznacza to, że coś takiego /blog/1/foo/?comments=15zostanie załadowane/blog/index.php?id=1&title=foo&comments=15

R

Ta flaga nie jest tą, której użyłem w powyższym przykładzie, ale uważam, że warto o niej wspomnieć. Pozwala to na określenie przekierowania http z opcją dołączenia kodu statusu (np R=301.). Na przykład, jeśli chcesz wykonać przekierowanie 301 na / myblog / to / blog /, po prostu napisz regułę mniej więcej taką:

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

Przepisz warunki

Warunki ponownego zapisu sprawiają, że przepisywanie jest jeszcze bardziej wydajne, umożliwiając określenie przepisywania dla bardziej konkretnych sytuacji. Istnieje wiele warunków, o których możesz przeczytać w dokumentacji , ale przytoczę kilka typowych przykładów i wyjaśnię je:

# 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]

Jest to bardzo powszechna praktyka polegająca na dodaniu Twojej domeny przedrostkiem www.(jeśli jeszcze jej tam nie ma) i wykonaniu przekierowania 301. Na przykład załadowanie http://example.com/blog/go przekieruje Cię dohttp://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]

Jest to nieco mniej powszechne, ale jest dobrym przykładem reguły, która nie jest wykonywana, jeśli nazwa pliku to katalog lub plik istniejący na serwerze.

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] wykona przepisanie tylko dla plików z rozszerzeniem jpg, jpeg, gif lub png (bez rozróżniania wielkości liter).
  • %{REQUEST_FILENAME} !-f sprawdzi, czy plik istnieje na bieżącym serwerze i wykona przepisanie tylko wtedy, gdy tak się nie stanie
  • %{REQUEST_FILENAME} !-d sprawdzi, czy plik istnieje na bieżącym serwerze i wykona przepisanie tylko wtedy, gdy tak się nie stanie
  • Przepisanie podejmie próbę załadowania tego samego pliku w innej domenie
Nacięcie
źródło
39

Bibliografia

Stack Overflow ma wiele innych świetnych zasobów na początek:

A nawet przyjazne dla nowicjuszy przeglądy wyrażeń regularnych:

Często używane symbole zastępcze

  • .*dopasowuje wszystko, nawet pusty ciąg. Nie chcesz używać tego wzorca wszędzie, ale często w ostatniej regule awaryjnej.
  • [^/]+jest częściej używany do segmentów ścieżki. Pasuje do wszystkiego oprócz ukośnika.
  • \d+ pasuje tylko do ciągów numerycznych.
  • \w+dopasowuje znaki alfanumeryczne. Jest to w zasadzie skrót [A-Za-z0-9_].
  • [\w\-]+dla segmentów ścieżki w stylu „slug”, używając liter, cyfr, myślników - i _
  • [\w\-.,]+dodaje kropki i przecinki. Wolę uciekający \-myślnik w […]klasach.
  • \.oznacza dosłowny okres. W przeciwnym razie .poza […]jest symbolem zastępczym dla dowolnego symbolu.

Każdy z tych symboli zastępczych jest zwykle zawijany w (…)nawiasach jako grupa przechwytywania. A cały wzór często w ^………$markerach początek + koniec. Cytowanie „wzorców” jest opcjonalne.

RewriteRules

Poniższe przykłady są skoncentrowane na PHP i są nieco bardziej przyrostowe, łatwiejsze do dostosowania w podobnych przypadkach. To tylko podsumowania, często zawierają linki do większej liczby odmian lub szczegółowych pytań i odpowiedzi.

  • Mapowanie statyczne
    /contact,/about

    Skrócenie kilku nazw stron do wewnętrznych schematów plików jest najprostsze:

     RewriteRule ^contact$  templ/contact.html
     RewriteRule ^about$    about.php
    
  • Identyfikatory liczbowe
    /object/123

    Wprowadzenie skrótów, takich jak http://example.com/article/531do istniejących skryptów PHP, jest również łatwe. Numeryczny symbol zastępczy można po prostu odwzorować na $_GETparametr:

     RewriteRule ^article/(\d+)$    article-show.php?id=$1
     #                      └───────────────────────────┘
    
  • Symbole zastępcze w stylu informacji o pracy
    /article/with-some-title-slug

    Możesz łatwo rozszerzyć tę regułę, aby umożliwić stosowanie /article/title-stringsymboli zastępczych:

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

    Zauważ, że twój skrypt musi być zdolny (lub dostosowany) do mapowania tych tytułów z powrotem na identyfikatory baz danych. Samo RewriteRules nie może tworzyć ani odgadywać informacji z powietrza.

  • Ślimaki z prefiksami numerycznymi
    /readable/123-plus-title

    Dlatego często zobaczysz mieszane /article/529-title-slugścieżki używane w praktyce:

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

    Teraz możesz po prostu pominąć przekazywanie i title=$2tak, ponieważ twój skrypt i tak będzie zazwyczaj polegał na identyfikatorze bazy danych. -title-slugStała arbitralne URL dekoracji.

  • Jednolitość z alternatywnymi listami
    /foo/… /bar/… /baz/…

    Jeśli masz podobne reguły dla wielu wirtualnych ścieżek stron, możesz je dopasować i kompaktować za pomocą |alternatywnych list. I znowu po prostu ponownie przypisz je do wewnętrznych parametrów GET:

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

    Możesz podzielić je na pojedyncze, RewriteRulejeśli stanie się to zbyt skomplikowane.

  • Wysyłanie powiązanych adresów URL do różnych backendów
    /date/SWITCH/backend

    Bardziej praktycznym zastosowaniem list alternatywnych jest mapowanie ścieżek żądań do różnych skryptów. Na przykład, aby zapewnić jednolite adresy URL dla starszej i nowszej aplikacji internetowej na podstawie dat:

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

    To po prostu ponownie mapuje posty z lat 2009-2011 do jednego skryptu, a wszystkie pozostałe lata niejawnie do innego programu obsługi. Zwróć uwagę na bardziej szczegółową regułę, która pojawia się jako pierwsza . Każdy skrypt może używać innych parametrów GET.

  • Inne ograniczniki niż tylko /ukośniki ścieżek
    /user-123-name

    Najczęściej widzisz RewriteRules do symulacji struktury katalogów wirtualnych. Ale nie jesteś zmuszony być nietwórczy. Możesz również użyć -łączników do segmentacji lub struktury.

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

    W przypadku również wspólnego /wiki:section:Page_Nameschematu:

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

    Czasami dobrze jest naprzemiennie /używać -delimiterów, a :nawet .według tej samej reguły. Lub ponownie użyj dwóch RewriteRules, aby zmapować warianty na różne skrypty.

  • Opcjonalny końcowy /ukośnik
    /dir=/dir/

    Decydując się na ścieżki w stylu katalogu, możesz sprawić, by były osiągalne z wersją końcową i bez niej /

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

    Teraz obsługuje zarówno http://example.com/blog/123i /blog/123/. A /?$podejście jest łatwe do dołączania na jakikolwiek inny RewriteRule.

  • Elastyczne segmenty dla ścieżek wirtualnych
    .*/.*/.*/.*

    Większość reguł, z którymi się spotkasz, mapuje ograniczony zestaw /…/segmentów ścieżki zasobów do poszczególnych parametrów GET. Jednak niektóre skrypty obsługują zmienną liczbę opcji . Silnik wyrażeń regularnych Apache nie pozwala na opcjonalizację dowolnej ich liczby. Ale możesz łatwo samodzielnie rozszerzyć go na blok reguł:

     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
     #              └─────┴─────┴───────────────────┴────┴────┘
    

    Jeśli potrzebujesz maksymalnie pięciu segmentów ścieżki, skopiuj ten schemat do pięciu reguł. Możesz oczywiście użyć bardziej konkretnego [^/]+symbolu zastępczego. Tutaj kolejność nie jest tak ważna, ponieważ żadna z nich nie pokrywa się. Zatem posiadanie najpierw najczęściej używanych ścieżek jest w porządku.

    Alternatywnie możesz użyć parametrów tablicowych PHP za pośrednictwem ?p[]=$1&p[]=$2&p[]=3ciągu zapytania tutaj - jeśli twój skrypt po prostu woli je wstępnie podzielić. (Chociaż częściej używa się reguły catch-all i pozwala skryptowi samodzielnie rozwinąć segmenty poza REQUEST_URI).

    Zobacz też: Jak przekształcić segmenty ścieżki adresu URL w pary klucz-wartość ciągu zapytania?

  • Opcjonalne segmenty
    prefix/opt?/.*

    Typową odmianą jest stosowanie opcjonalnych przedrostków w regule. Zwykle ma to sens, jeśli masz statyczne ciągi lub bardziej ograniczone symbole zastępcze wokół:

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

    Teraz bardziej złożony wzór (?:/([^/])+)?po prostu zawija grupę nieprzechwytywaną (?:…) i sprawia, że ​​jest opcjonalna )?. Zawarty symbol zastępczy ([^/]+)byłby wzorcem zastępowania $2, ale powinien być pusty, jeśli nie ma środkowej /…/ścieżki.

  • Uchwyć resztę
    /prefix/123-capture/…/*/…whatever…

    Jak powiedziano wcześniej, często nie chcesz zbyt ogólnych wzorców przepisywania. Warto jednak łączyć statyczne i szczegółowe porównania z .*czasami.

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

    Spowoduje to opcjonalizację wszelkich /…/…/…końcowych segmentów ścieżki. Co wtedy oczywiście wymaga skryptu obsługującego, aby je rozdzielić, i same parametry wyodrębnia zmienne (co jest tym, co robią frameworki „MVC” sieci Web ).

  • Końcowe „rozszerzenia” plików
    /old/path.HTML

    Adresy URL tak naprawdę nie mają rozszerzeń plików. O to właśnie chodzi w tym całym odwołaniu (= adresy URL są wirtualnymi lokalizatorami, niekoniecznie bezpośrednim obrazem systemu plików). Jednak jeśli miał 1: 1 mapowanie plików przed, może spreparować prostszych zasad:

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

    Inne typowe zastosowania to ponowne mapowanie przestarzałych .htmlścieżek do nowszych .phpprogramów obsługi lub po prostu tworzenie aliasów nazw katalogów tylko dla pojedynczych (rzeczywistych / rzeczywistych) plików.

  • Ping-Pong (przekierowuje i przepisuje jednocześnie)
    /ugly.html← →/pretty

    Więc w pewnym momencie przepisujesz swoje strony HTML, aby zawierały tylko ładne linki, zgodnie z deceze . W międzyczasie nadal będziesz otrzymywać żądania dotyczące starych ścieżek, czasem nawet z zakładek. Aby obejść ten problem , możesz ping-pong przeglądarek wyświetlić / ustanowić nowe adresy URL.

    Ta powszechna sztuczka polega na wysyłaniu przekierowania 30x / Location, gdy przychodzący adres URL jest zgodny z przestarzałym / brzydkim schematem nazewnictwa. Przeglądarki ponownie zażądają nowego / ładnego adresu URL, który jest następnie przepisywany (tylko wewnętrznie) do oryginalnej lub nowej lokalizacji.

     # 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]
    

    Zwróć uwagę, że w tym przykładzie zastosowano [END]zamiast [L]bezpiecznej zmiany. W przypadku starszych wersji Apache 2.2 możesz użyć innych obejść, oprócz zmiany przypisania parametrów ciągu zapytania, na przykład: Przekieruj brzydki na ładny adres URL, ponownie odwróć do brzydkiej ścieżki, bez nieskończonych pętli

  • Spacje we wzorach
    /this+that+

    Nie wygląda to zbyt ładnie na pasku adresu przeglądarki, ale możesz używać spacji w adresach URL. W przypadku wzorców przepisywania należy używać \␣spacji ze znakami ucieczki odwrotnym ukośnikiem . W przeciwnym razie po prostu "zacytuj cały wzorzec lub podstawienie:

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

    Klienci serializują adresy URL ze spacjami +lub %20dla nich. Jednak w RewriteRules są one interpretowane za pomocą literalnych znaków dla wszystkich względnych segmentów ścieżki.

Częste duplikaty:

Powszechne .htaccesspułapki

Teraz weź to z przymrużeniem oka. Nie każdą radę można uogólnić na wszystkie konteksty. To tylko proste podsumowanie dobrze znanych i kilku nieoczywistych przeszkód:

  • Włącz mod_rewritei.htaccess

    Aby faktycznie używać RewriteRules w plikach konfiguracyjnych dla poszczególnych katalogów, musisz:

    • Sprawdź, czy serwer jest AllowOverride Allwłączony . W przeciwnym razie .htaccessdyrektywy dla poszczególnych katalogów zostaną zignorowane, a RewriteRules nie będzie działać.

    • Oczywiście masz mod_rewritewłączone w httpd.confsekcji modułów.

    • Do każdej listy reguł dołącz ciąg RewriteEngine Onjeszcze. Chociaż mod_rewrite jest niejawnie aktywny w sekcjach <VirtualHost>i <Directory>, .htaccesspliki dla poszczególnych katalogów wymagają indywidualnego przywołania.

  • Główny ukośnik ^/nie pasuje

    Nie powinieneś normalnie rozpoczynać .htaccesswzorców RewriteRule ^/:

     RewriteRule ^/article/\d+$  …
                  ↑
    

    Jest to często widoczne w starych samouczkach. I było to poprawne dla starożytnych wersji Apache 1.x. Obecnie ścieżki żądań są dogodnie w pełni względne względem katalogu w .htaccessRewriteRules. Po prostu zostaw /wyprowadzenie.

    · Pamiętaj jednak, że wiodący ukośnik jest nadal poprawny w <VirtualHost>sekcjach. Dlatego często widzisz, że jest to ^/?opcja dla parytetu reguł.
    · Lub jeśli RewriteCond %{REQUEST_URI}użyjesz, nadal będziesz pasował do wiodącej /.
    · Zobacz także Webmaster.SE: Kiedy wiodący ukośnik (/) jest potrzebny we wzorcach mod_rewrite?

  • <IfModule *> opakowania zniknęły!

    Prawdopodobnie widziałeś to na wielu przykładach:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • Ma to sens w <VirtualHost>sekcjach - jeśli zostało połączone z inną opcją awaryjną, taką jak ScriptAliasMatch. (Ale nikt tego nigdy nie robi).
    • Jest często rozpowszechniany w przypadku domyślnych .htaccesszestawów reguł z wieloma projektami typu open source. Jest to po prostu przeznaczone do zastępowania i zachowuje domyślne działanie „brzydkich” adresów URL.

    Jednak zwykle nie chcesz tego w swoich własnych .htaccessplikach.

    • Po pierwsze, mod_rewrite nie wyłącza się losowo. (Gdyby tak było, miałbyś większe problemy).
    • Gdyby to naprawdę było wyłączone, Twoje RewriteRules i tak nie działałyby.
    • Ma to na celu zapobieganie 500błędom HTTP . To, co zwykle osiąga, to zaszczycanie użytkowników 404błędami HTTP . (Nie o wiele bardziej przyjazne dla użytkownika, jeśli się nad tym zastanowić).
    • W praktyce po prostu pomija bardziej przydatne wpisy dziennika lub wiadomości e-mail z powiadomieniami serwera. Nikt nie byłby mądrzejszy, dlaczego Twoje Reguły Przepisywania nigdy nie działają.

    To, co wydaje się kuszące jako uogólnione zabezpieczenie, często okazuje się przeszkodą w praktyce.

  • Nie używaj, RewriteBasejeśli nie jest to potrzebne

    Wiele przykładów kopiuj + wklej zawiera RewriteBase /dyrektywę. Co i tak jest domyślną wartością domyślną. Więc tak naprawdę tego nie potrzebujesz. Jest to obejście dla wymyślnych schematów przepisywania VirtualHost i błędnych ścieżek DOCUMENT_ROOT dla niektórych współdzielonych hostów.

    Warto używać z poszczególnymi aplikacjami internetowymi w głębszych podkatalogach. W takich przypadkach może skrócić wzorce RewriteRule. Ogólnie najlepiej jest preferować względne specyfikatory ścieżek w zestawach reguł dla poszczególnych katalogów.

    Zobacz także Jak działa RewriteBase w .htaccess

  • Wyłącz, MultiViewsgdy ścieżki wirtualne nakładają się

    Przepisywanie adresów URL jest używane głównie do obsługi wirtualnych ścieżek przychodzących. Zwykle wystarczy jeden dyspozytora skrypt ( index.php) lub kilka pojedynczych teleskopowe ( articles.php, blog.php, wiki.php, ...). Ta ostatnia może kolidować z podobnymi wirtualnymi ścieżkami RewriteRule.

    Na /article/123przykład żądanie może być mapowane niejawnie do article.phpz /123PATH_INFO. Wtedy albo musiałbyś strzec swoich reguł za pomocą zwykłego RewriteCond !-f+ !-di / lub wyłączyć obsługę PATH_INFO, albo po prostu wyłączyć Options -MultiViews.

    Co nie znaczy, że zawsze musisz . Negocjacje treści to po prostu automatyzm wirtualnych zasobów.

  • Kolejność jest ważna

    Zobacz wszystko, co kiedykolwiek chciałeś wiedzieć o mod_rewrite, jeśli jeszcze tego nie zrobiłeś. Łączenie wielu reguł RewriteRules często prowadzi do interakcji. To nie jest coś, co zwykle należy zapobiegać za [L]flagę, ale schemat, który przyjmiesz po zapoznaniu się. Państwo może re-re-re zapisu wirtualnych ścieżek z jednej reguły do drugiego, aż osiągnie rzeczywistą obsługi docelowej.

    Mimo to często chciałbyś mieć najbardziej szczegółowe reguły (ustalone /forum/…wzorce ciągów lub bardziej restrykcyjne symbole zastępcze [^/.]+) we wczesnych regułach. Ogólne reguły slurp-all ( .*) lepiej zostawić późniejszym . (Wyjątkiem jest RewriteCond -f/-dstrażnik jako główny blok).

  • Arkusze stylów i obrazy przestają działać

    Wprowadzenie struktur katalogów wirtualnych ma /blog/article/123wpływ na względne odwołania do zasobów w języku HTML (na przykład <img src=mouse.png>). Które można rozwiązać:

    • Tylko przy użyciu odwołań bezwzględnych do serwera href="https://stackoverflow.com/old.html"lubsrc="/logo.png"
    • Często po prostu przez dodanie <base href="https://stackoverflow.com/index">do <head>sekcji HTML . To niejawnie zmienia względne odniesienia do tego, czym były wcześniej.

    Możesz alternatywnie stworzyć dalsze Reguły RewriteRules, aby zmienić powiązanie .csslub .pngścieżki do ich pierwotnych lokalizacji. Ale to jest zarówno niepotrzebne, jak i powoduje dodatkowe przekierowania i utrudnia buforowanie.

    Zobacz też: CSS, JS i obrazy nie wyświetlają się z ładnym adresem URL

  • RewriteConds tylko maskuje jedną RewriteRule

    Częstym błędem jest to, że RewriteCond blokuje wiele Reguł RewriteRules (ponieważ są one wizualnie ułożone razem):

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

    Które nie jest domyślnie. Możesz je połączyć za pomocą [S=2]flagi. W przeciwnym razie będziesz musiał je powtórzyć. Chociaż czasami można utworzyć „odwróconą” regułę podstawową, aby [END] przerabiać przetwarzanie wcześniej.

  • QUERY_STRING zwolnione z RewriteRules

    Nie możesz dopasować RewriteRule index.php\?x=y, ponieważ mod_rewrite porównuje domyślnie tylko ze ścieżkami względnymi. Możesz jednak dopasować je osobno poprzez:

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

    Zobacz także Jak mogę dopasować zmienne ciągu zapytania do mod_rewrite?

  • .htaccess vs. <VirtualHost>

    Jeśli używasz RewriteRules w pliku konfiguracyjnym dla każdego katalogu, martwienie się o wydajność wyrażeń regularnych jest bezcelowe. Apache zachowuje skompilowane wzorce PCRE dłużej niż proces PHP ze wspólną strukturą routingu. W przypadku witryn o dużym natężeniu ruchu należy jednak rozważyć przeniesienie zestawów reguł do konfiguracji serwera vhost, gdy zostaną przetestowane w walce.

    W takim przypadku preferuj opcjonalny ^/?prefiks separatora katalogów. Pozwala to na swobodne przenoszenie RewriteRules między plikami konfiguracyjnymi PerDir i serwera.

  • Zawsze, gdy coś nie działa

    Nie denerwować.

    • Porównaj access.logierror.log

      Często można dowiedzieć się, w jaki sposób reguła RewriteRule zachowuje się nieprawidłowo, patrząc na Twoje error.logi access.log. Skoreluj czasy dostępu, aby zobaczyć, która ścieżka żądań pierwotnie nadeszła i której ścieżki / pliku Apache nie mógł rozwiązać (błąd 404/500).

      To nie mówi ci, która RewriteRule jest winowajcą. Ale niedostępne ścieżki końcowe, takie jak, /docroot/21-.itle?index.phpmogą zdradzić, gdzie można dalej zbadać. W przeciwnym razie wyłącz reguły, dopóki nie uzyskasz przewidywalnych ścieżek.

    • Włącz RewriteLog

      Zobacz dokumentację Apache RewriteLog . Do debugowania możesz włączyć to w sekcjach vhost:

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

      Daje to szczegółowe podsumowanie tego, jak ścieżki żądań przychodzących są modyfikowane przez każdą regułę:

      [..] 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'
      

      Co pomaga zawęzić zbyt ogólne reguły i wpadki w wyrażeniach regularnych.

      Zobacz też:
      · .htaccess nie działa (mod_rewrite)
      · Wskazówki dotyczące debugowania .htaccess zasady przepisywania

    • Zanim zadasz własne pytanie

      Jak być może wiesz, przepełnienie stosu bardzo dobrze nadaje się do zadawania pytań na temat mod_rewrite. Niech będą na temat , uwzględniając wcześniejsze badania i próby (unikaj zbędnych odpowiedzi), zademonstruj podstawowe zrozumienie i:

      • Uwzględnij pełne przykłady wejściowych adresów URL, błędnie przepisanych ścieżek docelowych, rzeczywistą strukturę katalogów.
      • Kompletny zestaw RewriteRule, ale także wyodrębnij przypuszczalnie wadliwy.
      • Wersje Apache i PHP, typ systemu operacyjnego, system plików, DOCUMENT_ROOT i $_SERVERśrodowisko PHP, jeśli chodzi o niezgodność parametrów.
      • Fragment twojego access.logi error.logweryfikacja, na co rozwiązały się istniejące przepisy. Jeszcze lepiej, rewrite.logpodsumowanie.

      Zapewnia to szybsze i dokładniejsze odpowiedzi oraz sprawia, że ​​są one bardziej przydatne dla innych.

  • Skomentuj swój .htaccess

    Jeśli kopiujesz skądś przykłady, pamiętaj o dołączeniu pliku # comment and origin link. Chociaż pomijanie atrybucji jest tylko złymi manierami, często naprawdę szkodzi późniejszej konserwacji. Dokumentuj dowolny kod lub źródło samouczka. W szczególności, gdy nie masz odwrotu, tym bardziej powinieneś być zainteresowany traktowaniem ich jak magicznych czarnych skrzynek.

  • To nie są adresy URL „SEO”

    Zastrzeżenie: tylko irytujące zwierzę. Często słyszysz ładne schematy przepisywania adresów URL, nazywane linkami „SEO” lub czymś podobnym. Chociaż jest to przydatne przy wyszukiwaniu przykładów w Google, jest to przestarzała myląca nazwa.

    Żaden ze współczesnych wyszukiwarek są naprawdę przeszkadzał .htmli .phpw segmentach Ścieżka lub ?id=123ciągów zapytań dotyczących tej sprawy. Wyszukiwarki starych, takich jak AltaVista, zrobił Unikaj indeksowania stron internetowych z potencjalnie niejednoznaczną ścieżek dostępu. Nowoczesne roboty często pragną nawet zasobów głębokiej sieci.

    Koncepcyjnie należy używać „ładnych” adresów URL, aby uczynić witryny przyjaznymi dla użytkownika .

    1. Posiadanie czytelnych i oczywistych schematów zasobów.
    2. Zapewnienie, że adresy URL są długotrwałe (tzw. Permalinki ).
    3. Zapewnienie wykrywalności poprzez /common/tree/nesting.

    Jednak nie rezygnuj z wyjątkowych wymagań dla konformizmu.

Przybory

Istnieją różne narzędzia online do generowania reguł RewriteRules dla większości adresów URL z parametrami GET:

Przeważnie wyświetla tylko [^/]+ogólne symbole zastępcze, ale prawdopodobnie wystarcza w przypadku trywialnych witryn.

mario
źródło
Nadal wymaga trochę przepisania, więcej linków, a wiele podtytułów jest nieco nieprzyjemnych. Niektóre odpowiedzi pokrywają się z innymi odpowiedziami, więc być może można je ograniczyć. Chodzi jednak głównie o przykłady wizualne i listę typowych problemów.
mario
3
Dawno nie widziałem takiej piękna odpowiedzi! Moje oczy świecą, kiedy to czytam. Nie przestawajcie publikować takich odpowiedzi :)
Rizier123
1
Doskonały post. Bardzo szybko zrozumiałem podstawowe pojęcia mod_rewrite!
breez
6

Alternatywy dla mod_rewrite

Wiele podstawowych schematów wirtualnych adresów URL można osiągnąć bez korzystania z RewriteRules. Apache pozwala na wywoływanie skryptów PHP bez .phprozszerzenia iz wirtualnym PATH_INFOargumentem.

  1. Użyj PATH_INFO , Luke

    Obecnie AcceptPathInfo Onjest często domyślnie włączona. Co w zasadzie pozwala .phpi innym adresom URL zasobów na przenoszenie wirtualnego argumentu:

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

    Teraz /virtual/pathpojawia się to w PHP, $_SERVER["PATH_INFO"]gdzie możesz obsłużyć dodatkowe argumenty, jak chcesz.

    Nie jest to wygodne, jak o Apache oddzielne segmenty ścieżki danych wejściowych $1, $2, $3i przekazywać je w różnych $_GETzmiennych do php. Po prostu emuluje „ładne adresy URL” przy mniejszym wysiłku konfiguracyjnym.

  2. Włącz MultiViews, aby ukryć .phprozszerzenie

    Najprostszą opcją unikania .php„rozszerzeń plików” w adresach URL jest włączenie:

    Options +MultiViews
    

    Powoduje to article.phpwłączenie funkcji wyboru Apache dla żądań HTTP /articleze względu na pasującą nazwę basenową. Działa to dobrze w połączeniu z wyżej wspomnianą funkcją PATH_INFO. Możesz więc po prostu użyć adresów URL, takich jak http://example.com/article/virtual/title. Ma to sens, jeśli masz tradycyjną aplikację internetową z wieloma punktami / skryptami wywołań PHP.

    Pamiętaj jednak, że MultiViews ma inny / szerszy cel. Wiąże się to z bardzo niewielkim spadkiem wydajności, ponieważ Apache zawsze szuka innych plików z pasującymi nazwami basenowymi. To rzeczywiście przeznaczona dla Content-negocjacja , więc przeglądarek otrzymać najlepszą alternatywę wśród dostępnych zasobów (takich jak article.en.php, article.fr.php, article.jp.mp4).

  3. SetType lub SetHandler dla .phpskryptów bez rozszerzeń

    Bardziej ukierunkowanym podejściem do uniknięcia przenoszenia .phpsufiksów w adresach URL jest skonfigurowanie programu obsługi PHP dla innych schematów plików. Najprostszą opcją jest zastąpienie domyślnego typu MIME / obsługi poprzez .htaccess:

    DefaultType application/x-httpd-php
    

    W ten sposób możesz po prostu zmienić nazwę swojego article.phpskryptu na po prostu article(bez rozszerzenia), ale nadal przetwarzać go jako skrypt PHP.

    Może to mieć pewne konsekwencje dla bezpieczeństwa i wydajności, ponieważ wszystkie pliki bez rozszerzeń będą teraz przesyłane potokiem przez PHP. Dlatego możesz alternatywnie ustawić to zachowanie tylko dla pojedynczych plików:

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

    Jest to w pewnym stopniu zależne od konfiguracji serwera i używanego PHP SAPI. Typowe alternatywy obejmują ForceType application/x-httpd-phplub AddHandler php5-script.

    Ponownie zwróć uwagę, że takie ustawienia są przenoszone z jednego .htaccessdo podfolderów. Zawsze należy wyłączyć wykonywanie skryptów ( SetHandler Nonei / Options -Execlub php_flag engine offitp.) Dla zasobów statycznych, przesyłania / katalogów itp.

  4. Inne schematy przepisywania Apache

    Wśród wielu opcji Apache zapewnia mod_aliasfunkcje - które czasami działają równie dobrze jak mod_rewriteRewriteRules. Zauważ, że większość z nich musi być skonfigurowana w <VirtualHost>sekcji, ale nie w .htaccessplikach konfiguracyjnych poszczególnych katalogów.

    • ScriptAliasMatchjest przeznaczony głównie dla skryptów CGI, ale powinien również działać dla PHP. Pozwala na wyrażenia regularne, takie jak inne RewriteRule. W rzeczywistości jest to prawdopodobnie najbardziej niezawodna opcja konfiguracji kontrolera frontowego typu catch-all.

    • A zwykły Aliaspomaga również w kilku prostych schematach przepisywania.

    • Nawet zwykła ErrorDocumentdyrektywa mogłaby zostać użyta, aby pozwolić skryptowi PHP obsługiwać ścieżki wirtualne. Zauważ, że jest to niezgrabne obejście, jednak zabrania czegokolwiek poza żądaniami GET i z definicji przepełnia error.log.

    Więcej wskazówek można znaleźć pod adresem http://httpd.apache.org/docs/2.2/urlmapping.html .

mario
źródło