Die .htaccess-Datei: Alles was man als SEO wissen muss

Bookmark and Share

Treibt man sich in der SEO-Szene herum, ist schnell und oft die Rede vom “htaccess”: Einfach nur dies und das “ins htaccess” mit aufgenommen, schon klappt es dann auch mit dem SEO. Denn damit gelänge es scheinbar mühelos, auch die verworrensten URL-Strukturen einer Webapplikation noch blendend aussehen zu lassen. Oder einfach beim Umzug auf eine neue Domain die alten URLs weiterzuleiten, um damit den “juice” noch mitzunehmen.

Als Außenstehender ohne näheres Hintergrundwissen, könnte man dabei schnell den Eindruck gewinnen, es handele sich um eine Art Wunderwaffe im täglichen SEO-Kampf ums bessere Ranking. Dabei hat die Frage nach dem konkreten Inhalt der “htaccess” durchaus das Potential, auch dem erfahrensten SEO schon mal die Schweißperlen auf die Stirn zu treiben. Denn leider wird sogar von den Profis der Szene das Wort oft missverständlich oder sogar falsch verwendet.

Wenn ein SEO von htaccess spricht, meint er damit meiner Erfahrung nach meist die Verwendung des in SEO-Kreisen äußerst beliebten Webservermoduls “ModRewrite”, das solche Zaubereien, s.o. erst ermöglicht. Doch dazu später noch mehr. Zuvor möchte ich versuchen, ein wenig Aufklärungsarbeit  zum besseren Verständnis der “htaccess” zu leisten und will dazu kurz auf einige grundlegende Konzepte des Apache Webservers eingehen.

Theorie:

Praxis-Tipps

Apache Module

Der Apache Webserver, besteht grob gesprochen aus unterschiedlichen Modulen, die ihre Features dem Webserverbenutzer zur Verfügung stellen. Die Module werden dabei entweder beim Start des Servers geladen oder sind statisch in den Server-Kernel kompiliert.

Beispiele für Module  sind unter  anderen:

  • mod_auth: Modul für die Verwaltung von Authentifikationen für die Zugriff auf Seiten
  • mod_headers: “Customization” von HTTP request und HTTP-Response Headern
  • mod_proxy: Den WebServer zum “Proxy” umkonfigurieren (buggy)
  • und eben (drumroll) mod_rewrite: “Überschreiben”, Verändern von URLs; Auch bekannt unter dem Namen mod_seo ;-)

Das von SEOs so beliebte mod_rewrite ist also nur eines von vielen Modulen des Apache-Webservers.

Direktiven

Diese Module stellen eine Vielzahl von Möglichkeiten der Konfiguration zur Verfügung, die in Form sogenannter Direktiven in die Konfigurationsdatei des Servers eingetragen werden können. Die DocumentRoot-Direktive zum Beispiel. Über sie kann angegeben werden, in welchem physikalischen Verzeichnis einer Maschine die Dateien liegen, die im Web zur Verfügung gestellt werden sollen:

DocumentRoot /usr/local/apache/htdocs

Natürlich gibt es Tonnen von solchen Direktiven (s. Links). Die Kenntnis, welche Direktive von welchem Modul zur Verfügung gestellt wird, ist insofern nicht ganz ohne Belang, da Direktiven natürlich nur dann in einer Konfiguration verwendet werden können, wenn das dafür zuständige Modul auch vorhanden und geladen ist. Damit es beim Laden der Konfiguration nicht zu Problemen kommt, gibt es die Direktive IfModule, über die sich sicherlich schon der eine oder andere gewundert hat, der die htaccess seines WordPress-Blogs bearbeitet hat:

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

Der oben gezeigte Abschnitt sagt also lediglich. Diese Direktiven nur interpretieren, wenn das Module mod_rewrite auch vorhanden und geladen ist. Dabei ist, vermutlich aus historischen Gründen, der Name der Quelltextdatei, hier mod_rewrite.c mit anzugeben. Etwas verwirrend…

Der Server Kontext

Innerhalb der Konfiguration des Servers, meist hinterlegt in einer Datei names httpd.conf, werden nun auch noch Kontexte unterschieden, innerhalb deren die Direktiven angewandt werden dürfen bzw. nicht. Dabei gibt es

  • den Server-Kontext: die Hauptkonfiguration des Servers
  • den VirtualHost-Kontext: Innerhalb der Hauptkonfiguration können mehrere Hosts (“Domains”) als VirtualHosts konfiguriert werden
  • den Directory-Kontext: Konfigurationen, die sich innerhalb eines Verzeichnisses abspielen.
  • und eben den htaccess-Kontext

