Christian Seiler: Christians Servertipps #1: PHP in Apache einbinden

Hallo Forum,

Ich habe gerade eben beschlossen, das Forum als Blog zu missbrauchen. ;-) Hintergrund ist der, dass ich in letzter Zeit einige nette Tipps zum Thema Serverkonfiguration gesammelt habe, von denen ich meine, dass sie zum einen sehr nützlich sein könnten und zum anderen ich sie in der Form an noch keiner anderen Stelle gelesen habe (was natürlich nicht heißen soll, das). Ich habe mir überlegt, ob ich nicht lieber das SELFHTML Weblog dafür verwenden soll, allerdings passen diese thematisch in meinen Augen nicht ganz so sehr hinein, daher nehme ich das Forum, da hier doch einige mitlesen, die sich dafür interessieren.

Mein Tipp #1 bezieht sich auf die Einrichtung von PHP unter dem Apache-Webserver.

Einleitung

Es werden primär zwei verschiedene Möglichkeiten vorgestellt, PHP in den Apache einzubinden: Als CGI und als Apache-Modul, das als Handler für PHP-Dateien fungiert (auf die Methode, PHP als Filter in Apache 2 einzurichten wird hier nicht eingegangen, ebensowenig auf Apache 1.3). Dieses Posting soll einen Licht auf einen Teilaspekt werfen, der fast immer unterschlagen wird.

Einbinden von PHP

Doch zuerst die unterschiedlichen Einbindungsmethoden. Zunächst als CGI:

AddHandler php5-script .php  
Action php5-script /cgi-bin/php

(Wenn der PHP-Interpreter unter /cgi-bin/php erreichbar ist.)

Und nun als Modul:

LoadModule php5_module modules/libphp5.so  
AddType application/x-httpd-php .php

Was nirgends wirklich gut dokumentiert ist, ist, dass beide Methoden sich außer in der Einbindung in CGI / Modul weiter unterscheiden:

* Bei der obigen CGI-Anleitung wird PHP als Handler für Dateien mit der
   Endung .php registriert.
 * Bei der Modul-Anleitung erkennt PHP, dass es die Datei verarbeiten soll,
   an Hand des Content-Types.

Dies führt zu unterschiedlichem Verhalten in einigen subtilen Situationen!

Hintergrund ist folgender: Der Apache verarbeitet requests so, dass er zuerst bestimmt, welcher a) Handler für die Datei zuständig ist und b) welchen Content-Type die Datei besitzt. Die subtilen Unterschiede kommen nun an zwei Stellen zu tragen: 1) Bei multiplen Dateiendungen und 2) bei Multiviews (ist NICHT das gleiche!).

I. Dateien können multiple Endungen besitzen und der Apache (genauergesagt mod_mime) versucht alle Endungen zu interpretieren. Dies hat noch nichts mit MultiViews zu tun. Zu jeder Endung können sowohl Typ (Content-Typ) als auch Handler registriert sein.

Betrachtet man nun eine Datei wie test.php.txt. Diese besitzt 2 Endungen: '.php' und '.txt'. Wenn der Apache nun versucht zu bestimmen, welchen Content-Type und welcher Handler zur Datei gehört.

Gut, für '.txt' ist in der Default-Konfiguration immer 'text/plain' als Content-Type eingestellt (d.h. es ist so, als ob ein AddType text/plain .txt in der Apache-Konfiguration stünde). Ein Handler wird für '.txt' nicht definiert.

Fall A: AddHandler php5-script .php

-> Handler = php5-script,   Content-Type = text/plain
 -> Dies führt dazu, dass die Datei 'test.php.txt' von PHP verarbeitet wird
    (welches dann evtl. den Content-Type auch ändert), weil der Handler
    auf 'php5-script' gesetzt ist und dann der Content-Type erst einmal
    irrelevant ist.

Fall B: AddType application/x-httpd-php .php

-> Handler = none,          Content-Type = text/plain
 -> Dies führt dazu, dass die Datei 'test.php.txt' als Textdatei
    ausgeliefert wird, da kein Handler definiert wurde und der
    Content-Type 'text/plain' ist.

Zu beachten ist, dass dies IMMER geschieht, UNABHÄNGIG von einer etwaigen Einstellung von Mutliviews (da dies NICHTS mit Multiviews zu tun hat!).

II. Multiviews

Multiviews sind eine Möglichkeit, multiple Versionen einer Resource anzubieten, von denen die passendste herausgesucht wird. Dies wird mit dem Browser ausgehandelt. Wenn man also z.B. zwei Bilder 'test.jpg' und 'test.png' anbietet, dann wird beim Aufruf von 'test' (ohne Endung) das zurückgeliefert, was der Browser am besten versteht.

