Nginx als Reverse Proxy für statische Inhalte vor Apache

2012-01-02 - kostaki 10 Kommentare »

Vor kurzem ergab sich für mich die Möglichkeit einen Nginx als Reverse Proxy vor einen Apache zu setzen. Persönlich setze ich seit ein paar Monaten komplett auf Nginx und bin sehr zufrieden, aber in diesem Fall ist vorerst angedacht beide Server parallel laufen zu lassen. Der Nginx soll zum ausliefern von statischen Inhalten benutzt werden und alles andere an den Apache weiterreichen. Damit ist es möglich ohne Anpassungen an der Webanwendung, Last vom Apache zu nehmen und damit Last vom Server. Auf dem Server läuft Debian Squeeze und da es sich um einen Managed Server handelt, benutze ich die in Squeeze enthaltene Nginx Version 0.7.67. Für aktuellere Versionen kann dotdeb.org benutzt werden.

Ablauf Nginx als Reverse Proxy

Nginx Installation

Nginx kann man ohne Probleme installieren auch wenn der Apache noch läuft, da er nicht automatisch gestartet wird.

$ aptitude install nginx

Nginx Basis Konfiguration

$ nano /etc/nginx/nginx.conf
user www-data;
worker_processes 4;

error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;

events {
    worker_connections 2048;
}

