Apache 2, Zeichensätze und Direktiven

Gestern hielt mich ein kleines Problemchen mit der Zeichensatzauswahl auf Apache Server unter SLES 9 auf.

Problembeschreibung

Mehrere von meiner Firma entwickelte Webseiten waren per Metatag-?nweisung vom ISO-8859-1 Zeichensatz

<meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1″>

auf UTF-8 umgestellt:

<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>

Bei den anschließenden Test mit einem Browser auf dem Server selbst sahen die Webseiten OK aus. Umlaute wurden korrekt dargestellt. Chaotisch wurde es jedoch, als wir die Seiten auf einem separaten Client-PC betrachten wollten. Alle Umlaute waren falsch. Zwar half eine explizite Umstellung des zu wählenden Zeichensatzes im Browser – aber eigentlich wünscht man sich hier eine automatische, richtige Wahl.

Ein Test derselben Web-Seiten auf einem anderen Apache-Server ergab dagegen gute Ergebnisse. Die Sache hatte also nichts mit Browser-Einstellungen oder anderen Client-Problemen zu tun.

Response Header

Weiter kommt man in einem solchen Fall zunächst dadurch, indem man sich ansieht, was der Server dem Browser nach der Seitenanfrage eigentlich so übermittelt. Den sog. “Response Header” kann man sich u.a. mit Funktionen des Firefox-Browsers ansehen. Die notwendige Angaben erhält man im aktuellen FF über den Menüpunkt:

“Extras” -> “Web Developer” -> “Information” -> “View Response Headers”.

Hier kam dann tatsächlich die Angabe, dass als Zeichensatz ISO-8859-1 gelten soll – und zwar über die Header-Zeile:

Content-Type: text/html; charset=iso-8859-1

Beim dem anderen getesteten Server, mit dem der Browser “richtig” zusammenarbeitete, war im Response Header dagegen gar keine Vorgabe zum Zeichensatz eingetragen. Der Befund, dass die Schwierigkeiten mit Servereinstellungen zu tun hatten, bestätigten sich also.

Leider sind die Einstellungen zu Zeichensätzen für den Apache2 in unterschiedlichen Linux-Distributionen auch in unterschiedlichen Dateien untergebracht. Ein wenig Suchen nach Begriffen wie “Charset” oder “ISO-8859” in den Dateien der Apache2-Konfigurationsdateien ist im Einszelfall daher ggf. notwendig.

Zeichensatzvorgaben auf dem Apache unter SLES 9

Bei SLES9 wird man in der Datei

/etc/apache2/mod_mime-defaults.conf

fündig. Auf anderen Servern können die Einstellungen aber auch in einer Datei “/etc/apache2/conf.d/charset” vorgegeben sein!

Dort werden nach Language-Festlegungen Vorgaben für Zeichensätze in der Form

AddCharset ISO-8859-1 .iso8859-1 .latin1
AddCharset utf-8 .utf8

getroffen. Wegen der Zeile für utf-8 wundert man sich vielleicht, wieso dann im obigen Beispiel im Response Header die Vorgabe ISO-8859-1 auftaucht. Der Grund ist/war auf dem “problematischen” Server die zusätzliche Zeile

AddDefaultCharset ISO-8859-1.

Ein testweises Auskommentieren und Neustarten des Apache2-Servers zeigte dann tatsächlich, dass diese Zeile die Ursache für die “Probleme” auf dem Browser bzw. die ISO-8859-1 Zeile im Response Header darstellte.

An dieser Stelle ist nun aber ein wenig Kontemplation gefragt, denn ein generelles Auskommentieren kann ja ggf. auch ein Sicherheitsrisiko darstellen. Und was macht man, wenn man Einstellungen auf einem Server treffen muss, über dessen Apache-Konfigurationsdateien man keinen Einfluss hat? Dies ist ja die typische Situation bei Hostern. Zudem fragt man sich, wie man
vorgehen muss, wenn man auf folgende Situationen trifft:

a) Man benötigt für die Dateien in ganz bestimmten definierten Verzeichnissen Charset-Vorgaben, die dem Default nicht entsprechen.

b) Man hat einen Mix aus Seiten mit unterschiedlichen Charset-Festlegungen im gleichen Verzeichnis.

Ich versuche weiter unten, nach einem Blick in die Apache-Konfiguration meinen “best guess” abzugeben.

