toscho.design

WordPress und .htaccess: Request säubern

WordPress .htaccess

WordPress erlaubt ja sehr flexible Permalinks. Dazu schreibt man sich dieses in die .htaccess:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
</IfModule>

Allerdings finde ich diese Regeln noch zu großzügig; sie verhindern nicht folgende Probleme:

  • Archive können um die Kette 00/ ergänzt werden.
    http://example.com/2009/00/ liefert ganz normalen Inhalt aus; http://example.com/0/ ebenso.
  • Manch externe Software »veredelt« gesetzte Links gerne um Trackingparameter in der Annahme, jeder verwende Google-Analytics.
    Twitterlinks beispielsweise bekommen ungefragt den Querystring ?utm_source=twitterfeed&utm;_medium=twitter angehängt, und FeedBurner klebt ein ?utm_source=feedburner&utm;_medium=feed&utm;_campaign=Feed:+wdrss+(WDRSS) an. Das nimmt WordPress einfach so hin.
  • Und dann gibt es noch die Spinner, die in den Parameter p, der eigentlich nur numerische Werte empfängt, etwas ganz anderes hineinpacken, eine URL etwa. Auch hier reagiert WordPress nicht.
  • Schließlich generiert WordPress für Archive, die geblättert werden, URLs in der Form /2009/page/3/.
    /2009/page/1/ liefert leider denselben Inhalt wie /2009/, und wenn jemand eine Ebene hochklettert (dafür habe ich eine Mausgeste), dann wirft /2009/page/ nur eine wenig hilfreiche Fehlermeldung aus.

Mehrere Adressen für eine Ressource wollen wir nicht. Und 404-Meldungen sind für kaputte Crawler, nicht für richtige, wertvolle Besucher.

Wir werfen also den ursprünglichen WordPress-Eintrag raus und ersetzen ihn durch diesen:

<LimitExcept HEAD GET POST>
    order deny,allow
    deny from all
</LimitExcept>
RewriteEngine On
RewriteBase /
# Catch obstrusive tracking parameters like ...
# ?utm_source=twitterfeed&utm_medium=twitter or
# ?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+wdrss+(WDRSS)
# ... because they generate doubled content.
RewriteCond %{QUERY_STRING} utm_source= [NC,OR]
# Some people put their own URIs into this parameter. May be better banned.
RewriteCond %{QUERY_STRING} ^p=\D
# The question mark removes the query string.
RewriteRule (.*) /$1? [L,R=301]
# WordPress allows URLs like /2010/0/ or /00/ == doubled content
RewriteRule (^0+|(.*)/0+)/$ /$2 [L,R=301]
# WordPress: paged content creates a /page/
RewriteRule (^p|(.*)/p)age/(1/)?$ /$2 [L,R=301]
# Fix double slashes redirect /a///b to /a/b
# http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /(([^/\ ]+/)*)/+([^\ ]*)
RewriteRule ^ /%1%3 [L,R=301]
# Images, Stylesheets etc.
RewriteCond %{REQUEST_URI} !.+\.\w{2,4}$
# Existing file
RewriteCond %{REQUEST_FILENAME} !-f
# Existing directory
RewriteCond %{REQUEST_FILENAME} !-d
# Symbolic link
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^ index.php [L]

Mein Dank geht an Francesco Schwarz (@isellsoap) für seine ausführlichen Tests.