Innerhalb eines Kontextes sind immer nur bestimmte Direktiven erlaubt. Das ermöglicht somit ein gewisses Berechtigungskonzept bei der Konfiguration des Servers und soll der Betriebssicherheit dienen: Die mächtigsten Direktiven sind nur im ServerKontext erlaubt, auf den z.B. nur der Webmaster Zugriff hat, usw.

Konfigurationsänderungen

Die Gesamtkonfiguration des Servers, die “http.conf” (und damit verbundene, evtl. ausgelagerte Dateien, die eingebunden werden können) werden beim Start des Servers interpretiert oder durch eine explizite Anweisung an den Server neu geladen. Änderungen der Konfiguration werden deswegen prinzipiell erst mal nur durch Neustart des Servers oder eben das “Nachladen” der Konfiguration wirksam.

htaccess

Die große Ausnahme stellt hier der htaccess-Kontext dar. Damit es möglich wird, vielen Webserver-Benutzern unter einem Server ebenfalls eine flexible Konfigurationsmöglichkeit anzubieten, ohne dabei jeden Server durchstarten zu müssen, gibt es den htaccess-Mechanismus, der vom Prinzip her sehr einfach ist:

Befindet sich in einem Webserver-Verzeichnis eine Datei mit dem Namen .htaccess und ist dieser Mechanismus über den Server-Kontext auch aktiviert, werden die sich in dieser Datei befindlichen Direktiven zur Laufzeit vom Server interpretiert. Damit ist es also auch möglich, Konfigurationsänderungen des Servers vorzunehmen, ohne Zugriff auf den Serverkontext zu haben.

Spricht man im Allgemeinen von htaccess, ist damit meist der damit verbundene beschriebene Konfigurations-Mechanismus gemeint. Um Verwirrungen zu vermeiden, bevorzuge ich persönlich deswegen lieber den Ausdruck htaccess-Datei. Vor allem, wenn es um SEO geht :-)

Damit der htaccess-Mechanismus auf einem System funktioniert, muss er u.U. noch im Serverkontext aktiviert werden. Das geschieht mit den Direktiven AllowOverride und Options.

<Directory “/path/to/your/webfolder/”>
Options All +MultiViews
AllowOverride All
</Directory>

Die Verwendung dieser Direktiven ist ein wenig gewöhnungsbedürftig. Wie man sieht, findet die Konfiguration im Directory-Kontext statt: Für das Webverzeichnis weist man mit Options erst einmal den Server an, welche Einstellungen für dieses Verzeichnis grundsätzlich gelten, in diesem Fall All. Eine Liste aller Optionen findet sich unter http://httpd.apache.org/docs/2.2/mod/core.html#options. Die Option MultiViews muss dabei etwas eigenartiger Weise explizit angegeben werden. Jede dieser Optionen repräsentiert einen Schwung von Direktiven. Hier sei ebenfalls auf die apache-Dokumentation verwiesen.

Welche der gesetzten Optionen (und damit Direktiven) dann davon auch in der htaccess-Datei zulässig sind und ggf. überschrieben werden können, wird über AllowOverride gesteuert, in diesem Fall ebenfalls All. All bedeutet natürlich auch nur alle diejenigen, die auch im Kontext der htaccess-Datei zugelassen sind.

Welche Direktiven in einer htaccess zugelassen sind, hängt also von der Konfiguration des Serverkontextes eures Providers ab. Wenn eine Direktive syntaktisch richtig, aber nicht erlaubt ist, antwortet der Server, genau wie bei einer syntaktisch falsch verwendeten Direktive, mit einem “500 Internal Server Error”. Checkt deswegen euer ErrorLog: Findet ihr einen Hinweis wie “.htaccess: Options not allowed here” o.ä. , fragt bei eurem Provider nach: Vielleicht lässt er sich erweichen, die benötige Option zu aktivieren, vor allem, wenn viele Kunden danach fragen.

ModRewrite

Wie erwähnt, gibt es mehrere Module mit vielen Direktiven, die innerhalb der htaccess-Datei verwendet werden können. Nicht alle haben für einen SEO auch wirklich Belang. Weil mod_rewrite aber das für SEOs relevanteste ist, will ich damit anfangen. Mod_rewrite ist nun oft in einschlägigen Foren umfänglich diskutiert worden, deswegen setzte ich grundlegende Kenntnisse einmal voraus und möchte nur auf einige Besonderheiten eingehen, die (zumindest bei mir) immer wieder zu Verwirrungen führten.