Was sagt die Apache-Doku ?

In der Apache Dokumentation auf der Webseite “http://httpd.apache.org/docs/2.0/mod/core.html” finden sich u.a. folgende Hinweise (die Hervorhebungen habe ich vorgenommen)

Description: Default charset parameter to be added when a response content-type is text/plain or text/html
Syntax: AddDefaultCharset On|Off|charset
Default: AddDefaultCharset Off
Context: server config, virtual host, directory, .htaccess
Override: FileInfo
Status: Core
Module:coreThis directive specifies a default value for the media type charset parameter (the name of a character encoding) to be added to a response if and only if the response’s content-type is either text/plain or text/html.

This should override any charset specified in the body of the response via a META element, though the exact behavior is often dependent on the user’s client configuration.

A setting of AddDefaultCharset Off disables this functionality. AddDefaultCharset On enables a default charset of iso-8859-1. Any other value is assumed to be the charset to be used, which should be one of the IANA registered charset values for use in MIME media types. For example:

AddDefaultCharset utf-8

AddDefaultCharset should only be used when all of the text resources to which it applies are known to be in that character encoding and it is too inconvenient to label their charset individually.

One such example is to add the charset parameter to resources containing generated content, such as legacy CGI scripts, that might be vulnerable to cross-site scripting attacks due to user-provided data being included in the output. Note, however, that a better solution is to just fix (or delete) those scripts, since setting a default charset does not protect users that have enabled the “auto-detect character encoding” feature on their browser.

Einige ergänzende Kommentare:

Ist die Direktive AddDefaultCharset gesetzt, so fügt Apache Zeichensatzinfos zum Content-Type-Header von HTTP-Antworten hinzu. Z.B.:

Content-Type: text/html; charset=iso-8859-1

Ist eine solche Angabe vorhanden, so übersteuert das bei den meisten Browsern eine evtl. vorhandene Meta-Tag-Angabe zu Zeichensätzen in der empfangenen HTML-Datei. (Dies war ja gerade die Ursache des oben beschriebenen Problems).

Man beachte, dass man lt. der obigen Beschreibung ein solches Verhalten auch durch

AddDefaultCharset Off

explizit abschalten kann.

Ferner kann man – soweit sonstige Einstellungen das zulassen – die Direktive “AddDefaultCharset” auch zusammen mit “.htaccess”-Dateien, also verzeichnisspezifisch, einsetzen.

Lösungsansätze für die Zeichensatzsteuerung

Auf den von mir selbst verwalteten Servern setze ich grundsätzlich eine serverweite Einstellung ein – in meinem Fall ISO-8859-1 (per AddDefaultCharset ISO-8859-1 ) in den globalen Apache-Einstellungen (bei SLES 9 in /etc/apache2/mod_mime-defaults.conf).

Ein Grund hierfür sind die Sicherheitsbedenken, die in der Apache-Doku bzgl. des
Abschaltens anklingen und auf die man in diversen Internet-Artikeln treffen kann.

Virtuelle Domainen mit spezifischen Default Zeichensätzen

Für größere Kunden-Projekte (Webseiten-Entwicklung) lege ich mir zumeist separate virtuelle Domainen an. (Das erleichtert später den Aufruf der Seiten beim Testen, macht die Umgebung kontrollierbarer und spiegelt schon in der Entwicklungsphase mehr die Realität der Kundensicht wieder.) Habe ich eine durchgehend einheitliche Zeichensatzverwendung für die Webseiten der virtuellen Domaine zu erwarten, setze ich den Default-Zeichensatz in der Konfiguration der virtuellen Domaine per “AddDefaultCharset” auf den entsprechenden Wert.

Einsatz von “.htaccess”-Dateien für verzeichnisspezifische Festlegungen

Muss ich mit unterschiedlichen Zeichensätzen der zu erstellenden Webseiten rechnen, so versuche ich eine Strategie zu fahren, bei der man die Dateien mit jeweils gleichen Zeichensätzen in Verzeichnissen bündelt. Dann kann man nämlich zum “.htaccess”-Mechanismus greifen und die verzeichnisspezifischen Einstellungen über die Festlegungen in zugehörigen “.htaccess”-Dateien treffen. Alternativ kann man auf Servern, die der eigenen Kontrolle unterstehen, aber auch über gezielte Direktiven in <Directory>-Abschnitten zur jeweiligen Domaine vorgehen.