Multiviews werden aber auch gerne verwendet, falls nur eine Datei existiert, man die Endung aber nicht anzeigen lassen will (sprich: es gibt nur 'test.html', man will aber nur auf 'test' zugreifen).

Browser senden in der Regel an den Server, dass sie text/html interpretieren können. Manche Browser senden zusätzlich noch weitere Typen und einige senden auch noch */* - d.h. dass sie im Zweifel alle Dateitypen akzeptieren. Letzteres wird jedoch nicht von allen Browsern gemacht und von vielen Suchmaschinen auch nicht.

Nehmen wir jetzt den Fall, dass eine Datei 'test.php' bei aktivierten Multiviews mit 'test' aufgerufen wird (d.h. ohne Endung). Dann gibt es drei Fälle:

Fall A:
AddHandler php5-script .php
(Ohne AddType für php)

Multiviews kennt den Dateityp von test.php nicht und weil er unbekannt ist, wird eine 404-Fehlerseite ausgegeben.

Als Ausweg kann man hier noch

MultiviewsMatch Handlers

definieren, damit Multiviews auch Endungen einbezieht, für die nur Handler, aber keine Typen definiert wurden. Dann verhält sich das wie Fall B.

Fall B:

AddHandler php5-script .php  
AddType text/html .php

Dies führt dazu, dass die PHP-Seite beim Zugriff auf 'test' korrekt angezeigt wird. Die AddType-Zeile gaukelt mod_negotiation (das Multiviews macht) vor, dass .php-Dateien vom Typ text/html wären und somit bei der Anfrage eines Clients, der nur text/html akzeptiert, ausgeliefert werden können. Da der Handler aber für das Auslieferungsverhalten sorgt, wird die Datei weiterhin von PHP verarbeitet.

Fall C:
AddType application/x-httpd-php

Hier gilt zu unterscheiden, ob der Client */* akzeptiert oder nicht. Wenn er */* akzeptiert, dann wird die PHP-Seite weiterhin ausgeliefert, weil Multiviews davon ausgeht, dass der Client auch application/x-httpd-php (was als MIME-Type für PHP angegeben wurde) akzeptieren wird. Dass PHP in einem weiteren Verarbeitungsschritt dann den Content-Type ändert, weiß mod_negotiation nicht und es ist in dem Moment auch egal.

Wenn der Client jedoch NICHT */* akzeptiert (was viele Suchmaschinen tun), dann wird die PHP-Seite NICHT ausgeliefert und eine Meldung 406 Not Acceptable wird ausgeliefert, von der man aus dann auf die PHP-Datei selbst weitergeleitet wird. Dies geschieht, weil der Apache davon ausgeht, dass der Client application/x-httpd-php nicht akzeptieren wird (der Apache kann ja nicht wissen, dass PHP den Content-Type später ändern wird) und damit sagt "Sorry, kann Dir nichts passendes anbieten".

Da insbesondere Suchmaschinen unter dieses Verhalten fallen, ist es nicht ratsam, Multiviews mit PHP und AddType zusammen einzusetzen.

Zusammenfassung bes bisherigen

Wie man hier sehr schön sehen kann, führen unterschiedliche Arten der Einbindung zu subtilen Änderungen im Verhalten des Apache bei der Auslieferung von Seiten.

Daher ist bei der Konfiguration zu beachten, welches Verhalten erwünscht ist.

Allerdings muss man sic nicht zwischen CGI- und Modulversion entscheiden - denn man kann SOWOHL die Modul-Version über einen Handler als auch die CGI-Version über einen Content-Type einbinden.

A. Modul-Version über einen Handler und nicht über einen Content-Type:

Die Modul-Version definiert (OHNE eine Action-Zeile!) implizit einen Handler 'php5-script' (auf Grund der Action-Zeile kann man bei der CGI-Variante den Handler übrigens anders benennen, das geht bei der Modulversion NICHT!). Daher ist es einfach möglich, per:

AddHandler php5-script .php  
AddType text/html .php

die Modulversion über Handler und nicht über Content-Type einzubinden. Dies wird auch in der PHP-Installations-FAQ empfohlen, wenn man MultiViews einsetzen will - und der Empfehlung kann ich mich anschließen.

B. CGI-Version über einen Content-Type und nicht über einen Handler definieren:

Dies ist meines Wissens noch _nirgends_ dokumentiert. Es ist nämlich problemlos möglich, PHP als CGI auch so einzubinden:

AddType application/x-httpd-php .php  
Action application/x-httpd-php /cgi-bin/php

(Sofern, wie bereits oben erwähnt, der PHP-Interpreter unter /cgi-bin/php erreichbar ist.)