In diesem Zusammenhang sei auch noch auf die Original-Dokumentation verwiesen. Sie ist zwar in Englisch, aber meiner Kenntnis nach noch immer noch das vollständigste und beste Dokument zum diesem Thema. Wer nicht gleich auf Anhieb alles versteht, sollte sich nicht sorgen ;-). Viele Dinge werden eben erst in der konkreten Anwendung klar, wie zum Beispiel die

leere Ersetzung

RewriteEngine on
RewriteRule . – [L]

So auf den ersten Blick nicht unbedingt ersichtlich, wozu das gut sein soll, denn die Regel sagt ja: Egal was da kommt: Mach nix. Sinn macht das erst im Zusammenhang mit einer Bedingung, zum Beispiel

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule . – [L]

Mit dieser Regel hebelt man alle folgenden Regeln für physikalisch vorhandene Dateien im DocumentRoot und darunter aus: Handelt es sich um eine physikalisch vorhandene Datei (-f), biege ab. Praktisch, und eine Regel, die ich ganz gerne am Anfang einer ModRewrite-Konfiguration stehen habe: Ermöglich sie mir doch, auch bei noch so komplexen Regelwerk immer noch Dateien und Skripte auf den Server hochzuladen, die dann ausgeführt werden, ohne das gesamte Regelwerk zu durchlaufen.

Interne, implizite und explizite Redirects (von ModRewrite)

Es ist ein großer Unterschied, ob man einen URL “in echt” weiterleitet, oder das Handeln dem Modul durch Angabe einer RewriteRule überlässt. Dabei kann es nämlich auch sein, dass der “Redirect” nur intern durchgeführt oder intern forciert wird, was wiederum bedeutet, das schon ein “echter” Redirect durchgeführt wird, ohne dass man das explizit angegeben hat.. Auch das kann verwirrend sein, deswegen ein Beispiel zur Verdeutlichung:

Internal Redirect

RewriteEngine on
RewriteRule ^old-script.php$ new-script.php [L]

Ruft man den URL /old-script.php auf, wird /new-script.php ausgeführt und ausgegeben. Voraussetzung dafür ist natürlich schon, dass es das Script new-script.php auch gibt. Der Server antwortet dabei schlicht mit einem Statuscode “200 OK”, in der der Adresszeile steht aber immer noch /old-script.php, und im RewriteLog (s. weiter u.) so etwas wie [ INTERNAL REDIRECT] . Das “Rewriten” ist damit nach außen hin auch gar nicht sichtbar, internal eben.

Die Dokumentation spricht hier etwas verwirrend von einem “internen Redirect”, dabei ist meines Erachtens das wesentliche an einem Redirect eigentlich gar nicht mehr gegeben, nämlich die Anweisung an den Client, sich die Daten woanders zu holen.

Impliziter Redirect

Ergänzt man das ganz nun um einen Server und eine Protokoll zu einem vollqualifizierten URL, wird ein Redirect implizit erzwungen.

RewriteEngine on
RewriteRule ^old-script.php$ http://wwww.myhost.de/new-script.php [L]

Der Unterschied zum ersten Beispiel ist dabei nicht ganz unwesentlich: Denn jetzt schickt der Server an den Browser lediglich die Information, das sich die Ressource “vorübergehend” woanders befindet ( “302 Moved temporarily”) und gibt dabei auch die neue Adresse in der Header-Variable “Location” mit. Content wird dabei keiner übertragen. Befolgt der Browser dies, steht dann in seiner Adresszeile http://wwww.myhost.de/new-script.php. Das neue Ziel wurde vom Browser aufgerufen.

Das kostet zum einen Performance, zum anderen kann dieser “echte” Redirect aber auch von außen “gesehen” bzw. insbesondere von Crawlern natürlich, interpretiert werden.

Expliziter Redirect

Das Schöne ist, dass man bei ModRewrite eben auch explizit angeben kann, das man redirecten will und wie.

RewriteEngine on
RewriteRule ^old-script.php$ new-script.php [L,R=301]

Nun wird versucht, den URL wie oben weiterzuleiten, aber unter dem explizit angegebenen Statuscode “301 Moved Permanently”.

Man beachte, dass im Beispiel in der Ersetzungsregel nur der Skriptname new-script.php angegeben wird. Für das Erzeugen eines gültigen ResponseHeaders für den Redirect muss aber ein vollqualifizierter URL zurückgeliefert werden. Deswegen ergänzt mod_rewrite den URL um den aktuellen Servernamen und das aktuelle Protokoll.

Das kann wiederum zu unerwünschten Ergebnissen führen, wenn der konfigurierte Servername vom Hostnamen abweicht: Eine Beispielkonfiguration eines Apache-Hosts, der auf Port 80 hört:

