Webserver mit Nginx, PHP7 & MariaDB

31.12.2019
In diesem Beitrag möchte ich euch zeigen, wie ihr unter Debian 10 'buster' einen Webserver mit Nginx, PHP7.3 und MariaDB einrichten könnt. Diese Kombination wird oft als LEMP bezeichnet, dabei steht L für Linux, E für Nginx (engine-x), M für MariaDB (MySQL) und P für PHP. Eventuell kennt ihr das auch als LAMP, mit Apache2 als HTTP Server. Allerdings ist Nginx in den meisten Fällen die bessere Alternative, da es mit einer hohen Performance und Stabilität bei vergleichsweise geringen Ressourcen Anforderungen punkten kann. Zusätzlich zeige ich euch wie ihr Certbot (Lets Encrypt) für kostenlose TLS Zertifikate und phpMyAdmin zur Datenbankverwaltung im Browser einrichtet.

Hinweis: Ihr benötigt für viele oder alle Schritte dieser Anleitung einen User mit sudo Rechten oder den root User. Ich nutze den root User, tut ihr dies nicht, müsst ihr bei den angegebenen Befehlen "sudo" voranstellen, z.B. "sudo apt update".

Sprungmarken: PHP 7.3 Certbot Nginx MariaDB phpMyAdmin

Server Update

Bevor ihr mit der eigentlichen Anleitung beginnt, solltet ihr vorab den Server updaten!

apt update && apt dist-upgrade && apt autoremove && apt clean

Mit dieser Befehls-Kombination aktualisiert ihr die Paketquellen, sucht nach Upgrades für installierte Pakete, entfernt überflüssige Abhängigkeiten und leert den Paket-Cache. "dist-upgrade" hat gegenüber "upgrade" den Vorteil, dass zusätzlich geprüft wird, ob durch geänderte Abhängigkeiten bestimmte Pakete deinstalliert bzw. neu installiert werden sollten.

PHP 7.3

Ihr solltet zuerst PHP (php-fpm) installieren, damit ihr dieses bei der Nginx Konfiguration sofort mit einbeziehen könnt. Außerdem solltet ihr, sofern ihr MariaDB nutzen werdet, auch das php-mysql Paket installieren, damit PHP Scripte auf die Datenbank zugreifen können. Installieren könnt ihr die Pakete mit folgendem Befehl.

apt install -y php7.3-fpm php7.3-mysql

Damit habt ihr PHP erfolgreich installiert und könnt mit dem nächsten Schritt weitermachen. Man kann zwar PHP-FPM über die Konfiguration noch optimieren, das ist aber etwas aufwendiger und von eurer Hardware abhängig, da gibt es meiner Meinung nach keine pauschal sinnvollen Angaben (auch wenn manche Guides im Internet das anders darstellen). Für wenig bis mittel gut besuchte Webseiten sollte die Standardkonfiguration aber sowieso ausreichen. Eine falsche Konfiguration kann zudem zu großen Problemen, wie einem voll laufenden RAM, führen.

Ältere PHP Versionen: Es ist auch problemlos möglich, eine ältere Version bzw. mehrere Versionen gleichzeitig zu installieren. Das kann beispielsweise nötig sein, wenn ihr zwei Webseiten bzw. Anwendungen auf dem Server betreiben wollt, eine davon PHP 7.3 unterstützt, die andere aber nur PHP 7.1. Im Debian 10 Repository (stable) werdet ihr nur die aktuelle Version (zur Zeit 7.3) bekommen. Daher müsst ihr, um eine ältere Version zu installieren, erst das Repository "deb.sury.org" von Ondrej Sury (Hauptentwickler der PHP Pakete unter Debian) einpflegen.

apt install -y apt-transport-https lsb-release ca-certificates
wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
echo "deb https://packages.sury.org/php/ buster main" > /etc/apt/sources.list.d/php.list
apt update

Anschließend könnt ihr ganz einfach die gewünschte Version installieren, beispielsweise für PHP7.1 wie folgt.

apt install -y php7.1-fpm

Certbot (Lets Encrypt)

Als nächstes solltet ihr Cerbot installieren, um kostenlose TLS Zertifikate von Lets Encrypt beantragen zu können. Heutzutage sollte eigentlich jede Webseite über https aufgerufen werden können, auch wegen entsprechender Warnmeldungen bei aktuellen Browsern. Außerdem solltet ihr noch das Nginx Plugin mitinstallieren, damit ihr direkt über Nginx Zertifikate beantragen und später erneuern könnt.