18 Kommentare

  1. zonebattler am 02.04.2010 · 07:25

    Vielen Dank für die wertvollen Tipps!

    Bei mir macht die letzte Zeile Schwierigkeiten, die da lautet:

    RewriteRule ^ index.php [L]

    Füge ich diese Zeile ein, wird mein Blog nicht mehr ordentlich formatiert und "nackt" angezeigt, so als würden die CSS-Angaben vollständig ignoriert. Von Haus aus steht in meiner .htaccess die Zeile

    RewriteRule . /index.php [L]

    drin, und die macht keine Schwierigkeiten. Leider bin ich nicht fachkundig genug, um dieses Verhalten zu interpretieren und Ursachenforschung zu betreiben, aber immerhin kann ich das beschriebene Fehlerbild so reproduzieren...

    Sei's drum, die anderen Befehle konnte ich sämtlich einbauen und damit meinen virtuellen Maschinenraum wieder ein Stückchen verbessern. Nochmals danke! :-)

    Beste Grüße,
    Ralph

  2. Thomas Scholz am 02.04.2010 · 10:29

    @zonebattler: Den CSS-Ausfall finde ich sehr seltsam. Mir fällt jetzt erstmal kein rechter Grund dafür ein. Wenn die andere Lösung funktioniert, dann ist es ja in Ordnung.

  3. zonebattler am 02.04.2010 · 10:38

    Heißt das, daß beide Zeilen letztlich das gleiche bewirken (sollen)?

  4. Thomas Scholz am 02.04.2010 · 10:40

    @zonebattler: Ja, der Ersatz wird an die RewriteBase angehängt.

  5. zonebattler am 02.04.2010 · 11:11

    Also dann lag mein Problem vielleicht daran, daß ich BEIDE Zeilen inder .htaccess drinhatte (die schon vorhandene PLUS die von Dir). Egal, wenn's eine der beiden tut, dann paßt es ja jetzt...

    Dank & Gruß,
    Ralph

  6. Thomas Scholz am 02.04.2010 · 11:14

    @zonebattler: Ach sooo! Mein Code soll den von WordPress erzeugten natürlich komplett ersetzen. Das habe ich jetzt deutlicher herausgestellt.

  7. zonebattler am 02.04.2010 · 12:17

    Ich habe natürlich schon sämtliche Doubletten vermieden, also in meiner Ursprungsdatei nur jene Zeilen dringelassen, die nicht in Deiner vorkommen. Im Wesentlichen ist das jetzt nur noch die Zeile

    DirectoryIndex index.php

    ganz am Anfang. Jetzt habe ich übrigens testhalber Dein

    RewriteRule ^ index.php [L]

    am Schluß anstelle des ursprünglichen

    RewriteRule . /index.php [L]

    reingeschrieben, und siehe, jetzt klappt es wunderbar. Der Fehler war also hausgemacht. Sorry für die Aufregung!

    Beste Grüße,
    Ralph

  8. Francesco am 04.04.2010 · 16:49

    Oha. WordPress scheint noch viel großzügiger mit URLs umzugehen. Jede URL (außer Jahres- und Monatsarchive) kann um beliebige Zahlenreihenfolgen ergänzt werden … und es erscheint keine 404-Fehlerseite. Oder täusch ich mich etwa? Probiert's mal aus …

  9. zonebattler am 04.04.2010 · 18:33

    Ich kann (bzw. muß) das bestätigen. Sehr eigenartig, in der Tat...

  10. Francesco am 04.04.2010 · 18:37

    Die Startseite und (wie schon erwähnt) Jahres- und Monatsarchive sind davon nicht betroffen, der Rest schon.

  11. Thomas Scholz am 04.04.2010 · 18:42

    Schon gut, schon gut. Hört bitte mal auf, hier durchzutesten; ihr kriegt gleich ein passendes Plugin. ☺

  12. Schnurpsel am 06.04.2010 · 12:34

    Man kann übrigens auch beliebige, unbekannte Parameter anhängen und bekommt immer eine Fehlerfreie Seite angezeigt, z.B.:
    http://testblog.schnurpsel.de/?quark=Gesund%20und%20schmeckt%20lecker
    Sowas bekommt man allerdings nur in WP weggebügelt, da muß man die URL-Parameter gegen die WordPress bekannten checken und entsprechend reagieren.

  13. Schnurpsel am 06.04.2010 · 13:12

    Vielleicht noch eine kleine Ergänzung zu der Zahlensache (Francesco). Das funktioniert nur bei Seiten und Einzelartiklen. Diese können ja auf mehrere Seiten aufgeteilt werden und genau deswegen geht es überhaupt. Man müßte nun prüfen, ob in der Seite oder im Artikle überhaupt ein Seiten-Trenner vorkommt und wie oft. Puhhh, das nimmt ja Ausmaße an...

  14. Thomas Scholz am 06.04.2010 · 13:40

    @Schnurpsel:

    Man müßte nun prüfen, ob in der Seite oder im Artikle überhaupt ein Seiten-Trenner vorkommt und wie oft. Puhhh, das nimmt ja Ausmaße an...

    Genau diese Arbeit habe ich im nächsten Beitrag in ein Plugin gegossen. War tatsächlich etwas knifflig.

    Die übrigen Parameter freilich kann man nur schwer erfassen. Ich hatte mal eine Whitelist, aber da haben dann einige Plugins nicht mehr zuverlässig funktioniert … selbst ein Abgleich gegen die interne Liste query_vars genügt nicht unbedingt.

  15. Schnurpsel am 06.04.2010 · 14:08

    Ein ordentliches Plugin sollte seine Variablen mit add_query_var bei WP anmelden :-)
    Zumindest habe ich den Check gegen query_vars seit einiger Zeit bei mir drin, funktioniert bisher problemlos.

  16. Mario am 20.08.2010 · 12:00

    Hallo!

    Wenn ich diese Zeilen

    # Catch obstrusive tracking parameters like ...
    # ?utm_source=twitterfeed&utm_medium=twitter or
    # ?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+wdrss+(WDRSS)
    # ... because they generate doubled content.
    RewriteCond %{QUERY_STRING} utm_source= [NC,OR]
    # Some people put their own URIs into this parameter. May be better banned.
    RewriteCond %{QUERY_STRING} ^p=\D
    # The question mark removes the query string.
    RewriteRule (.*) /$1? [L,R=301]

    einfüge, dann kommt man direkt auf die Startseite und nicht zum gewünschten Artikel.

    LG, Mario

  17. Thomas Scholz am 20.08.2010 · 12:04

    @Mario: Dann fährt dir irgend eine andere Rewrite-Regel dazwischen. Hier funktioniert es.

  18. Robert Hartl am 21.12.2010 · 16:08

    Guter Ansatz. Bislang hilft vielleicht nur das Tag rel canonical. Allerdings sollten sich die oben genannten überschaubaren klassischen Probleme per htaccess schon gut lösen lassen, als eine falsche Seite/ URL oder einen 404 anzuzeigen. Ich werde versuchen, die Regeln zu erweitern, um bspw. die page-Sache etc. abzufangen. Danke soweit.