<VirtualHost *:80>
DocumentRoot /path/to/my/webfolder
ServerName www.domain.de
ServerAlias www.domain-2.de
# …
</VirtualHost>

Der Abschnitt definiert einen “VirtualHost” für die Domain www.domain.de. Der Name des virtuellen Servers, “ServerName” also, ist damit www.domain.de.Es wird aber auch noch ein Alias auf den Servernamen definiert, www.domain-2.de. Damit sollte der Server auch auf Anfragen des Namens www.domain-2.de reagieren, mit einem kleinen, aber feinen Unterschied:

Aufruf von mod_rewrite: Wert von ist
http://www.domain.de
%{SERVER_NAME} www.domain.de
%{HTTP_HOST} www.domain.de
http://www.domain-2.de
%{SERVER_NAME} www.domain.de
%{HTTP_HOST} www.domain-2.de

Beim Aufruf von http://www.domain-2.de/old-script.php würde die Regel aus dem Beispiel oben und dieser Host-Konfiguration also auf http://www.domain.de/new-script.php weiterleiten. Das kann man natürlich unterbinden, wenn der Hostname bei der Weiterleitung auch noch mit angegeben wird.

Backreferences

Oft unverstanden ist das Konzept der Backreferences. Dabei sind diese keine Erfindung von mod_rewrite, sondern in regulären Ausrücken üblich, weil sehr nützlich.

RewriteEngine On
RewriteRule ^/(.+)/.+.html$ /index.php?id=$1

Befindet sich in einem Muster um ein Teilmuster eine Klammer, wie hier (.+) , kann in der Ersetzungsregel die Entsprechung des geklammerten Musters, der “Match”, mit $ referenziert werden. Die angegebene Zahl, im Beispiel 1, bezieht sich dabei auf den ersten Match. Es können so maximal 6 Referenzen verwendet werden.

RewriteRule ^/(.+)/(.+).html$ /index.php?id=$1&name=$2

Dies ist z.B. nützlich bzw. notwendig, um Steuerinformation einer dynamischen Applikation im “schönen” URL unterzubringen. Der URL http://www.domain.de/234/tolle-seite.html könnte damit zum Beispiel auf http://www.domain.de/index.php?id=234 abgebildet und dabei die Steuerinformation 234 im Parameter id an index.php übergeben werden.

SEO-Pitfall

Natürlich muss dabei die Applikation auch dafür sorgen, dass die Zeichenkette “tolle-Seite” bei der Ausgabe im URL mit ausgegeben wird. Technisch gesehen ist diese Information so erst einmal reine Makulatur, denn sie wird vom verarbeitenden Skript ja gar nicht berücksichtigt. Mit der im Beispiel angegebenen Regel matched z.B. ja auch http://www.domain.de/234/elender-versager.html.

In komplexeren Applikationen mit minderwertiger URL-Kontrolle ist der “Duplicate Content” im Sinne von multiplen URLs damit eigentlich schon vorprogrammiert: Allzu schnell gelangen redundante “sprechende” Teile mit in die Ausgabe. Fällt nicht auf, denn sie steuern ja nichts… Man sollte so etwas eigentlich nur verwenden, wenn die Applikation auch in der Lage ist, den URL beim Request auch wieder zu verifizieren, um ggf. einen Redirect zur richtigen Seite durchzuführen oder ein passendes Canonical-Tag zu setzen.

RewriteLog

Zur Fehlersuche bei ModRewrite gibt es eine Direktive, die eigentlich ganz einfach ist.

RewriteLog /path/to/my/log/file/log.txt
RewriteLogLevel 10

RewriteLog gibt eine Logdatei an, mit RewriteLogLevel 10 werden interne Verarbeitungsschritte auf dem gesprächigsten Niveau 10 in eine Datei protokolliert.

Wer weiß, wie oft diese beiden Zeilen so oder ähnlich schon in eine .htaccess geschrieben wurden! Leider vergeblich, denn diese Direktive steht in der htaccess-Datei gar nicht zur Verfügung. Wer es nicht glaubt, werfe einen Blick in sein ErrorLog:

RewriteLog not allowed here”

Und das war es dann eigentlich auch schon. Vermutlich ist das auch der Grund, warum so viel in Foren über RewriteRules diskutiert und spekuliert wird: Eine strukturierte Fehlersuche und damit nicht zuletzt auch ein sauberes Design von komplexeren Regeln, ist ohne Zugriff auf den Serverkontext gar nicht möglich! Man steht bei der Fehlerrecherche also auf ziemlich verlorenem Posten und ist auf “Rumprobieren” angewiesen.