Der Hintergrund ist folgender: Wenn KEIN Handler für eine Resource definiert ist, setzt Apache intern den Handler auf den Content-Type. Damit funktioniert die obige Action-Zeile, obwohl nirgends ein Handler definiert wurde.

Hinweis: Dies ist bei Apache 2 der Fall, bei Apache 1 ist es sehr gut möglich, dass die Methode nicht funktioniert.

Hinweise zu FastCGI und SuPHP:

a) PHP als FastCGI wird zwar deutlich aufwändiger eingebunden, als PHP als normales CGI, allerdings bleibt die Action-Zeile identisch, weswegen sich für diesen Artikel nichts ändert.

b) mod_suphp funktioniert offensichtlich gar nicht mit Multiviews und bereitet zusätzliche Probleme (die PHP-Dateien werden zum Download angeboten) - siehe auch diesen Archivthread. Da mod_suphp außerdem vollkommen andere Konfigurationssemantiken besitzt, wird hier nicht weiter darauf eingegangen.

Fazit

Fast alle Anleitungen zum Einbinden von PHP unterschlagen diese subtilen Unterschiede. Ich hoffe, ich konnte mit diesem Posting dazu beitragen, etwas Licht auf diese eher vernachlässigte Stelle der Apache-Konfiguration zu werfen.

Viele Grüße,
Christian

  1. Ich habe mir überlegt, ob ich nicht lieber das SELFHTML Weblog dafür verwenden soll, allerdings passen diese thematisch in meinen Augen nicht ganz so sehr hinein, daher nehme ich das Forum, da hier doch einige mitlesen, die sich dafür interessieren.

    Falls sich noch ein paar Leute an diesen Thread dranhängen (*hint!*), könnten wir doch ganz prima eine Forumsauslese damit machen - so wie neulich zum ersten Mal als »Selfforumssieb« gemacht.

    Allein dein Initialposting ist sicherlich schon wertvoll genug (Danke!), wobei zu einer Auslese wohl gehören sollte, dass das Thema auch von anderen Seiten weiter zerpflückt oder ergänzt wird.

    Viele Grüße!
    _ds

    --
    »Sommer ziehen langsamer vorbei, wenn man sie nur oft genug mit Sandal/Gentleman "Isyankar" oder dem Buena Vista Social Club penetriert.«
    - kommirnichmitkation
    1. Hallo Schuer,

      Falls sich noch ein paar Leute an diesen Thread dranhängen (*hint!*), könnten wir doch ganz prima eine Forumsauslese damit machen - so wie neulich zum ersten Mal als »Selfforumssieb« gemacht.

      Von mir aus absolut keine Einwände - nur weiß ich wie gesagt nicht, ob das nicht etwas zu "technisch" für's Blog ist, der Fokus des Blogs eher auf Webentwicklern als auf Administratoren liegt, daher hab ich's erstmal im Forum gepostet.

      Nachdem ich noch einiges mehr im Hinterkopf habe, was ich schon immer mal posten wollte, könnten wir es ja so machen, dass ich hier im Forum in mehr oder weniger regelmäßigen Abständen "Christians Servertipps" poste und falls sich da eine interessante Diskussion ergibt, die auch für Entwickler und weniger für Administratoren relevant sein könnte, dass die dann im Weblog nochmal aufgegriffen wird? Das hätte außerdem den Vorteil, dass wir hier im Forum auch mal Dinge diskutieren, die eher in Richtung "bleeding edge" gehen.

      Viele Grüße,
      Christian

      1. könnten wir es ja so machen, dass ich hier im Forum in mehr oder weniger regelmäßigen Abständen "Christians Servertipps" poste und falls sich da eine interessante Diskussion ergibt, die auch für Entwickler und weniger für Administratoren relevant sein könnte, dass die dann im Weblog nochmal aufgegriffen wird?

        Absolutely. Ich fänd's ganz großartig!

        Viele Grüße!
        _ds

        --
        »Wenn ich groß bin, möchte ich Tandem-und-Tridem-Wannenkipperfahrer werden.«
        - kommirnichmitkation
  2. hallo Christian,

    interessanter Vorschlag. Es mag nun ein bißchen dämlich klingen, aber über _genau diese_ Dinge war ich bereits vor einiger Zeit gestolpert und hatte sie eigentlich in https://redaktion.selfhtml.org/export/4635/selfhtml/branches/selfhtml-8.1/servercgi/server/perlphp.htm#php auch ausführen wollen.
    Formuliere das, was du da jetzt angedacht hast, doch an dieser Stelle aus. Ich kanns ja nicht mehr - und will auch nicht mehr.

    Grüße aus Berlin

    Christoph S.

    --
    Visitenkarte
    ss:| zu:) ls:& fo:) va:) sh:| rl:|