Relative Substitution in mod_rewrite RewriteRule

7

Ich möchte eine mod_rewrite RewriteRule erstellen, die unabhängig von dem Speicherort der Webseite ist. Ich möchte die Umschreiberegel in einer .htaccess-Datei definieren. Nehmen wir dies als Beispiel:

RewriteEngine on
RewriteRule ^(.*)\.html html.php

Mit dieser Regel möchte ich alle * .html-Anforderungen einem html.php-Skript zuordnen, das sich im Webstamm befindet. Das Problem ist, dass sich die öffentliche Basis-URL der Webroot ändern kann. Das Webstammverzeichnis könnte sich also in http://www.somewhere.tld/oder in einem Unterverzeichnis unter befinden http://www.somewhere.tld/foo/bar/.

Die Verwendung eines relativen Pfads in einer Umschreiberegel funktioniert jedoch nicht. Also muss ich eines davon schreiben:

/html.php (When web is located in root directory of the web)
/foo/bar/html.php (When web is located in foo/bar sub directory)

Alternativ kann ich eine RewriteBase festlegen, aber ich möchte diesen Pfad einfach überhaupt nicht konfigurieren. Ich möchte, dass Apache automatisch das Richtige tut, damit ich das Web einfach in ein Verzeichnis kopieren kann und es funktioniert, ohne die Umschreiberegeln mitzuteilen, in denen sich das Web befindet. Wie kann ich das tun?

Kayahr
quelle
Was bestimmt in diesem Zusammenhang den Standort des "Webstamms"?
MrWhite
"Die Verwendung eines relativen Pfads in einer Umschreiberegel funktioniert jedoch nicht." - Es würde scheinen etwas anderes hier geht, weil ein relativer Pfad arbeiten sollten , wenn sie in einem gebrauchten Verzeichnis Kontext (zB. .htaccess, Sie haben Bereitstellung einer nicht bereits festgelegt) an RewriteBaseanderer Stelle in der Config (einschließlich aller geerbten configs, wenn mod_rewrite Erbe gewesen installieren). Beachten Sie, dass dies interne Umschreibungen betrifft (wie in der Frage und allen Antworten unten verwendet), nicht externe Weiterleitungen (die in der Tat ein Problem darstellen würden).
MrWhite

Antworten:

2

Meines Wissens nach gibt es keine Möglichkeit, dies zu erreichen. Sie müssen RewriteBase konfigurieren. Eine Möglichkeit besteht darin, die Einrichtung von RewriteBase mithilfe eines PHP-Skripts zu automatisieren. Dafür ist jedoch (zumindest) eine Schreibberechtigung für den .htaccess erforderlich. Sie müssen die RewriteBase jedoch in .htaccess konfigurieren.

Hameedullah Khan
quelle
1
Ich akzeptiere diese Antwort, weil lange Zeit keine Lösung erschienen ist. Ich muss einfach damit leben, dass es nicht möglich ist, ohne RewriteBase festzulegen oder absolute URLs in den Rewrite-Zielen zu verwenden.
Kayahr
11

Ich habe mit dem gleichen Problem und aus dem gleichen Grund gekämpft. Ich versuche, eine Web-App unabhängig von dem Ort zu machen, an dem sie installiert ist, ohne auf ein Konfigurationsskript oder einen manuellen Benutzereingriff zurückzugreifen. Lass die App einfach irgendwo fallen und lass sie ihre Sache machen.

Und es scheint doch eine Lösung zu geben, zumindest für Apache 2. Nimmt vier Zeilen. Das Denken dahinter zu erklären dauert allerdings mehr als vier Zeilen;)

Versuchen Sie Folgendes:

RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond $1#%{REQUEST_URI} ([^#]*)#(.*?)\1$
RewriteRule ^(.*)$ %2index.php [QSA,L]