Verschafft euch eine lokale Webserver Installation, auf der ihr auch alle Rechte habt, um das RewriteLog zu aktivieren und testet die .htaccess-Datei dort.

Querystring bei ModRewrite

Wer meint, URLs auf Basis von Werten im QueryString weiterleiten zu müssen, sei hier schon einmal gewarnt, denn der QueryString (“alles nach dem Fragezeichen”) eines URLs wird von der Direktive RewriteRule gar nicht zur Mustervergleich herangezogen wird. Folgender Anwendungsfall: Wir wollen alle URLs, die einen QueryString mit dem Parameter param1 und eine Zahl als Wert hat, auf ein neues Format abbilden. Warum auch immer.

http://www.server.de/pfad/datei.html?param1=1343&param2=a

soll also zum Beispiel auf

http://www.server.de/index.php?p=1343

weiterleiten.

RewriteRule “param1=([0-9]+)” /index.php?p=$1

Wer meint, das damit oder ähnlichem, lösen zu können, irrt gewaltig. Hat man Zugriff auf sein RewriteLog, wird man darin so etwas auffinden. Aufruf: http://www.server.de/pfad/datei.html?param1=1&param2=2

… applying pattern ‘param1=([0-9]+)’ to uri ‘pfad/datei.html’

Der Querystring wird für den Vergleich also gar nicht herangezogen. Abhilfe, wenn auch nur eingeschränkt, kann man mit einer Bedingung schaffen.

RewriteCond %{QUERY_STRING} param1=[0-9]+
RewriteRule pfad/datei.html /index.php?param3=3 [L,QSA]

Es ist dabei wohlgemerkt auch nicht möglich, einzelne Werte des QueryStrings über Backreferences an das verarbeitende Script zu übergeben. Der Wert des Parameters param1 kann also gar nicht, wie in unserem Anwendungsfall gefordert, an die die Variable p übergeben werden (danke für eure Posts, wenn ihr hier andere Möglichkeiten seht). Nur der ganze QueryString kann angehängt werden, was hier durch die Angabe QSA (QueryStringAppend) geschieht. Mit der beschriebenen Regel wird dann

/pfad/datei.html?param1=1&param2=2

zu

/index.php?param3=3&param1=1&param2=2

Ein Problemchen hat man aber auch noch, wenn man den QueryString gar nicht mehr an den weitergeleiteten URL übergeben will. Die Option QSA der RewriteRule ist eigentlich eher gedacht, um bei der Weiterleitung noch zusätzliche Parameter unterzubringen, wie im Beispiel. Gibt man sie nicht an, wird dennoch der QueryString aus dem Request mit angehängt. Mit diesem Konstrukt schafft man sich so schnell einen Endlos-Redirect !

RewriteCond %{QUERY_STRING} param1=[0-9]+
RewriteRule .* http://www.domain.de/ [L]

Obwohl man QSA gar nicht angibt, wird der QueryString angehängt. Eher nicht zu empfehlen.

http://www.domain.de/blabla/?param1=1

leitet weiter auf

http://www.domain.de/?param1=1

und das Regelwerk wird beim nächsten Request von vorne durchlaufen. Wieder matched die Bedingung und leitet weiter und so geht es dahin… Aber hier gibt es einen Trick! Das Anhängen eines Fragezeichens an die Ersetzung der RewriteRule “does the job”:

RewriteCond %{QUERY_STRING} param1=[0-9]+
RewriteRule .* http://www.domain.de/? [L]

Eigenartiger Weise wird das Fragezeichen wohl von ModRewrite verschluckt, denn es ist nach dem Redirect verschwunden! Gut zu wissen.

ModRewrite: Noch ein paar Klassiker

robots.txt überschreiben

Unter der DocumentRoot eines Webverzeichnisses gibt es einige Dateien, die per Konvention einen eindeutigen Namen haben müssen. Typisches SEO-Beispiel ist z.B. die robots.txt. Betreibt man eine Applikation, die mehrere Domains oder Subdomains über dieselbe DocumentRoot bedient, steht einem also zunächst nur eine physikalische robots.txt für alle Domains zur Verfügung. Da kann mod_rewrite helfen.

UseCase: Wir sollen für die Domain www.domain.de und die Subdomains sub1.domain.de und sub2.domain.de… jeweils eine eigene robots.txt ausliefern:

RewriteRule ^robots.txt$ robots/%{HTTP_HOST}-robots.txt

Damit kann man recht komfortabel, nämlich immer noch als Textdatei, die einzelnen “robots.txt”-Dateien der Domain im Unterverzeichnis robots/ der DocumentRoot verwalten.

  1. robots/www.domain.de-robots.txt
  2. robots/sub1.domain.de-robots.txt
  3. robots/sub2.domain.de-robots.txt