Über den Einsatz von “.htaccess”-Dateien erschlägt man übrigens evtl. Probleme bei den meisten Providern – soweit sie den “.htaccess”-Mechanismus überhaupt zulassen.

Verzeichnisspezifisches Abschalten des Default Zeichensatzes kann u.U. eine Möglichkeit sein

Hat man einen Mix aus Dateien mit unterschiedlichen Zeichensätzen in ein und demselben Verzeichnis einer (virtuellen) Domaine zu erwarten, so schalte man entweder die Zeichensatzvorgabe für die gesamte Domaine oder das Verzeichnis ab (“AddDefaultCharset Off”). In letzterem Fall natürlich wieder über eine “.htaccess”-Datei.

Der gezielte Einsatz von Dateiendungen

Will man zu keinem der oben genannten Mittel greifen, so kann man immer noch über Dateiendungen arbeiten. Die Zusatzangaben zu Datei-Endungen in den Direktiven “AddCharset” legen nämlich fest, auf welche Dateien der Zeichensatz angewendet werden soll.

AddCharset utf-8   .utf8

Eine betroffene Datei “test.html” muss dann halt in “test.html.utf8” umbenannt werden. Nach gleichem Muster verfährt man für Dateien mit anderen Zeichensätzen – also: Datei-Endung für den Zeichensatz vorgeben und hinten an den Dateinamen anhängen.

Links und weitere Informationen

Grundsätzliches :
http://httpd.apache.org/docs/2.0/mod/core.html

Generelle Möglichkeiten, die Zeichensatz-Regeln des Apache-Servers für spezielle Verzeichnisse oder Files anzupassen, werden unter folgenden Links diskutiert:
http://www.w3.org/International/questions/qa-htaccess-charset
http://www.linuxforen.de/forums/showthread.php?t=229702

Das Vorgehen zur konsequente Umstellung eines LAMP Servers auf UTF-8 kann man z.B. unter folgenden Links nachlesen:
http://wiki.hetzner.de/index.php/Utf-8
http://www.xaranetblog.de/page/5/

Vertikales Zentrieren einer Webseite

Manchmal hat man es mit Kunden zu tun, die ihre Webseite unbedingt vertikal zentriert in ihrem Browserfenster dargestellt haben wollen. Dafür gibt es mehrere Lösungen – u.a. den Einsatz von Framesets und/oder von Tabellen. Frameset-Lösungen und reine Tabellenlösungen lassen sich vollständig W3C-konform erstellen.

Nicht jeder mag aber Framesets. Und bei reinen Tabellenlösungen gibt es Probleme, wenn man als DTD aus irgendwelchen Gründen das “http://www.w3.org/TR/html4/loose.dtd”-Dokument laden will oder muss.

Nachfolgend daher mal eine etwas andere Lösung, die allerdings nicht standardkonform ist und auf der Zuweisung einer Style-Vorgabe zum HTML-Tag basiert. Die Lösung funktioniert in vielen relativ neueren Browsern – u.a. FF 3.x, MS IE 7,8, Konqueror 4.3.x, Opera 9.64. Sie ist ferner zusammen mit dem oben genannten DTD funktionsfähig.

Folgende grundsätzliche CSS-Vorgaben sind erforderlich:

html { height: 100%; width: 100%; margin: 0px; padding: 0px; overflow:hidden; }
body { height: 100%; width: 100%; margin: 0px; padding: 0px; font-family: Arial, Helvetica, sans-serif; font-size:10px; overflow:hidden; }
table { table-layout:fixed; position:relative; border-collapse:collapse; border-width:0px; }

Nicht standardkonform ist hier die erste Zeile. Sie sorgt aber dafür, dass nachfolgende 100%-Dimensionierungen sich weitgehend an den Dimensionen des Browser-Fensters orientieren. Es gibt kleine Abweichungen bei der Zentrierung – die sind aber so klein, das man damit aus meiner Sicht in der Praxis leben kann.

Den weiteren Aufbau verdeutlich folgender HTML-Code (, dessen CSS-Anteile man natürlich auslagern kann und sollte. Für erste Tests finde ich es aber immer praktisch, direkt am Objekt zu arbeiten):