Hier ist das Wie und Warum

  • Wir können die RewriteBase nicht dynamisch festlegen, daher setzen wir sie ein für alle Mal auf die Stamm-URL des Servers : RewriteBase /. Dies sorgt für Konsistenz, bedeutet aber auch, dass wir den URL-Pfad zum aktuellen Verzeichnis selbst festlegen und ihn der neu geschriebenen URL voranstellen müssen.

  • In welchem ​​Verzeichnis befinden wir uns? Nehmen wir ein REQUEST_URIvon / some / path / app-root / virtual / stuff an. Unser htaccess ist in App-Root. Wenn wir den virtuellen Teil - virtual / stuff - greifen und ihn aus dem entfernen REQUEST_URI, bleibt uns der URL-Pfad zu unserem App-Verzeichnis.

  • Das Erfassen des virtuellen Teils ist unkompliziert und kann in der Umschreiberegel selbst erfolgen. RewriteRule ^(.*)$ ...macht es in der $1Variablen verfügbar .

  • Jetzt führen wir unsere kleine Zeichenfolgenoperation aus und entfernen den virtuellen Teil aus dem Anforderungs-URI. Wir haben keine String-Befehle dafür, aber RewriteCond kann mit Strings übereinstimmen und Teilzeichenfolgen erfassen. Daher fügen wir eine RewriteCond hinzu, um den URL-Pfad zum aktuellen Verzeichnis zu extrahieren. Ansonsten sollte der "Zustand" uns aus dem Weg gehen und immer wahr sein.

  • Wir können die $1Variable aus der RewriteRule in der RewriteCond verwenden, da mod_rewrite einen Regelsatz tatsächlich rückwärts verarbeitet. Es beginnt mit dem Muster in der Regel selbst und überprüft, wenn es übereinstimmt, die Bedingungen. Die Variable ist also bis dahin verfügbar.

  • Während die Testzeichenfolge in der RewriteCond die Variable verwenden kann, kann der Regex der tatsächlichen Bedingung dies nicht. Innerhalb der Bedingung können wir nur interne Rückverweise verwenden. Also setzen wir zuerst eine Testzeichenfolge "[virtueller Teil] [ein Trennzeichen] [Anfrage uri]" zusammen. Das Zeichen '#' ist ein gutes Trennzeichen, da es nicht in der URL angezeigt wird. Als nächstes vergleichen wir es mit einer Bedingung von

    ([^#]*) - anything up to the separator, captures the virtual part
    #       - the separator
    (.*?)   - anything in the request uri up to what we've captured in group one,
              grabs the current directory url-path
    \1$     - group one again, ie the virtual part of the request uri
    

    Hier ist also die vollständige Bedingung : RewriteCond $1#%{REQUEST_URI} ([^#]*)#(.*?)\1$.

  • Die zweite erfasste Gruppe in der RewriteCond-Regex ist unser Standort. Wir müssen es nur der neu geschriebenen URL mit einem %2Verweis voranstellen . Das lässt uns mit RewriteRule ^(.*)$ %2index.php [QSA,L]. Voilà.

Ich habe noch keine umfangreichen Tests durchgeführt, aber ich habe festgestellt, dass es sowohl mit normalen virtuellen Hosts als auch mit virtuellem Massenhosting (unter Verwendung von VirtualDocumentRoot) funktioniert. Implizit sollten auch andere Alias-Speicherorte in Ordnung sein.

Apache 1.3

Apache 1.3 gibt es leider immer noch und es wird das RewriteCond-Muster ersticken. Apache 1.3 unterstützt den Modifikator "unreedy" (das "?" In (.*?)) nicht.

Aber für Apache 2 sollte es den Trick machen. Ich würde mich auf jeden Fall über Feedback freuen, insbesondere wenn es in Ihrer Umgebung fehlschlägt.

Bearbeiten: Ich habe gerade einen umfassenderen Artikel zu diesem Thema in meinem Blog veröffentlicht (" Verwenden von mod_rewrite in .htaccess-Dateien ohne Kenntnis der RewriteBase "). Weitere Details finden Sie dort.

Hashwechsel
quelle
Vielen Dank für Ihre Lösung, ich konnte sie nicht genug bewerten. Scheint an meinem Setup zu arbeiten.
Christian Studer
Dies funktioniert nicht, wenn der Anforderungs-URI so etwas wie /some/path/app-root/virtual//stuff(beachten Sie die doppelten Schrägstriche) ist, da Apache diese Schrägstriche entfernt, wenn sie mit dem URL-Pfad in der RewriteRuleDirektive ^(.*)$virtual//stuff
abgeglichen werden
1
Vielleicht fehlt mir etwas, aber ... "Wir setzen [RewriteBase] ... auf die Root-URL des Servers : RewriteBase /. Dies sorgt für Konsistenz" - dies scheint der Fehler zu sein . Warum müssen Sie das RewriteBasein diesem Zusammenhang überhaupt einstellen ? Warum für "Konsistenz"? RewriteBasehat einen bestimmten Zweck; das ist es nicht. Wenn Sie a nicht festlegen RewriteBase, können Sie einfach eine relative Pfadsubstitution verwenden - Problem gelöst. Siehe meine Antwort .
MrWhite
1

Ich finde die Dokumentation von Apache irreführend:

Bei Verwendung der Rewrite-Engine in .htaccess-Dateien wird das Präfix pro Verzeichnis (das für ein bestimmtes Verzeichnis immer gleich ist) automatisch für den RewriteRule-Mustervergleich entfernt und nach einer relativen Ersetzung (die nicht mit einem Schrägstrich oder einem Protokollnamen beginnt) automatisch hinzugefügt trifft auf das Ende eines Regelsatzes. Weitere Informationen dazu, welches Präfix zu relativen Substitutionen hinzugefügt wird, finden Sie in der RewriteBase-Direktive.

Das zurückgesetzte Präfix ist jedoch völlig anders (Pfad auf der Festplatte anstelle der ursprünglichen URL). Ich kann mir keine Situation vorstellen, in der dies das richtige Verhalten wäre.

Jay K.
quelle
1
"Aber das Präfix, das es wieder hinzufügt, ist völlig anders" - das hängt davon ab, was Sie für das Präfix pro Verzeichnis halten . Das Präfix pro Verzeichnis ist der Dateisystempfad, der zur .htaccess-Datei führt (einschließlich des abschließenden Schrägstrichs), nicht der ursprüngliche URL-Pfad. Dies ist sinnvoll für interne Umschreibungen (die wohl häufiger vorkommen und in der Frage verwendet werden), aber für externe Weiterleitungen müssen Sie dies überschreiben, indem Sie die Option " RewriteBaseoder" root-relativ (oder absolut - was einige sagen, dass Sie immer verwenden sollten) überschreiben. URLs.
MrWhite
0

Wie wäre es mit so etwas

RewriteEngine on
RewriteRule ^(.*)\.html $1/html.php

In meinem Beispiel wird jedoch der Dateinamensbereich der HTML-Datei beibehalten. Beispielsweise wird page1.html in page1 / html.php umgeleitet. (Hinweis: Ich habe dies überhaupt nicht getestet, versuche es auf eigenes Risiko :))

Außerdem enthält die mod_rewrite-Anleitung unzählige Beispiele, die Ihrem Problem ähneln. Hast du dir das schon angesehen?

Janne Pikkarainen
quelle
Das funktioniert nicht. Wenn sich die .htaccess-Datei in dem Verzeichnis befindet, auf das über den Pfad / foo / bar öffentlich zugegriffen werden kann, und ich auf die Datei /foo/bar/test.html zugreife, enthält $ 1 nur /test.html. Der Pfad, der außerhalb des Bereichs der .htaccess-Datei liegt, wird von mod_rewrite entfernt.
Kayahr
@kayahr Zunächst einmal müssen diese Regeln nicht in einer .htaccess-Datei enthalten sein. Zweitens muss die .htaccess-Datei nicht im Verzeichnis $ path / foo / bar gespeichert sein. Sie kann sich im Verzeichnis $ path / befinden. Jeder Verzeichnispfad wird von Apache auf .htaccess
Philip Reynolds
1
@Phil: Ich weiß, dass die Regeln nicht in .htaccess sein müssen, aber ich möchte sie in .htaccess und ich möchte, dass sie sich im Verzeichnis des Webs befinden und nicht irgendwo in einem übergeordneten Verzeichnis. Der Grund ist "einfache Bereitstellung". Entpacken Sie das Web einfach irgendwo und es funktioniert sofort. Wenn ich eine RewriteBase manuell konfigurieren oder eine .htaccess-Datei an anderer Stelle manuell erstellen oder die globale Apache-Konfiguration manuell bearbeiten muss, ist dies kontraproduktiv.
Kayahr
1
@ Kayahr, hast du gesehen, wie Wordpress nicht installiert ist? Sie richten automatisch .htaccess ein, es wird jedoch abhängig vom Wordpress-Speicherort von DocumentRoot generiert. Dies ist der einzige Weg, um dies zu erreichen. Fügen Sie ein Installationsskript hinzu, das .htaccess generiert, schreiben Sie es, wenn das Skript über Schreibberechtigung verfügt, oder zeigen Sie es zur Verwendung an, damit es manuell zu .htaccess hinzugefügt werden kann.
Hameedullah Khan
@kayahr Denken Sie über den Tellerrand hinaus, es gibt andere Möglichkeiten, Ihre Anwendung bereitzustellen, aber sie erfordern Arbeit. Willkommen in der wundervollen Welt der Devops! :)
Philip Reynolds
0