http {
    include /etc/nginx/mime.types;

    access_log off;
    sendfile on;
    tcp_nodelay on;
    tcp_nopush on;
    server_tokens off;

    keepalive_timeout 10;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Nginx Gzip Konfiguration

$ nano /etc/nginx/conf.d/gzip.conf
gzip on;
gzip_disable "msie6";
gzip_comp_level 6;
gzip_types text/plain text/css application/json
    application/x-javascript text/xml application/xml
    application/xml+rss text/javascript;

Nginx Proxy Konfiguration

$ nano /etc/nginx/conf.d/proxy.conf
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Hier werden nur ein paar Header gesetzt, die vom Nginx an den Apache weitergereicht werden. Das ist wichtig, damit der Apache die User IP bekommt und nicht die lokale IP (127.0.0.1). Dazu später mehr. Das Proxy Module bietet noch viele weitere Settings, aber ich würde erst mit den Default Werten starten. Sollte es Problem geben, kann man immer noch an den Stellschrauben drehen.

Wichtig zu wissen ist das man Anpassungen in location Blöcken machen kann! Man muss sie also nicht für den kompletten Server oder Vhost machen, sondern kann sie für bestimmte Dateien oder Ordner setzen!

# Änderungen an eine Datei
location = /nginxtest/upload.php {
    ...
}

# Änderungen an einem Verzeichnis und dessen Inhalts
location ^~ /nginxtest/ {
    ...
}

Nginx Default Vhost

Zum testen wird der Nginx auf Port 8080 laufen und der Apache weiterhin auf 80. Wenn alles funktioniert wie es soll, kann das einfach umgedreht werden.

$ nano /etc/nginx/sites-available/default
server {
    listen 8080 default;

    location / {
        proxy_pass http://127.0.0.1:80;
    }
}

Wie man sehen kann ist hier nicht viel angegeben. Diese Config sorgt dafür das alles was beim Nginx ankommt an den Apache weitergereicht wird. Damit arbeitet der Nginx komplett transparent und man kann die ersten Tests durchführen.

$ /etc/init.d/nginx start

Alle Seiten sollten unter Port 8080 erreichbar sein (zum Beispiel: http://www.example.org:8080). Des weiteren sollte alles was vorher lief auch jetzt noch funktionieren, da der Nginx noch nichts selbst macht, sondern nur alles weiterreicht. Probleme könnte es bei den Timeouts vom Nginx zum Apache geben, bei File Uploads und womöglich bei den Buffern im Nginx. Alles sauber testen und wenn es zu Problemen kommt diese fixen.

Nginx 80 -> Apache 8080

Nun können die Ports getauscht werden. Der Nginx macht immer noch nichts als durch reichen aber jetzt dann unter User Last. Hier fällt einem dann sicher noch das ein oder andere auf, also das error_log im Auge behalten.

$ nano /etc/nginx/sites-available/default
# Vorher
...
listen 8080 default;
proxy_pass http://127.0.0.1:80;
...
# Nachher
...
listen 80 default;
proxy_pass http://127.0.0.1:8080;
...
$ nano /etc/apache2/ports.conf
# Vorher
...
NameVirtualHost *:80
Listen 80
..
# Nachher
...
NameVirtualHost *:8080
Listen 8080
...

Als nächstes müssen noch alle Vhost Configs des Apaches angepasst werden. Einfach alle Dateien in apache2/sites-enabled durchgehen und in ändern.

Noch die Webserver neu starten und dann wird es spannend. Den Nginx erst stoppen, damit Port 8080 frei wird. Dann den Apache neu starten, damit 80 frei wird. Als letztes den Nginx wieder starten.

$ /etc/init.d/nginx stop
$ /etc/init.d/apache2 restart
$ /etc/init.d/nginx start

Nochmal testen ob alles läuft.

Nginx zum ausliefern von statischen Dateien konfigurieren

Liegen mehrere Projekte auf dem Server, lohnt sich eine extra Vhost Config für jedes Projekt anzulegen. Hat man nur 1 Projekt, dann können alle Anpassungen im default Vhost gemacht werden.

$ /etc/nginx/sites-available/www.example.org
server {
    listen 80;
    server_name www.example.org;
    root /var/www/vh-example/htdocs/public;

    location ~* ^.+\.(jpe?g|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|swf|avi|mp3)$ {
    	expires max;
    }

    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}

Alles was auf die Regex im ersten location Block passt wird vom Nginx ausgeliefert. Alles andere wird wie im zweiten Block definiert an den Apache weitergeleitet. Die expires max; Anweisung sorgt dafür das für die statischen Dateien ein Expires Header von 31 December 2037 23:59:59 GMT gesetzt wird und ein Cache-Control Header von max-age 10 Jahre. Wenn die Webanwendung damit nicht klar kommt, einfach die Zeile entfernen oder anpassen.

$ ln -s /etc/nginx/sites-available/www.example.org \
/etc/nginx/sites-enabled/www.example.org
$ /etc/init.d/nginx reload

Überprüfen kann man es indem man ins Access log des Apache guckt. Dort sollten keine statischen Contents mehr landen.

Mögliche Fehler aus dem Nginx error_log

Timeouts:

[error] upstream timed out (110: Connection timed out) while reading response header from 
upstream

In diesem Fall bricht die Verbindung vom Nginx zum Apache ab und damit auch zum User. Die Standard Einstellungen besagen das der Nginx 60 Sekunden auf den Apache wartet. Hat man langlaufende Scripte, dann kann man die Timeout für diese erhöhen.

location ^~ /lang/laufende/scripte/ {
    proxy_send_timeout 120s;
    proxy_read_timeout 120s;
}

File Uploads brechen ab:

[error] client intended to send too large body: xxx bytes

Um das Problem zu beseitigen erhöht man die client_max_body_size. Man sollte diesen Wert nur für ein bestimmtes Verzeichnis erhöhen und nicht für den kompletten Server/Vhost und man sollte den Wert mit den Einstellungen im Backend Modul (PHP?) abgleichen. Es macht keinen Sinn hier 256MB einzustellen, wenn die upload_max_filesize/post_max_size in der php.ini nur 2MB zulässt.

Korrekte User IP an den Apache übergeben

Damit die User IP die der Nginx bekommt auch vom Apache verstanden wird (und auch von PHP!) muss man das rpaf Modul installieren. Ansonsten taucht in den Apache Access Logs nur noch die IP 127.0.0.1 auf.

$ aptitude install libapache2-mod-rpaf
$ a2enmod rpaf
$ /etc/init.d/apache2 restart

Testen kann man es durch einen Blick in die Access Logs oder durch eine PHP Ausgabe von $_SERVER (vor dem aktivieren [REMOTE_ADDR] => 127.0.0.1, nach dem aktivieren [REMOTE_ADDR] => 192.168.111.111).

Zugriff auf Port 8080 einschränken

Unschön ist das man per 8080 direkt auf den Apache kommt. Das kann zu SEO Problemen führen, da für Suchmaschinen example.org etwas anderes ist als example.org:8080 und damit im schlimmsten Fall alles doppelt indexiert wird. Es gibt mehrere Möglichkeiten dies zu beheben. Die einfachste ist das Interface auf dem der Apache läuft anzupassen und nur noch Zugriffe von 127.0.0.1 zuzulassen.

$ nano /etc/apache2/ports.conf
# Vorher
...
NameVirtualHost *:8080
..
# Nachher
...
NameVirtualHost 127.0.0.1:8080
...

Dann alle Dateien in apache2/sites-enabled durchgehen und in ändern.

Eine weitere Lösung ist alle Aufrufe auf Port 8080 auf Port 80 weiter zuleiten.

$ nano /etc/apache2/redirect8080.conf
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{SERVER_PORT} 8080
    RewriteRule (.*) http://%{SERVER_NAME}%{REQUEST_URI}
</IfModule>

Nun packt man in die Apache Vhost Configs noch folgende Zeile.

<VirtualHost *:8080>
...
Include /etc/apache2/redirect8080.conf
...
</VirtualHost>

Nach einem Apache Reload werden dann alle Aufrufe mit Port 8080 auf Port 80 umgeleitet. Man kann natürlich auch den Zugriff auf den Port durch iptables limitieren.

Related Links

  1. 10 Kommentare

  2. S0me0ne
    schrieb am 02.01.2012 um 20:59 Uhr

    Interessant wäre, wie viel Performance das gebracht hat. Oder war es ein reines Spaßprojekt?

  3. kostaki
    schrieb am 03.01.2012 um 09:10 Uhr

    Nein war kein Spaß Projekt. Was es gebracht hat ist schwer an irgend welchen Werten festzumachen, da auch an der Anwendung optimiert wurde. Für mich liegt der Erfolg in der Tatsache das der Apache nun ca. 5 Millionen Anfragen pro Tag weniger beantworten muss.

  4. biwebco
    schrieb am 03.01.2012 um 19:27 Uhr

    @S0me0ne:
    http://www.google.de/search?q=benchmark+nginx+apache&ie=utf-8&oe=utf-8 => Es gibt viele benchmarks, die deutlich zeigen, dass Nginx wesetlich perfomanter als Apache ist.

    @Kostaki:
    es wäre noch besser auf Apache zu verchichten: nginx + php-fpm

    Einzelfall: Wenn die ganze Webseite kein PHP verwendet, würde ich auch html direkt über Nginx ausgeben. D.h. nginx.conf -> Zeile mit “location”, zu den Erweiterungen “html” hinzufügen.

  5. kostaki
    schrieb am 04.01.2012 um 08:47 Uhr

    Wirklich viel html gibt es leider nicht. Wird alles dynamisch erzeugt und ja ich hätte auch lieber komplett auf den Apache (prefork :( ) verzichtet, aber ich war überhaupt schon froh das sie mich nginx installieren ließen. Ist ja immerhin managed.

  6. nusshörnchen
    schrieb am 26.01.2012 um 12:34 Uhr

    @biwebco: Wieso würdest du auf Apache verzichten??

  7. wynni
    schrieb am 04.02.2012 um 15:34 Uhr

    Tolle Anleitung,

    was muß ich machen un nginx abzusichern? Kannst du mir da ein paar tips geben?

    wynni

  8. Hannes
    schrieb am 03.10.2012 um 16:42 Uhr

    @ wynni

    kann deinem Kommentar nur zustimmen!

    hast du bereits infos bekommen?

  9. wynni
    schrieb am 13.11.2012 um 09:27 Uhr

    @ Hannes

    nein leider nicht.

  1. Trackback(s)

  2. Jun 16, 2012:Nginx als Reverse Proxy mit Cache und Apache2 | Ulrich-Block.de
  3. Mrz 18, 2013:26 Tipps um Webseiten schneller zu machen Wordpress, Webdesign & SEO Blog von Netzgänger René Dasbeck

Kommentar schreiben

*

*