Nick: Clean URLs (Abschneiden von Dateinamenserweiterungen)

Hallo,

ich hätte gern URLs nach folgendem Schema:

example.org/contact/

anstatt:

example.org/contact.php

Nun habe ich ziemlich lang mit mod_rewrite herumprobiert, doch leider erfolglos. Der letzte Stand sieht so aus:

RewriteCond %{REQUESTFILENAME} !-f
RewriteCond %{REQUESTFILENAME} !-d
RewriteRule ^(.*)/$ /$1.php [R=301,L]

Könnte mir jemand behilflich sein?

LG
Nick

PS: Wie ist das eigentlich mit dem Trailing Slash, sollte der vorhanden sein oder lieber nicht?

  1. PS: Wie ist das eigentlich mit dem Trailing Slash, sollte der vorhanden sein oder lieber nicht?

    Er hat keine sinnvolle Information, ergo ist er überflüssig.

    PS: QSA solltest du noch eintragen.

    mfg Beat

    --
    ><o(((°>           ><o(((°>
       <°)))o><                     ><o(((°>o
    Der Valigator leibt diese Fische
    1. Hallo,

      Er hat keine sinnvolle Information, ergo ist er überflüssig.

      Nein, siehe hier: http://httpd.apache.org/docs/2.0/misc/rewriteguide.html. Für Apache ist macht es einen Unterschied, ob mit oder ohne /.

      Grüße

      1. Er hat keine sinnvolle Information, ergo ist er überflüssig.
        Nein, siehe hier: http://httpd.apache.org/docs/2.0/misc/rewriteguide.html. Für Apache ist macht es einen Unterschied, ob mit oder ohne /.

        Also soll er deiner Ansicht nach
          foo/
        für
          foo.php
        wählen?

        mfg Beat

        --
        ><o(((°>           ><o(((°>
           <°)))o><                     ><o(((°>o
        Der Valigator leibt diese Fische
        1. Also soll er deiner Ansicht nach
            foo/
          für
            foo.php
          wählen?

          Gemäß dem Vorschlag aus dem Guide: Ja.

          Grüße

          1. Also soll er deiner Ansicht nach
              foo/
            für
              foo.php
            wählen?
            Gemäß dem Vorschlag aus dem Guide: Ja.

            Aber gibt es überhaupt _hier_ ein trailing slash problem, oder siehst nur du eines?

            mfg Beat

            --
            ><o(((°>           ><o(((°>
               <°)))o><                     ><o(((°>o
            Der Valigator leibt diese Fische
            1. Hallo,

              Aber gibt es überhaupt _hier_ ein trailing slash problem, oder siehst nur du eines?

              die Frage ist: Kann es eins geben? Im Minimalbeispiel des Fragenden ist das sicherlich nicht der Fall. Aber wie du wissen solltest, wachsen Webprojekte, es kommen neue Anforderungen hinzu usw.
              Um für die Zukunft gerüstet zu sein und unter der Annahme, dass es nicht bei einer foo.php Datei bleiben wird: Ja, es ist immer richtig das Trailing Slash Problem zu kennen und entsprechend zu behandeln.

              Grüße

        2. Hi!

          Er hat keine sinnvolle Information, ergo ist er überflüssig.
          Nein, siehe hier: http://httpd.apache.org/docs/2.0/misc/rewriteguide.html. Für Apache ist macht es einen Unterschied, ob mit oder ohne /.
          Also soll er deiner Ansicht nach
            foo/
          für
            foo.php
          wählen?

          Es ist für den Browser wichtig, ob etwas auf / endet oder nicht. Wenn die aktuelle Ressource .../foo/bar heißt und relative Links enthält, werden diese zu .../foo/link aufgelöst, bei .../foo/bar/ hingegen zu .../foo/bar/link.

          Im normalen Betrieb ohne Rewriting lässt ein Webserver, den Browser einen Redirect nach .../foo/bar/ drehen, wenn .../foo/bar angefordert wird und das auf ein Verzeichnis (mit Default-Dokument) trifft. Wenn man sowieso mit Redirects arbeiten will (was im Falle des OP eigentlich nicht erforderlich ist), dann kann man gleich den richtigen Redirect senden und muss den Browser nicht noch eine weitere Ehrenrunde drehen lassen.

          Lo!

          1. Es ist für den Browser wichtig, ob etwas auf / endet oder nicht. Wenn die aktuelle Ressource .../foo/bar heißt und relative Links enthält, werden diese zu .../foo/link aufgelöst, bei .../foo/bar/ hingegen zu .../foo/bar/link.

            Im normalen Betrieb ohne Rewriting lässt ein Webserver, den Browser einen Redirect nach .../foo/bar/ drehen, wenn .../foo/bar angefordert wird und das auf ein Verzeichnis (mit Default-Dokument) trifft. Wenn man sowieso mit Redirects arbeiten will (was im Falle des OP eigentlich nicht erforderlich ist), dann kann man gleich den richtigen Redirect senden und muss den Browser nicht noch eine weitere Ehrenrunde drehen lassen.

            Schon richtig.

            Aber meine Frage war mit mod_rewrite im Konkreten Fall gestellt

            wenn
            /foo
            eh kein Direktory ist und die Regel matcht
            dann hilft dir auch bei

            /foo/
            ein relativer Link
            some.png

            nichts, weil foo eben kein directory ist, aus welchem eine ressource ausgeliefert werden könnte.

            mfg Beat

            --
            ><o(((°>           ><o(((°>
               <°)))o><                     ><o(((°>o
            Der Valigator leibt diese Fische
            1. Hi!

              Aber meine Frage war mit mod_rewrite im Konkreten Fall gestellt

              Und aus welcher Information ging heraus hervor, wie konkret die Verweise aussehen? Wenn ich mal davon aussgehe, dass ich nichts übersehen habe, war diese nicht da, weswegen man meiner Meinung nach nicht pauschal auf die Überflüssigkeit des Trailing Slash schließen kann.

              Lo!

              1. Also, die Dateien liegen in folgender Form vor:

                <root>/foo.php
                <root>/bar.php

                <root>/foo.php soll über example.org/foo(/) und <root>/bar.php über example.org/bar(/) aufrufbar sein. Von example..org/foo.php soll eine 301-Weiterletung auf example.org/foo(/) und von example..org/bar.php eine 301-Weiterletung auf example.org/bar(/) erfolgen.

                Wie müssen dafür die Rewrite-Condition(s) und die Rewrite-Rule aussehen?

                1. Also, die Dateien liegen in folgender Form vor:

                  <root>/foo.php
                  <root>/bar.php

                  <root>/foo.php soll über example.org/foo(/) und <root>/bar.php über example.org/bar(/) aufrufbar sein. Von example..org/foo.php soll eine 301-Weiterletung auf example.org/foo(/) und von example..org/bar.php eine 301-Weiterletung auf example.org/bar(/) erfolgen.

                  Wie müssen dafür die Rewrite-Condition(s) und die Rewrite-Rule aussehen?

                  Sorry, zwei Punkte zu viel.

                2. Also, die Dateien liegen in folgender Form vor:

                  <root>/foo.php
                  <root>/bar.php

                  <root>/foo.php soll über example.org/foo(/) und <root>/bar.php über example.org/bar(/) aufrufbar sein. Von example..org/foo.php soll eine 301-Weiterletung auf example.org/foo(/) und von example..org/bar.php eine 301-Weiterletung auf example.org/bar(/) erfolgen.

                  ?
                  example.org/foo.php
                  -> 301
                  example.org/foo
                  -> interner subrequest
                  example.org/foo.php
                  -> 301
                  example.org/foo
                  -> interner subrequest
                  example.org/foo.php
                  -> 301
                  ?

                  Wie müssen dafür die Rewrite-Condition(s) und die Rewrite-Rule aussehen?

                  Ich würde die direkte Ressourcenanforderung nicht umleiten wollen.

                  mfg Beat

                  --
                  ><o(((°>           ><o(((°>
                     <°)))o><                     ><o(((°>o
                  Der Valigator leibt diese Fische
                3. @@Nick:

                  nuqneH

                  Wie müssen dafür die Rewrite-Condition(s) und die Rewrite-Rule aussehen?

                  Warum hälst du daran fest anstatt MultiViews einzusetzen?

                  Qapla'

                  --
                  Gut sein ist edel. Andere lehren, gut zu sein, ist noch edler. Und einfacher.
                  (Mark Twain)
                  1. Hi!

                    Wie müssen dafür die Rewrite-Condition(s) und die Rewrite-Rule aussehen?
                    Warum hälst du daran fest anstatt MultiViews einzusetzen?

                    Ist das nicht mit Vorsicht zu genießen, weil es sich bei nicht direkt zu findenden /* irgendeines der /*.* greift und nicht gezielt nur auf eines zu lenken geht?

                    Lo!

                    1. @@dedlfix:

                      nuqneH

                      Ist das nicht mit Vorsicht zu genießen, weil es sich bei nicht direkt zu findenden /* irgendeines der /*.* greift und nicht gezielt nur auf eines zu lenken geht?

                      ?? Soll in *.* der erste Stern dasselbe bedeuten wie der zweite?

                      Wenn foo.html und foo.jpg im selben Verzeichis liegen, wird nicht „irgendeines“ der beiden gegriffen, sondern das, wessen MIME-Type der Client im HTTP-Accept-Header höhere Priorität gibt – Inhaltsvereinbarung (content negotiation).

                      Aber man muss ja nicht foo.html und foo.jpg im selben Verzeichis liegen haben.

                      Das hat auch den Vorteil, dass man nichts weiter ändern muss, wenn man aus einer vorher statischen Seite foo.html eine mit serverseitigem Scripting foo.php wird. Sie ist nach wie vor unter http://example.net/path/foo zu erreichen.

                      Und die verwendete serverseitige Technik geht den Client sowieso nichts an.

                      Qapla'

                      --
                      Gut sein ist edel. Andere lehren, gut zu sein, ist noch edler. Und einfacher.
                      (Mark Twain)
                      1. Hi!

                        Ist das nicht mit Vorsicht zu genießen, weil es sich bei nicht direkt zu findenden /* irgendeines der /*.* greift und nicht gezielt nur auf eines zu lenken geht?
                        ?? Soll in *.* der erste Stern dasselbe bedeuten wie der zweite?

                        Nein, es müssten eigentlich zwei verschiedene Jokerzeichen sein, à la x.y. Doch solche benannten Platzhalter suggerieren womöglich, dass man es auf bestimmte Namen festlegen kann. Man kann es höchstens in einen <Location>-Container einsperren. Bei /foo mit /foo.* könnte man annehmen, man legt Multiviews auf genau /foo fest. Stattdessen nimmt es eine beliebige URL und sucht sich eine beliebige Datei zur Vervollständigung (im Rahmen der dokumentierten Regeln, jedoch nicht exakt festlegbar).

                        Wenn foo.html und foo.jpg im selben Verzeichis liegen, wird nicht „irgendeines“ der beiden gegriffen, sondern das, wessen MIME-Type der Client im HTTP-Accept-Header höhere Priorität gibt – Inhaltsvereinbarung (content negotiation).
                        Aber man muss ja nicht foo.html und foo.jpg im selben Verzeichis liegen haben.

                        Ja, aber man muss es beachten, weil es sonst ein zunächst unerklärliches Verhalten geben kann. Deswegen fände ich es besser, diesen Fallstrick beim Empfehlen von MultiViews mit zu erwähnen.

                        Das hat auch den Vorteil, dass man nichts weiter ändern muss, wenn man aus einer vorher statischen Seite foo.html eine mit serverseitigem Scripting foo.php wird. Sie ist nach wie vor unter http://example.net/path/foo zu erreichen.
                        Und die verwendete serverseitige Technik geht den Client sowieso nichts an.

                        Nur den Administrator, der auch die weniger gewünschten Eigenschaften kennen muss.

                        Lo!

                4. Hi!

                  Also, die Dateien liegen in folgender Form vor:
                  <root>/foo.php
                  <root>/bar.php

                  Es geht nicht (nur) um diese Dateien sondern um Verweise, die in deren Ausgabe stehen, also Bilder, CSS, Javascript und ähnliches.

                  <root>/foo.php soll über example.org/foo(/) und <root>/bar.php über example.org/bar(/) aufrufbar sein.

                  Und die darin eingebundenen Ressourcen sind stets mit Verweisen relativ zum DocumentRoot eingebunden? Wenn sie relativ zur aufgerufenne Ressource verlinkt sind, kommt es auf den / an.

                  Ansonsten willst du hier ein normales internes Rewriting, wie es schon oft dokumentiert ist.

                  Von example..org/foo.php soll eine 301-Weiterletung auf example.org/foo(/) und von example..org/bar.php eine 301-Weiterletung auf example.org/bar(/) erfolgen.

                  Und das noch oben drauf geht so nicht, weil das eine zum anderen weiterleitet und wieder zurück. /foo wird umgeschrieben zu foo.php, danach folgt immer ein interner Redirect mit foo.php als neuem Request. Aber das willst du ja als als /foo umgeschrieben sehen. Die Reihenfolge der Regel spielt auch keine Rolle, weil jeweils nur die eine oder die andere passt und das L-Flag nichts nützt. Allerdings könntest du die Umschreibung des internen Redirekts zu verhindern versuchen, indem du mit einer RewriteCond(ition) IS_SUBREQ auswertest. Da kann ich aber nicht mit Erfahrung dienen, jedoch sollte das laut der im Netz zu findenden Literatur zur Identifikation des internen Redirects passen.

                  Lo!

                  1. Hi,

                    Allerdings könntest du die Umschreibung des internen Redirekts zu verhindern versuchen, indem du mit einer RewriteCond(ition) IS_SUBREQ auswertest. Da kann ich aber nicht mit Erfahrung dienen, jedoch sollte das laut der im Netz zu findenden Literatur zur Identifikation des internen Redirects passen.

                    http://httpd.apache.org/docs/2.2/en/mod/mod_rewrite.html#rewriterule:

                    “'nosubreq|NS' (not for internal sub-requests)
                    This flag forces the rewriting engine to skip a rewriting rule if the current request is an internal sub-request. For instance, sub-requests occur internally in Apache when mod_include tries to find out information about possible directory default files (index.xxx files).”

                    Als sub-request gilt nur das, was von anderen Modulen wie bspw. dem erwähnten mod_include (oder meinen die da eigentlich mod_dir, wenn es doch um directory default files geht?) ausgelöst wird - interne Umschreibungen durch die Rewrite-Engine fallen nicht darunter, so dass dieses Flag ebenso wie IS_SUBREQ bei einer RewriteCond wirkungslos bleibt.

                    Was ggf. funktionieren könnte, ist das Setzen einer Umgebungsvariablen (Flag 'env|E=') und späteres Abfragen dieser auf Existenz/Inhalt. In der Doku finde ich keine explizite Erwähnung, ob diese auch über interne Rewrite-Runden hinweg erhalten bleiben, gehe aber erst mal davon aus.

                    MfG ChrisB

                    --
                    RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
                    1. Hi!

                      http://httpd.apache.org/docs/2.2/en/mod/mod_rewrite.html#rewriterule:

                      “'nosubreq|NS' (not for internal sub-requests)
                      This flag forces the rewriting engine to skip a rewriting rule if the current request is an internal sub-request. For instance, sub-requests occur internally in Apache when mod_include tries to find out information about possible directory default files (index.xxx files).”

                      Als sub-request gilt nur das, was von anderen Modulen wie bspw. dem erwähnten mod_include (oder meinen die da eigentlich mod_dir, wenn es doch um directory default files geht?) ausgelöst wird - interne Umschreibungen durch die Rewrite-Engine fallen nicht darunter, so dass dieses Flag ebenso wie IS_SUBREQ bei einer RewriteCond wirkungslos bleibt.

                      Das las ich auch (also zumindest die Erklärung zu IS_SUBREQ, nach Flags hab ich nicht geschaut) und suchte zusätzlich nach Anwenderberichten, und die lasen sich beim Überfliegen so, als ob man damit auch den internen Redirect erkennen kann. Müsste man mal probieren.

                      Lo!

                      1. Hi,

                        Das las ich auch (also zumindest die Erklärung zu IS_SUBREQ, nach Flags hab ich nicht geschaut) und suchte zusätzlich nach Anwenderberichten, und die lasen sich beim Überfliegen so, als ob man damit auch den internen Redirect erkennen kann. Müsste man mal probieren.

                        Also ich hatte damit in bisherigen Versuchen keinen Erfolg;
                        Dass ich es schlicht und einfach „falsch“ gemacht habe, ist zwar nicht auszuschließen, aber ich hatte auch schon mal mit intensivem Rewrite-Logging das ganze nachzuvollziehen versucht, und dabei konnte ich nicht entdecken, dass IS_SUBREQ wie in der Doku beschrieben gesetzt gewesen wäre bei internen Umschreibungs-Schleifen.

                        MfG ChrisB

                        --
                        RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
                        1. Hi!

                          Also ich hatte damit in bisherigen Versuchen keinen Erfolg;

                          Ok, dann fällt diese Möglichkeit also aus.

                          Lo!

    2. PS: QSA solltest du noch eintragen.

      Also so?

      RewriteCond %{REQUESTFILENAME} !-f
      RewriteCond %{REQUESTFILENAME} !-d
      RewriteRule ^(.*)/$ /$1.php [R=301,L,QSA]

      Das funktioniert auch nicht. :-(

  2. @@Nick:

    nuqneH

    ich hätte gern URLs nach folgendem Schema:
    example.org/contact/

    Wozu der '/' am Ende?

    MultiViews ist dein Freund, dann kannst du dir mod_rewrite sparen.

    Qapla'

    --
    Gut sein ist edel. Andere lehren, gut zu sein, ist noch edler. Und einfacher.
    (Mark Twain)
    1. Wozu der '/' am Ende?

      Da bin ich mir ja unschlüssig.

      MultiViews ist dein Freund, dann kannst du dir mod_rewrite sparen.

      Und was ist der Vorteil gegenüber mod_rewrite?

      1. @@Nick:

        nuqneH

        MultiViews ist dein Freund, dann kannst du dir mod_rewrite sparen.

        Und was ist der Vorteil gegenüber mod_rewrite?

        https://forum.selfhtml.org/?t=203395&m=1375114

        Qapla'

        --
        Gut sein ist edel. Andere lehren, gut zu sein, ist noch edler. Und einfacher.
        (Mark Twain)