In der Frage ist nicht klar, was in diesem Zusammenhang tatsächlich den Standort des "Webstamms" bestimmt. (Da es sich deutlich vom Dokumentenstamm unterscheidet .)

Zur Beantwortung gehe ich davon aus, dass das "Webstammverzeichnis" das Verzeichnis ist, in dem die Webanwendung installiert ist. Dies sollte auch der Speicherort der .htaccessDatei sein, die diese "Webanwendung" steuert (nicht im Dokumentstamm).

In diesem Fall Sie können nur eine relative Pfad Substitution verwenden. Sie müssen nicht eine Verwendung RewriteBaseRichtlinie (die den Verzeichnis-Präfix überschreibt , die später wieder hinzugefügt wird und nicht „dynamisch“ gesetzt werden).

Wenn also das "Webstamm" ist, sollte /foo/bardas .htaccessat /foo/bar/.htaccessungefähr so ​​lauten:

RewriteEngine On
RewriteRule \.html$ html.php [L]

(Das RewriteRule Muster Suffix ^(.*)ist hier überflüssig.)

Bei einer entsprechenden Anfrage /foo/bar/baz/something.htmlwird die Anfrage intern in <document-root>/foo/bar/html.php(Dateisystempfad) umgeschrieben . <document-root>/foo/bar/ist das "Verzeichnispräfix" (der Dateisystempfad des Speicherorts der .htaccessDatei), das allen relativen Pfadsubstitutionen wieder hinzugefügt wird.


Standardmuster des Frontcontrollers ( .htaccessDatei befindet sich unter <web-application-root>/.htaccess):

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

Legen Sie die RewriteBaseRichtlinie nicht fest.


Beachten Sie, dass sich die Frage und alle Beispiele auf dieser Seite auf interne Umschreibungen und nicht auf externe Weiterleitungen beziehen . Nur wenn es sich um externe Weiterleitungen handelt , müssen wir die effektive Basis-URL (oder RewriteBase) (dynamisch) festlegen / berechnen . Wenn Sie zulassen, dass das Verzeichnispräfix im Falle einer externen Umleitung wieder zu einer relativen Ersetzung hinzugefügt wird, tritt höchstwahrscheinlich eine ungültige Umleitung auf.

Herr weiß
quelle