Dieser Artikel ist Teil einer Reihe zum Webserver nginx. Schau dir auch die anderen Artikel an: Zum Leitartikel Vor einigen Tagen schrieb ich bereits über eine Möglichkeit, die Gravatar-Icons lokal zwischen zu speichern. Leider wurde dabei bei jedem Aufruf eines Bildes immer einen php-call gemacht, was mir bei manchen Beiträgen das Blog in die Knie zwingen würde und dem eigentlich gewünschten Effekt (Beschleunigung) entgegenstehen würde. Daher habe ich mir mal die Funktionen meines Webservers nginx angesehen und dabei festgestellt, dass dieser die folgende Prüfung erlaubt:

  • Ist das Bild vorhanden?
    • Wenn ja: Direkte Auslieferung
    • Wenn nein: Aufruf einer PHP-Datei zum Download des Bildes

Die Umsetzung war dann einfach:

Das ist die Konfigurationsdatei für den vHost in nginx:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server {
 listen 80;
 server_name img.yourdomain.tld;
 error_log /path/to/your/logs/error/img.yourdomain.tld.log warn;
 root /path/to/your/document/root/;

 location ~ \.php$ {
 fastcgi_pass 127.0.0.1:1234;
 include /etc/nginx/fastcgi_params;
 }

 location / {
 index cdn.php;
 # if the requested file exists, return it immediately
 if (-f $request_filename) {
 break;
 }

 # all other requests go to CDN-PHP-File
 if (!-e $request_filename) {
 rewrite ^(.+)$ /cdn.php last;
 }
 }
}

Nun speichert man in /path/to/your/document/root/ die Datei cdn.php mit folgendem Inhalt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 $grav_img,
 CURLOPT_HEADER => false,
 CURLOPT_RETURNTRANSFER => true,
 CURLOPT_TIMEOUT => 20,
 CURLOPT_BINARYTRANSFER => true,
 CURLOPT_MAXREDIRS => 2
 );
 curl_setopt_array($gch, $gcurl_options);

 $gdata = curl_exec($gch);
 if(!curl_errno($gch)) {
 $info = curl_getinfo($ch);
 if(curl_getinfo($gch, CURLINFO_HTTP_CODE) != 200) {
 echo "Foo";
 exit;
 }
 }
 curl_close ($gch);
 unset($gch);
 }
 $gfilehandle_img = fopen("g/" . $cache['size'] . "/" . $cache['hash'] . ".png", "wb+");
 fwrite($gfilehandle_img, $gdata);
 fclose($gfilehandle_img);
 unset($gfilehandle_img);

 header('content-type: image/png');
 echo $gdata;
}

Was macht diese Datei?

Zunächst wird die URL zerlegt und anhand des Typs die Quelle für die Avatare ausgewählt. Warum der Unterordner /g/? Ich wollte mir damit offen halten, verschiedene Bildertypen zu cachen. Das habe ich gelöst, indem ich meinen Bilderhashes ein “/g/” vorstelle, welches diese als Gravatar-Bilder markiert. Fällt mir also irgendwann mal ein, dass ich beispielsweise gerne noch Pavatar (wie auch immer geartet) cachen möchte, dann kann ich dies hier einbauen.

Der nächste Teil der URL ist die größe des Avatars, die nur aus Zahlen bestehen darf.

Anschließend wird eine Prüfung der md5-summe der E-Mailadresse durchgeführt, die einige mögliche Angriffsvektoren ausfiltern soll. Dabei bleiben dann nur Strings mit 32 Zeichen in Groß- und Kleinbuchstaben übrig, andernfalls wird das Script beendet. Im Anschluss wird dann das Bild geholt (per curl, wenn möglich), gespeichert und ausgegeben. Das verzeichnis /path/to/your/document/root/g/ sollte hierzu vorhanden sein.

Die Url für ein 32x32 großes Bild sieht also am Ende so aus:

http://img.yourdomain.tld/g/32/a9ec4695d424a6dcb6f896afb0ee22bc.png

und wird bei ersten Aufruf gecached mittels php und anschließend in den weiteren Aufrufen immer statisch durch den Webserver ausgegeben. Möchte man nun regelmäßig die Bilder aktualisieren, empfiehlt es sich, das hier bereits vorgestellte Script in regelmäßigen Abständen laufen zu lassen.

Für den Einbau in Wordpress ist das ganze auch recht einfach. Hierzu nimmt man einfach mein Wordpress-Plugin wu-get_avatar.

Zu guter letzt noch ein Hinweis für Apache-Nutzer: Das PHP-Script lässt sich natürlich auch mit diesem Webserver nutzen.