<body>
  <div style=”width:100%; height:100%; border:0px #FF0000 solid; overflow:auto;”>
   <table style=”height:98%; width:98%; table-layout:fixed; margin:0px;” border=”0″>
    <tr>
     <td style=”width:100%; height:100%; text-align:center;”>
      <div style=”background-color:#FC0; border:#00C 0px solid; width:40.0em; height:40.0em; margin:auto; line-height:40.0em;”>Hallo</div>
     </td>
    </tr>
   </table>
  </div>
</body>

Das der Tabelle vorgeschaltete DIV sorgt für eine automatische Bereitstellung von Scrollbars, wenn das Browserfenster kleiner als der in der Tabellenzelle dargestellte Inhalt wird. Die 98% bei der Dimensionierung der Tabelle unterdrücken ein initiales Anschalten von Scrollbalken in manchen Browsern. Das innere DIV dient nur der Darstellung eines repräsentativen Objekts. Es könnte aber den gesamten geplanten Seiten-Inhalt aufnehmen.

Die Lösung ist sicher nichts für Puristen, die ihren Seiten ein W3C-Logo nach bestandenen Validator-Tests anfügen wollen. Aber wie gesagt, sie funktioniert in vielen Browsern und zusammen mit den “loose.dtd” – Deklarationen.

Schemata und Web-Applikationen

Wegen aktueller Erfahrungen bei einem Kunden möchte ich ein Thema ansprechen, das man nicht oft genug betonen kann :

Wenn man eigene Web-Services wie z.B. Formulardienste oder Pflegeapplikationen mit PHP und dabei auch noch objektorientiert entwickelt, dann lohnt es sich unbedingt, hierbei so vorzugehen, dass die

  • grundlegenden Datenbank-Strukturen (Tabellen, Felddefinitionen, Master-Detail-Relationen),
  • essentielle Elemente der Datenprüfung/-Validierung,
  • Objekte für die Repräsentation von Webformularen,
  • Objekte der Datenbankinteraktions-Schicht und
  • die darüber liegenden Pflegeapplikationen

auf der Basis von sog. zentralen “Datenschemata” quasi generiert werden. Dabei kann man sogar Vorgaben für die Repräsentation von Master-Detail-Relationen integrieren.

Natürlich ist der Aufbau entsprechender eigener Frameworks mühsam – aber es lohnt sich nach unserer Erfahrung schon ab dem zweiten Projekt.

Die zentrale Idee ist die, dass man sich ein “Framework” so aufbaut, dass durch die programmtechnische Auswertung von vorgegebenen “Datenfeld-” und “Validierungs-” Beschreibungen (“Schema”)

  • sowohl die benötigten Datenbank-Tabellen generiert
  • als auch zugehörige Pflegeapplikationen in einem “Rutsch” (durch reines Kopieren von Dateien) bereitgestellt werden können.

Die entsprechenden Objekte müssen so “intelligent” sein, dass sie aus der vorgegebenen Datenstruktur-Information des “Schemas” die richtigen Schlüsse ziehen : D.h., dass sie die benötigten SQL-Statements und Validierungsstatements innerhalb der Pflege- und Applikationslogik und die zugehörigen Web-Formulare dynamisch aufbauen, wobei Sie immer gleiche Klassendefinitionen heranziehen.

Salopp ausgedrückt folgt man dabei der Devise: “Gib mir deine Datenstruktur und ich gebe dir eine erste komplette Pflegeapplikation auf der Basis eines universellen Model-View-Controllers und einer universellen Datenbank-Interaktionsschicht an die Hand.” PHP ist für den Aufbau solcher Frameworks aus unserer Sicht seit PHP5.2 geradezu ideal geeignet.

Baut man die Pflegefunktionalität selbst modular in Funktionalklassen auf, so verschafft man sich gleichzeitig die Basis für komplexere und spezialiserte Business-Applikationen, denen man dann Schema-Objekte übergibt. Aus der Schema-Information werden dann nach Bedarf automatisch Objekte für die Repräsentation einzelner Datensätze oder ganzer Listen von Datensätzen generiert. Die Interaktion mit der Datenbank tritt dann für die Business-Logik in der Regel fast völlig in den Hintergrund.

Bei Erweiterungen um Felder ändert man primär das entsprechende “Schema” und schon läuft die erweiterte Applikation – vorbehaltlich inhaltlicher Erweiterungen der Business-Logik. Es liegt auf der Hand, dass sich die anfängliche Mühe bei der Erstellung “schemabasierter” Frameworks schnell lohnt.