Das funktioniert natürlich genauso mit favicon.ico oder XML-Sitemap-Dateien, die ja bekanntlich auch alle in der DocumentRoot liegen müssen.

Richtigen Host erzwingen

RewriteCond %{HTTP_HOST} !^www.domain.de$
RewriteRule ^(.*)$ http://www.domain.de/$1 [QSA,L,R=301]

UserAgent aussperren

Soll Googlebot angeblich ganz wild machen: Googlebot den Zugriff auf das Verzeichnis “nobotshere” sperren:

RewriteCond %{HTTP_USER_AGENT} Googlebot
RewriteCond %{REQUEST_URI} ^/nobotshere/.*
RewriteRule . – [F]

Andere Module

Hier noch ein paar Beispiele von Direktiven anderer Module, die mir in der Praxis mit SEO untergekommen sind und in der .htaccess-Datei verwendet werden können. Beachtet bitte, dass es immer abhängig von der Webserver-Konfiguration ist, ob Direktiven zugelassen sind oder nicht.

Dateikontrolle: FilesMatch (mod_core)

Mit der Direktive “FilesMatch” hat man grundsätzlich Kontrolle über seine Dateien im Webverzeichnis. Dabei sind auch reguläre Ausdrücke zugelassen.

Beispiel: .ini, .cnf, und .conf – Dateien für den Webzugriff sperren:

<FilesMatch “.(ini|co?nf)$”>
deny from all
</FilesMatch>

Performance: Entity Tags setzen (mod_core)

Performance ist ja auch immer wieder mal ein in SEO-Kreisen gern diskutiertes Thema. Wer sich damit einmal schon auseinander gesetzt hat, ist sicherlich früher oder später auf E-Tags gestoßen. Über die htaccess-Datei lassen sie sich recht einfach aktivieren.

FileETag INode MTime Size

Der Direktive FileETag können hier die “Komponenten” mit übergeben werden, die für die Berechnung des ETags herangezogen werden. Hier wird auch die Angabe All unterstützt, was dem Beispiel gleich kommt.

Erwähnenswert ist dabei vielleicht die Verwendung des Wertes von INode: Ein INode repräsentiert die “Nummer” einer Datei in einem Unix Filesystem, der Wert des Inodes ist dabei innerhalb des Filesystems eindeutig. Das Problem entsteht, wenn man dieselbe Datei von unterschiedlichen Servern ausliefern will. Geht der Wert des Inodes in die Berechnung des E-Tags mit ein, weicht der Wert des ETags abhängig vom Server, der es ausgeliefert hat, ab. Je mehr Server man hat, desto kontraproduktiver wird dann der Einsatz des E-Tags. Abhilfe schafft hier, den Inode einfach nicht mit anzugeben oder E-Tags grundsätzlich zu deaktivieren:

FileETag None

Performance: ExpireHeader setzen (mod_expires)

Mindestens so wichtig bei der Performance-Optimierung ist, für Ressourcen den sogenannten Expired-Header zu schicken, falls man sich das leisten kann. Der Wert gibt an, bis wann eine Ressource gültig sein soll. Das ermöglicht Browsern und Proxys die Datei zu cachen. Dazu gibt es mehrere Möglichkeiten. Hier eine Kombination mit der FilesMatch-Direktive:

Expired-Header für “modified-Datum der Datei + 4 Wochen in der Zukunft” schicken, gültig für alle Dateien mit der Endung .css und .js

<filesmatch “.(css|js)$”>
ExpiresActive on
ExpiresDefault “modified plus 4 weeks”
</filesmatch>

Als Zeitangabe kann hier modified oder access angegeben werden, was sich jeweils auf eine Datei bezieht. In der Praxis wäre hier vielleicht auch die Angabe “forever” sinnvoll, was leider nicht unterstützt wird . Wer den Überblick der Dateiendungen seiner JavaScript oder CSS-Dateien verloren hat, kann den ExpiredHeader auf MIME-Type Basis schicken. Der Profi weiß natürlich: Auch die Mime-Types werden anhand der File-Extension abgebildet.

ExpiresActive on
ExpiresByType text/css “access plus 4 weeks”
ExpiresByType application/x-javascript “modified plus 4 weeks”

Damit das funktioniert, muss man genau wissen, welchen MIME-Type der Server schickt, denn das kann schon mal abweichen. Bei Apache sind die MIME-Types und deren zugehörige Extensions per default in einer Datei mime.types abgelegt. Hier finden sich, je nach Version, zum Beispiel für JavaScript-Dateien abweichende Werte wie