apt install -y certbot python-certbot-nginx

Anschließend könnt ihr direkt ein TLS Zertifikat für eure Domain/Subdomain beantragen.

certbot certonly --rsa-key-size 4096 -d domain.tld -d www.domain.tld --nginx

Wenn ihr das erste Mal ein Zertifikat beantragt, müsst ihr eine E-Mail Adresse angeben, den ToS zustimmen und Entscheiden ob ihr die E-Mail Adresse mit Partnern von Lets Encrypt teilen wollt (N für ablehnen).

Abschließend solltet ihr noch dafür sorgen, dass eure Zertifikate automatisch erneuert werden, da diese nur 3 Monate gültig sind. Dafür könnt ihr bequem einen Cronjob anlegen, der fortan jeden Monat eure Zertifikate erneuert.

nano /etc/crontab

In dieser Datei ergänzt ihr dann ganz unten den folgenden Cron.

0  4  1 * *   root  certbot renew --force-renew --nginx

Das war es auch schon, ab sofort werden eure Zertifikate immer am 1. eines Monats um 4 Uhr morgens erneuert.

Nginx

Nun könnt ihr Nginx installieren.

apt install -y nginx

Bevor ihr euch nun an den Konfigurationen zu schaffen macht, fahrt ihr zuerst Nginx runter.

service nginx stop

Nun könnt ihr mit der Konfiguration von Nginx beginnen. Zuerst könnt ihr die unnötigen Dateien in "Snippets" löschen, die wir später sowieso ersetzen. In diesem Ordner werdet ihr später Konfigurationen für verschiedene Dinge anlegen, mit denen ihr die einzelnen Webseiten-Konfigurationen erweitern könnt. Zudem könnt ihr die Verknüpfung der Standard-Seitenkonfiguration löschen.

