wolfuli

Bildercache für den Webserver nginx am Beispiel von Gravatar-Icons

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

Werbung


Die Umsetzung war dann einfach:

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

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:

<?php
// Script by Uli Wolf - https://wolf-u.li
 
<?php
$url=explode("/", $_SERVER['REQUEST_URI']);
 
// Type
$url[1] = strtolower(preg_replace("/[^a-zA-Z\s]/", "", $url[1]));
 
// Now evaluate the type
if(strlen($url[1])==1) {
        switch($url[1]) {
                case "g":
                        cache_get_gravatar($url);
                break;
                default:
                        exit;
                break;
        }
}
 
 
function cache_get_gravatar($url) {
        $cache['type']=$url[1];
        $cache['size']=$url[2];
        $cache['hash']=$url[3];
        if(!is_dir("./g")) { mkdir("./g"); }
        // Size
        $cache['size'] = preg_replace("/[^0-9\s]/", "", $cache['size']);
        if(!is_dir("./g/" . $cache['size'])) { mkdir("./g/" . $cache['size']); }
 
        // Key
        // - The String looks like: 3c7257f1a8c44frc6oae3ed84cd78a88.png
        $cache['hash'] = explode(".", $cache['hash']);
        // - The String looks like: 3c7257f1a8c44frc6oae3ed84cd78a88
        $cache['hash'] = preg_replace("/[^a-zA-Z0-9\s]/", "", $cache['hash'][0]);
        // Check correct string length
        if(strlen($cache['hash'])!=32) {exit;}
 
        // If the mailadress was not found, you can get various images:
        // - identicon
        // - monsterid
        // - wavatar
        // - mm (mysteryman)
        $mailnotfound = "mm";
        $grav_img = "http://www.gravatar.com/avatar/" . $cache['hash'] . "?s=" . $cache['size'] . "&d=" . $mailnotfound . "&r=G";
        if(extension_loaded('curl')) {
                $gch = curl_init();
                $gcurl_options = array(
                        CURLOPT_URL => $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 32×32 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.

Veröffentlicht von

Uli

IT-Nerd und Admin

3 Gedanken zu „Bildercache für den Webserver nginx am Beispiel von Gravatar-Icons“

  1. Ähm… nginx ist ursprünglich ein PROXYserver. Und kann ganz alleine Files cachen, ohne PHP-Skripte: siehe proxy_pass, proxy_cache.

    Schöne Weihnachten 😉

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.