application/x-javascript js
text/javascript js

Wer das Glück hat, in seiner htaccess-Datei FileInfos ändern zu dürfen, kann auch den Mime-Type seiner Javascript-Dateien überschreiben, um ganz sicherzugehen:

AddType text/my-javasript .js

Mit AddType lassen sich neue Mime-Types hinzufügen, aber auch schon bestehende Mime-Types überschreiben: Im Beispiel wird die vorkonfigurierte Dateiendung .js mi dem Typ text/my-javascript überschrieben. Das es funktioniert, sieht man, wenn jetzt eine Datei mit der Endung .js über Browser aufruft: Dieser hat erwartungsgemäß das Problem, den eigenartigen Mime-Type zu interpretieren und reagiert darauf mit der Frage, ob die Datei heruntergeladen werden soll o.ä.

Redirects (mod_alias)

Auch andere Module von Apache stellen die Möglichkeit zur Verfügung, Redirects durchzuführen. Hier gibt es, vermutlich ebenfalls historisch bedingt, gleich 4 sehr ähnliche Direktiven aus dem Modul mod_alias mit den Formaten:

  • Redirect [status] URL-path URL
  • RedirectPermanent URL-path URL
  • RedirectTemp URL-path URL
  • RedirectMatch [status] regex URL

Eigentlich braucht man nur RedirectMatch, denn man kann hier den Statuscode (301 / 302 / 303 / 410) übergeben und es wird auch ein regulärer Ausdruck als Pattern für den Requests unterstützt. Es können hier auch Backreferences verwendet werden.

RedirectMatch .*oldscript.* http://www.new-domain.de/

Das funktioniert bestens, wenn zum Beispiel in der DocumentRoot einer Domain www.old-domain.de ein Skript namens oldscript.php und eine htaccess-Datei mit dieser Anweisung residiert. http://www.old-domain.de/oldscript.php leitet dann weiter auf http://www.new-domain.de/. So weit so gut.

Trotzdem kann man mit dieser Direktive so seine Probleme bekommen. Gehen wir davon aus, in der DocumentRoot der alten Domain www.old-domain.de gibt es auch noch eine CSS-Datei namens lib.css. Mit dem beschriebenen RedirectMatch versucht man nun z.B. auch http://www.old-domain.de/lib/oldscript.php weiterzuleiten und dürfte sich, unter gewissen Umständen, gewaltig wundern.

Not Found
The requested URL /lib/oldscript.php was not found on this server.

Der Server antwortet mit 404. Sonderbar: (.*oldscript.*) hat doch eben noch gematched!

Content Negotiation heißt hier das Zauberwort und jetzt, liebe Freunde der Suchmaschinen-Optimierung, wird es wirklich ein wenig hässlich. Der Apache stellt nämlich ein Feature zur Verfügung, dass es ermöglicht Dateien auch ohne Angabe der Dateiendung zu finden, so eine Art Autocompleter für Dateien. Man spricht hier auch von MultiViews. Das kann zum Beispiel sehr nützlich sein, wenn man eine IndexDatei, z.B. index.html, abhängig von der vom Browser im Request mitgeschickten Spracheinstellung, auf unterschiedliche Sprachversionen abbilden will, wie z.B. auf index.html.es, index.html.en oder index.html.fr usw.

Aber es kann eben auch zu unerwarteten Ergebnissen führen. Voraussetzung dafür ist, dass das Feature aktiviert und freigegeben ist, was aber i.d.R. der Default ist. Beim Aufruf von http://www.old-domain.de/lib/oldscript.php hat man vielleicht folgendes im Logfile stehen.

Negotiation: discovered file(s) matching request: /path/to/htdocs/lib (None could be negotiated).

Das Problem hat man übrigens auch, wenn man versucht, dies mit mod_rewrite zu lösen.

RewriteRule (.*oldscript.*) http://www.new-domain.de/

Damit kann man http://www.old-domain.de/oldscript.php noch auf http://www.new-domain.de/ weiterleiten. Unter den oben beschriebenen Bedingungen aber (MultiViews an, lib.css existiert), funktioniert die Weiterleitung von http://www.old-domain.de/lib/oldscript.php aber eben auch nicht mehr, es hagelt einen 404 Fehler. Folgendes könnte im RewriteLog stehen:

.. [perdir /path/to/htdocs/] add path info postfix: /path/to/htdocs/lib.css -> /path/to/htdocs/lib.css/oldscript.php
….