rm /etc/nginx/snippets/* ; rm /etc/nginx/sites-enabled/*

Optional: Ich persönlich lösche zusätzlich die Ordner "sites-available" und "modules-available" und benenne "modules-enabled" in "modules" und "sites-enabled" in "sites" um. Das ist aber Geschmackssache und eher unüblich. Eigentlich sieht Nginx vor, dass im jeweiligen "-available" Ordner die Konfiguration und im entsprechenden "-enabled" Ordner eine Verknüpfung angelegt wird. So kann man Module und Seiten durch löschen der Verknüpfung an- und abschalten. Ihr könnt das aber natürlich so machen, wenn ihr das möchtet. Alternativ könnt ihr auch einfach die Konfigurationen direkt im jeweiligen "-enabled" Ordner erstellen. Ich bin aber kein Fan von leeren/sinnlosen Ordnern, daher räume ich wie beschrieben immer gerne etwas auf.

rm -R /etc/nginx/modules-available/ ; rm -R /etc/nginx/sites-available/
mv /etc/nginx/modules-enabled /etc/nginx/modules ; mv /etc/nginx/sites-enabled /etc/nginx/sites

Für eine bessere Übersicht, könnt ihr euch den Ordner von Certbot mit den aktiven Zertifikaten im Nginx Ordner verknüpfen. Dies könnt ihr bei der Seitenkonfiguration dann auch als Pfad verwenden.

ln -s /etc/letsencrypt/live/ /etc/nginx/sslcerts

Nun könnt ihr Konfigurationen erstellen. Sinnvollerweise nutzt ihr den Ordner "conf.d" für Konfigurationen die ihr in "nginx.conf" einbinden werdet; also die, die für alle eure Seiten zur Anwendung kommen sollen. Den Ordner "snippets" nutzt ihr hingegen für Konfigurationen, die ihr nur bei bestimmten Seiten anwenden wollt oder die in der globalen Konfiguration nicht zulässig sind, z.B. location{} Blocks wie bei der PHP Konfiguration.

Erstellt zuerst die Konfiguration für PHP7.3 (nutzt ihr eine ältere Version, müsst ihr diese in "fastcgi_pass" ändern!).

nano /etc/nginx/snippets/php_7-3.conf

Von mir empfohlener Inhalt:

location ~ (?U)\.php(/.*$|$) {
  fastcgi_split_path_info   ^(.+?\.php)(/.*)$;
  fastcgi_pass  unix:/var/run/php/php7.3-fpm.sock;
  fastcgi_index   index.php;
  fastcgi_keep_conn   on;
  fastcgi_buffer_size   128k;
  fastcgi_buffers 256   16k;
  fastcgi_busy_buffers_size   256k;
  fastcgi_temp_file_write_size  256k;
  fastcgi_read_timeout  240;
  fastcgi_intercept_errors  on;
  try_files   $fastcgi_script_name =404;
  set $path_info   $fastcgi_path_info;
  fastcgi_param   PATH_INFO   $path_info;
  include   fastcgi.conf;
}

Nun könnt ihr die Browser Cache Konfiguration anlegen.

nano /etc/nginx/snippets/cache.conf

Von mir empfohlener Inhalt:

location ~* \.(ogg|ogv|svg|svgz|eot|otf|woff|woff2|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
  expires 30d;
  add_header Pragma "public";
  add_header Cache-Control "public, no-transform";
}
location ~* \.(css|js)$ {
  expires 7d;
  add_header Pragma "public";
  add_header Cache-Control "public, no-transform";
}

Nun könnt ihr die Header Konfiguration anlegen.

nano /etc/nginx/conf.d/headers.conf

Von mir empfohlener Inhalt:

add_header X-Cache $upstream_cache_status;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block" always;
add_header "Cache-Control" "no-transform";
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains" always;
proxy_hide_header Etag;

Nun könnt ihr die SSL Konfiguration anlegen.

nano /etc/nginx/conf.d/ssl.conf

Von mir empfohlener Inhalt:

ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_session_cache shared:SSL:5m;
ssl_session_timeout 30m;

Nun könnt ihr die Gzip Konfiguration anlegen.

nano /etc/nginx/conf.d/gzip.conf

Von mir empfohlener Inhalt:

gzip on;
gzip_buffers 32 16k;
gzip_http_version 1.1;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_proxied expired no-cache no-store private auth;
gzip_comp_level 6;
gzip_min_length 250;
gzip_types
  application/atom+xml
  application/javascript
  application/json
  application/ld+json
  application/manifest+json
  application/rdf+xml
  application/rss+xml
  application/schema+json
  application/vnd.geo+json
  application/vnd.ms-fontobject
  application/x-font-ttf
  application/x-javascript
  application/x-web-app-manifest+json
  application/xhtml+xml
  application/xml
  font/eot
  font/opentype
  image/bmp
  image/svg+xml
  image/vnd.microsoft.icon
  image/x-icon
  text/cache-manifest
  text/css
  text/javascript
  text/plain
  text/vcard
  text/vnd.rim.location.xloc
  text/vtt
  text/x-component
  text/x-cross-domain-policy
  text/xml
;

Nun könnt ihr die Konfiguration für verschiedenen allgemeine Einstellungen anlegen.

nano /etc/nginx/conf.d/general.conf

Von mir empfohlener Inhalt:

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
index index.php index.html index.htm;
client_max_body_size 0;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

Zum Abschluss könnnt ihr die Hauptkonfiguration von Nginx löschen und neu erstellen.

rm /etc/nginx/nginx.conf && nano /etc/nginx/nginx.conf

Von mir empfohlener Inhalt:

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules/*.conf;
events {
  worker_connections 1024;
  multi_accept off;
}
http {
  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites/*;
}

Die Pfade beim jeweiligen include von modules (modules-enabled) und sites (sites-enabled) müsst ihr natürlich ändern, wenn ihr die Ordner nicht wie oben bei "Optional" angegeben umbenannt habt!

Die Konfiguration von Nginx ist damit fast abgeschlossen, es fehlt nur noch eine Seitenkonfiguration. Diese könnt ihr nun erstellen, domain.tld solltet ihr als Dateinamen und in der nachfolgenden Beispielkonfiguration natürlich zu eurer URL ändern.

nano /etc/nginx/sites/domain.tld

Von mir empfohlener Inhalt:

server {
  listen 80;
  server_name www.domain.tld domain.tld;
  return 301 https://domain.tld$request_uri;
}
server {
  listen 443 ssl;
  server_name www.domain.tld;
  ssl_certificate /etc/nginx/sslcerts/domain.tld/fullchain.pem;
  ssl_certificate_key /etc/nginx/sslcerts/domain.tld/privkey.pem;
  return 301 https://domain.tld$request_uri;
}
server {
  listen 443 ssl http2;
  server_name domain.tld;
  ssl_certificate /etc/nginx/sslcerts/domain.tld/fullchain.pem;
  ssl_certificate_key /etc/nginx/sslcerts/domain.tld/privkey.pem;
  root /cloud/websites/domain.tld;
  access_log /var/log/nginx/domain.tld_access.log;
  error_log /var/log/nginx/domain.tld_error.log error;
  include /etc/nginx/snippets/php_7-3.conf;
  include /etc/nginx/snippets/cache.conf;
}

Wichtig: Den nach "root" angegeben Pfad müsst ihr natürlich anpassen, je nachdem wo auf dem Server sich eure Webseite befindet. Allgemein gilt /var/www/ als Standardordner für Webseiten, ihr könnt euch das aber frei aussuchen. Wichtig ist nur, dass ihr das in der entsprechenden Seitenkonfiguration richtig angebt.

Subdomain: Wenn ihr eine Subdomain (sub.domain.tld) für eure Seite nutzen möchtet, müsst ihr den 2. server{} Block entfernen. Dieser ist lediglich dafür gedacht, bei Aufruf von www.domain.tld auf domain.tld umzuleiten. Bei einer Subdomain führt das allerdings zu einer Endlosschleife.

Das war es auch schon zu Nginx, ihr habt jetzt einen fertig eingerichtet Webserver mit PHP und HTTPS Verschlüsselung. Ich habe bewusst darauf verzichtet, die diversen Konfigurationen einzeln aufzuschlüsseln. Erfahrungsgemäß möchten die meisten sowieso nur fertige Konfigurationen zum kopieren haben. Ihr könnt aber ganz einfach die jeweiligen Einträge (z.B. "client_max_body_size") bei Google eingeben und bekommt jede Menge Infos dazu. Zum Abschluss müsst ihr Nginx natürlich wieder starten.

service nginx start

MariaDB (MySQL)

Nun könnt ihr MariaDB installieren und anschließend direkt das mitgelieferte Script zum Einrichten ausführen.

apt install -y mariadb-server
mysql_secure_installation

Ihr werdet nun ein paar Sachen abgefragt

  • Enter current password; mit Enter bestätigen
  • Set root password?; mit Enter bestätigen
  • New password; ein sicheres Passwort eingeben
  • Re-enter new password; euer Passwort erneut eingeben
  • Remove anonymous users?; mit Enter bestätigen
  • Disallow root login remotely?; mit Enter bestätigen
  • Remove test database and access to it?; mit Enter bestätigen
  • Reload privilege tables now?; mit Enter bestätigen

Das war auch schon alles, MariaDB ist damit fertig eingerichtet.

phpMyAdmin

Wenn ihr möchtet, könnt ihr noch phpMyAdmin einrichten, um eure MariaDB Datenbanken im Browser verwalten zu können. Die Webanwendung ist kostenlos und kann direkt auf phpmyadmin.net heruntergeladen werden. Theoretisch kann man es sogar aus den Paketquellen mit apt installieren, würde ich in dem Fall aber wegen der Aktualität von abraten.

Nachdem ihr euch das Archiv von der Webseite heruntergeladen habt, könnt ihr es irgendwo entpacken und mit dem FTP Programm eurer Wahl auf euren Server laden. Ich empfehle aus Sicherheitsgründen keinen Unterordner einer bestehenden Webseite zu nutzen, sondern ein neues Verzeichnis zu erstellen und eine Subdomain mit Verzeichnisschutz dafür einzurichten. Erstellt dazu einen Ordner an einer Stelle eurer Wahl.

mkdir /cloud/websites/phpmyadmin.domain.tld

Anschließend ladet ihr mit eurem FTP Programm die Dateien aus dem entpackten Ordner ("phpMyAdmin-5.0.0-all-languages" oder ähnlich) in das gerade erstellte Verzeichnis hoch. Dann erstellt ihr eine Seitenkonfiguration in Nginx für die Subdomain. Die entsprechende Subdomain solltet ihr natürlich bei eurem Hoster bzw. Registrar vorher eingerichtet haben.

nano /etc/nginx/sites/phpmyadmin.domain.tld

Von mir empfohlener Inhalt:

server {
  listen 80;
  server_name phpmyadmin.domain.tld;
  return 301 https://phpmyadmin.domain.tld$request_uri;
}
server {
  listen 443 ssl http2;
  server_name phpmyadmin.domain.tld;
  ssl_certificate /etc/nginx/sslcerts/phpmyadmin.domain.tld/fullchain.pem;
  ssl_certificate_key /etc/nginx/sslcerts/phpmyadmin.domain.tld/privkey.pem;
  root /cloud/websites/phpmyadmin.domain.tld;
  access_log /var/log/nginx/phpmyadmin.domain.tld_access.log;
  error_log /var/log/nginx/phpmyadmin.domain.tld_error.log error;
  include /etc/nginx/snippets/php_7-3.conf;
  include /etc/nginx/snippets/cache.conf;
  auth_basic "Restricted";
  auth_basic_user_file /cloud/websites/phpmyadmin.domain.tld/.htpasswd;
}

Ihr benötigt nun noch eine ".htpasswd" Datei im phpMyAdmin Verzeichnis. Diese beinhaltet User und Passwort für den Verzeichnisschutz. Bitte nehmt da nicht das selbe Passwort wie für den MySQL root!

nano /cloud/websites/phpmyadmin.domain.tld/.htpasswd

Den Inhalt könnt ihr euch generieren, es gibt diverse Generatoren über Google zu entdecken. Ihr könnt euch auch selbst einen Generator in php erstellen, dafür reicht der folgende Code:

$user = 'DEIN_USER';
$pw = 'DEIN_PASSWORT';
$password = crypt($pw, base64_encode($pw));
echo $user . ':' . $password;

Die Ausgabe dieser PHP Datei könnt ihr dann in eurer .htpasswd eintragen. Für "Bob" mit Passwort "hallowelt123" sieht das beispielsweise so aus:

bob:aGtMptpFqcGHw

Anschließend besorgt ihr euch noch ein TLS Zertifikat für die Subdomain.

certbot certonly --rsa-key-size 4096 -d phpmyadmin.domain.tld --nginx

Nun müsst ihr nur noch Nginx neustarten und könnt anschließend das Webinterface von phpMyAdmin über https://phpmyadmin.domain.tld/ aufrufen.

service nginx restart

Ihr werdet allerdings feststellen, dass ihr euch nicht mit dem root Account einloggen könnt. Das liegt an den Sicherheitseinstellungen von MariaDB. Da ihr wahrscheinlich keine Lust habt, immer über die Shell neue MySQL Accounts anzulegen und diese dann für den Zugriff auf verschiedene Datenbanken in phpMyAdmin zu nutzen, zeige ich euch, wie ihr diese Sicherheitseinstellung abschalten könnt. Man könnte natürlich, wie es manche Guides zeigen, einen Admin Account anlegen und diesem alle Rechte geben. Das kommt aus sicherheitstechnischer Sicht aber meiner Ansicht nach auf das selbe raus, wie den Login mit dem root Account zu aktivieren. Den Login als root Account könnt ihr wie folgt über die Shell aktivieren (Befehle nacheinander eingeben).

mysql -u root
use mysql;
update user set plugin='' where User='root';
flush privileges;
exit;

Anschließend einfach die Seite mit phpMyAdmin neu laden und schon könnt ihr euch mit dem root Account und dem im MariaDB Abschnitt vergebenen Passwort einloggen.

Zur Begrüßung werden euch wahrscheinlich gleich 3 rote Meldungen entgegen winken. Aber keine Sorge, die sind schnell zu beheben. Zuerst werdet ihr auf die fehlende PHP Erweiterung mbstring hingewiesen, die ihr also kurz installiert und anschließend nginx neustartet.

apt install -y php7.3-mbstring && service nginx restart

Anschließend könnt ihr die phpMyAdmin Seite neuladen, die Meldung sollte verschwunden sein. Als nächstes kümmert ihr euch um die fehlende "secret passphrase".

cp /cloud/websites/phpmyadmin.domain.tld/config.sample.inc.php /cloud/websites/phpmyadmin.domain.tld/config.inc.php
nano /cloud/websites/phpmyadmin.domain.tld/config.inc.php

Gebt nun bei "$cfg['blowfish_secret']" eine 32 Zeichen lange Passphrase ein. Die müsst ihr euch nicht merken, also nehmt einfach irgendwelche Buchstaben, Zahlen und Sonderzeichen. Speichert die Datei ab und ladet die Seite neu. Ihr müsstet euch nun neu einloggen müssen. Anschließend ist die Meldung aber verschwunden. Für die letzte Meldung, reicht es das angegebene Verzeichnis zu erstellen und www-data als Besitzer des Verzeichnisses einzutragen. Letzteres machen wir direkt für das komplette Verzeichnis.

mkdir /cloud/websites/phpmyadmin.domain.tld/tmp
chown -R www-data:www-data /cloud/websites/phpmyadmin.domain.tld/

Nach einem letzten Neuladen der phpMyAdmin Seite sollten nun alle roten Meldungen verschwunden sein und ihr seid fertig mit der Einrichtung.

Zuletzt bearbeitet: 29.05.2020