Bevor ModRewrite überhaupt zum Zuge kommt, schlägt MultiViews zu und ergänzt bereits /lib zu /lib.css. Damit kann ModRewrite natürlich nur zum falschen Ergebnis kommen.

Wer schon solche “Phänomene” festgestellt hat, dem kann vielleicht folgende Anweisung helfen:

Options -multiviews

Damit wird MultiViews deaktiviert und schon funktioniert auch der Redirect, auch wenn es keine Datei oldscript.php auf dem Server gibt. Voraussetzung dafür ist allerdings auch wieder, dass er dieses Feature in der .htaccess auch überschreiben darf.

Wer nicht so genau weiß, ob er MultiViews aktiviert hat, kann das leicht testen. Wie beschrieben, eine lib.css im Webverzeichnis abgelegt und dann über Browser www.domain.de/lib anfordern. Ist MultiViews aktiviert, sollte der Inhalt von lib.css ausgegeben werden, anderenfalls antwortet der Server “404 Not found” (falls es nicht auch noch ein Verzeichnis /lib gibt).

Der QueryString bei RedirectMatch

Auch bei der Direktive RedirectMatch lässt sich offensichtlich die Weitergabe des Querystrings durch Angabe eines Fragezeichens unterdrücken.

RedirectMatch .*oldscript.* http://www.new-domain.de/?

www.old-domain.de/oldscript.php?foo=bar
leitet dann weiter auf
www.new-domain.de/?
Bemerkenswerter Weise bleibt das Fragezeichen aber hier stehen. Nicht so schön und aus SEO-Sicht ja wohl eher unerwünscht. ;-)

Fehler-Dokument festlegen

Last not least noch was einfaches, aber sehr effizientes. Wenn ihr zum Beispiel komplexere Alt-URLs auf neue, schöne weiterleiten müsst oder euch nicht mit endlosen htaccess-Weiterleitungen rumschlagen wollt, zieht folgende Möglichkeit in Betracht:

ErrorDocument 404 /404.php

Damit kann man ein Skript, hier 404.php, als ErrorHandler definieren. Wenn der Server eine Anfrage nicht bedienen kann und deswegen mit einem Statuscode “404 NOT FOUND” antworten will, wird dadurch die Kontrolle an das Skript deligiert. Die Ausgabe des ResponseHeaders und damit auch des StatusCodes wird dabei dem Programm überlassen. Im Script ist es dann wiederum leicht möglich, den Request mit einem anderen Statuscode weiterzuleiten. z.B. 301.

Damit habt ihr deutlich mehr Möglichkeiten, den eingehenden Request zu zerpflücken und mit komplexen Logiken etc. für die Weiterleitung zu versehen. Falls keine Weiterleitung ermittelt werden kann, sollte das Script aber dafür sorgen, den “richtigen” Statuscode 404 zurückzuliefern. Sonst kommt es zu sog. “Soft 404-Fehlern”, wie Google es nennt.

<?php
// Request analysieren und redirect-Ziel ermitteln
// …
if ($redirect) {
header (“HTTP/1.1 301 Moved Permanently”);
header(“Location: $location”);
// …
}
header (“HTTP/1.1 404 Not Found”);
// Content der 404-Seite ausgeben:
// …
?>

Links

GD Star Rating
loading...
Die .htaccess-Datei: Alles was man als SEO wissen muss, 4.3 out of 5 based on 32 ratings

Hubert Ried

Hubert Ried ist Technical SEO der Verlagsgruppe Weltbild GmbH.

More Posts - Website

Bookmark and Share
8 comments
Thomas K.
Thomas K.

Toller Beitrag Hubert! VG Thomas

Carsten Appel
Carsten Appel

Vielen Dank für den tollen Beitrag zur htaccess aus SEO Sicht. So etwas hatte ich schon lange gesucht.

Ralf Klein
Ralf Klein

Gute Sache !!! Endlich mal auf den Punkt gebracht!

Norbert
Norbert

Sehr übersichtliche und verständliche Zusammenfassung. Vielen Dank dafür!

Nedim Sabic
Nedim Sabic

Wer dies von A bis Z liest ist ein mod_freak :) ps.klasse "Zusammenfassung" ;)

Bella
Bella

wow, danke, hab ich gleich mal abgespeichert. Allerdings habe ich auch schon mit den besten Anleitungen richtig lustige Sachen bei Weiterleitungen erlebt, meist mach ich Trial-and-Error bis ich umfalle oder einen Schreikrampf kriege (eine .htaccess die auf Server 1 wunderbar funktioniert, auf Server 2 kommt ein 500er, da wirste narrisch). Aber danke für die Anleitung und die Mühe.

Trackbacks