Python/Django und Letsencrypt für erste Website über Nginx
In meinem Artikel Grundlegende Einrichtung des vServers habe ich geschrieben, dass ich meine ersten Schritte bei der Webserver-Konfiguration mit froxlor gemacht habe. Dabei wird standardmäßig von froxlor das Documentroot (Wurzelverzeichnis) für Websites in dem Unterordner /var/customers/webs festgelegt. Auch wenn ich mittlerweile auf froxlor verzichte, behalte ich die Verzeichnisstruktur annähernd bei. Die Änderungen sind dabei als root-User notwendig, also seid bitte vorsichtig bei den Schritten, damit ihr nichts anderes am Server kaputt macht! Falls ihr die Möglichkeit habt, macht einen Snapshot von eurem Server, bevor ihr los legt. So könnt ihr immer wieder zu dem Arbeitsstand der letzten Sicherung zurück.
Installation von Python3 und virtualenv
Der virtuelle Server wird mit Python2 vorkonfiguriert ausgeliefert. Für unsere Websites möchten wir aber das aktuelle Python3 verwenden. Außerdem empfielt es sich, für jedes einzelne Projekt einen eigenen Pfad anzugeben, indem die benötigten Site-Packages für Python installiert werden können. Das hat den Vorteil, dass man somit auch mit unterschiedlichen Versionen der Site-Packages arbeiten kann und bei der Aktualisierung eines Projektes nicht andere bestehende Projekte in Mitleidenschaft zieht. Die notwendigen Pakete lassen sich wieder über den Debian Paketmanager installieren:
$ apt-get install python3 python3-pip python3-virtualenv virtualenv
$ pip3 install virtualenvwrapper
Nun muss man ein paar Umgebungsvariablen für die Shell setzen, standardmäßig wird die bash verwendet. Wir passen also die Umgebung für den root-User an, welche sich in der Datei ~/.bashrc befindet:
..
export LS_OPTIONS='--color=auto'
alias ls='ls $LS_OPTIONS'
alias ll='ls $LS_OPTIONS -l'
alias la='ls $LS_OPTIONS -a'
..
# Python virtualenv
export WORKON_HOME=/var/customers/venvs
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
source /usr/local/bin/virtualenvwrapper.sh
Das Verzeichnis /var/customers/venvs muss noch angelegt werden. Wir weisen dieses dann dem User und der Gruppe www-data zu und berechtigen alle User dieser Gruppe zum Schreiben:
$ mkdir -p /var/customers/venvs
$ chown www-data:www-data /var/customers/venvs
$ chmod g+s /var/customers/venvs
$ chmod 775 /var/customers/venvs
Außerdem bekommt das Verzeichnis das spezielle s-Bit für die Gruppe gesetzt (oben 3. Befehlszeile) und vererbt es somit an alle Unterordner weiter. Damit bleibt dann später die Gruppenzugehörigkeit immer bei www-data. Somit können der neue Administrator und auch der Webserver, welcher unter dem User www-data mit gleichnamiger Gruppe läuft, immer auf alle Dateien lesend zugreifen.
Um beim Wechsel in ein Projekt-Verzeichnis später automatisch die zugehörige virtuelle Python Umgebung zu aktivieren, kann man sich des kleinen Scriptpakets namens autoenv bedienen:
$ git clone git://github.com/kennethreitz/autoenv.git ~/.autoenv
$ echo 'source ~/.autoenv/activate.sh' >> ~/.bashrc
Der neue Administrator
Da es immer sehr gefährlich ist, alle Änderungen als root-User auszuführen, gibt es das Konzept bei Linux, sich als normaler Nutzer einzuloggen und dann mit erhöhter Berechtigung einige systemnahen Befehle auszuführen. Verwendet wird dafür sudo, was soviel bedeutet wie „führe als Super User aus”. Alle anderen Befehle werden aber nur mit normaler Berechtigung des Nutzers ausgeführt und können so das System nicht weiter beeinträchtigen. Wir legen uns deshalb den Nuter namens serveradmin als sudo-User an:
$ adduser serveradmin
$ usermod -aG sudo serveradmin
$ usermod -aG www-data serveradmin
Danach loggen wir uns mit diesem Nutzer ein:
$ su - serveradmin
Auch bei diesem Nutzer legen wir die Umgebungsvariablen für Python virtualenv der bash in der Datei ~/.bashrc an:
..
alias ls='ls --color=auto'
..
# some more ls aliases
alias ll='ls -l'
alias la='ls -a'
#alias l='ls -CF'
..
# Python virtualenv
export WORKON_HOME=/var/customers/venvs
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
source /usr/local/bin/virtualenvwrapper.sh
Wir machen später als serveradmin weiter, bis dahin wechseln wir aber zurück zum root-User:
$ exit
Einrichten von Letsencrypt
Um eine sichere Kommunikation der Website zur erreichen, muss diese im Webserver für SSL konfiguriert sein und ein gültiges SSL-Zertifikat besitzen. Es gibt einige Anbieter im Internet, welche diese Zertifikate kostenpflichtig bereitstellen. Es gibt aber auch das Projekt Letsencrypt, was sich durch Spenden finanziert und diese Zertifikate kostenlos zur Verfügung stellt. Die Gültigkeit eines dieser Zertifikate ist dabei meist auf 30 Tage beschränkt, jedoch kann man automatisiert überprüfen lassen, ob dessen Gültigkeit endet und dieses erneuern lassen. Ich finde das einen klasse Service, den man ruhig mit einer kleinen Spende unterstützen darf!
Bevor man Letsencrypt nutzen kann, muss man alle Abhängigkeiten über den Debian Paketmanger installieren. Standardmäßig sind die benötigten Pakete aber nicht in den Paketquellen von Debian 8 - Codename Jessie - enthalten, deshalb müssen wir dazu die Backports in unseren Paketquellen aktivieren. Hierzu müssen wir die /etc/apt/sources.list Datei um folgende Zeile erweitern:
..
# jessie-backports
deb http://ftp.debian.org/debian jessie-backports main
Nun muss man die Paketquellen erneut auslesen und kann den certbot installieren, welcher Letsencrypt bereitstellt:
$ apt-get update
$ apt-get install certbot -t jessie-backports
Das Programm certbot erledigt für uns den Ausstellvorgang der SSL-Zertifikate. Dabei wird ein Antrag an den Letsencrypt-Service gestellt, den unser Webserver beantworten muss. Das bedeutet, dass wir unseren Nginx dafür noch etwas anpassen müssen. Wir benötigen die Datei /etc/nginx/snippets/letsencrypt.conf mit folgendem Inhalt:
location ^~ /.well-known/acme-challenge/ {
default_type text/plain;
root /var/www/letsencrypt;
}
Diese Datei müssen wir nun in unsere Website-Konfiguration einbinden, z.B. in die Datei /etc/nginx/sites-enabled/default:
server {
listen 80 default_server;
include /etc/nginx/snippets/letsencrypt.conf;
..
..
}
}
Nun legen wir noch die Verzeichnisse für Letsencrypt an, welche für die Zertifikats-Anfragen, das Logging, usw. benutzt werden und weisen es unserem sudo-User serveradmin zu:
$ mkdir -p /var/www/letsencrypt/.well-known/acme-challenge
$ chown -R serveradmin:serveradmin /var/www/letsencrypt
$ mkdir -p /var/lib/letsencrypt
$ chown -R serveradmin:serveradmin /var/lib/letsencrypt
$ mkdir -p /var/log/letsencrypt
$ chown -R serveradmin:serveradmin /var/log/letsencrypt
$ mkdir -p /etc/letsencrypt
$ chown -R serveradmin:serveradmin /etc/letsencrypt
Als nächstes laden wir die Konfiguration unseres Webservers neu und loggen uns als serveradmin ein:
$ /etc/init.d/nginx reload
$ su - serveradmin
Versuchen wir nun ein Zertifikat im Test-Modus (--dry-run) für unsere Website ausstellen zu lassen:
$ certbot certonly --rsa-key-size 4096 --dry-run --webroot -w /var/www/letsencrypt -d eureDomain.de -d www.eureDomain.de
Hinweis: In dem Beispiel sind 2 Domains für ein Zertifikat angegeben, da meistens alle Websites mit und ohne führendes www aufrufbar sein sollen! Wird nur eine Domain angegeben, wird das Zertifikat für die andere Variante als ungültig angezeigt. Unter welchen Domains eure Website verfügbar sein soll, kann man auch der Nginx-Konfiguration entnehmen, indem man in der Zeile server_name nachschaut.
Wird nach Abschluss des Kommandos die Ausschrift - The dry run was successful. angezeigt, ist alles korrekt eingerichtet. Falls der Abruf nicht erfolgreich gewesen ist, dann probieren wir es später nochmal beim Einrichten der Website.
Python's virtuelle Umgebung und Website anlegen
Wir sind immer noch als sudo-User eingeloggt und nehmen alle weiteren Einstellungen vor. Zunächst legen wir die virtuelle Python Umgebung namens «web_project» an:
$ mkvirtualenv -p /usr/bin/python3 web_project
Danach ist man dann automatisch in dieser Umgebung eingeloggt, was man an dem Prompt Präfix in der Console sieht:
(web_project) $
Nun aktualisieren wir noch den Python Package-Installer pip:
(web_project) $ pip install --upgrade pip
Als nächstes richten wir das Webroot für die Website ein und holen den aktuellen Sourcecode des Projektes MyWebsite aus dem GIT-Repository:
(web_project) $ chown serveradmin:www-data /var/customers/webs
(web_project) $ chmod g+s /var/customers/webs
(web_project) $ chmod 775 /var/customers/webs
(web_project) $ cd /var/customers/webs
(web_project) $ git clone https://rhodecode.dsoft-app-dev.de/MyWebsite web_project
Das Wurzelverzeichnis für die Websites haben wir somit unserem sudo-User und der Gruppe www-data zugewiesen und mit dem s-Bit versehen. Somit kann man Änderungen an den Dateien vornehmen und der Webserver kann auf diese lesend zugreifen.
Das Projekt MyWebsite verfügt über einen Unterodner namens media/, welcher es ermöglicht, Daten auszutauschen, welche auf der Website verlinkt werden können. Wir stellen noch einmal sicher, das dieser und auch seine Unterordner die richtigen Schreibrechte gesetzt haben:
(web_project) $ chmod 775 web_project
(web_project) $ find web_project/media -type d -print -exec chmod 775 {} \;
Hinweis: Das Projekt MyWebsite ist öffentlich zugänglich. Hat man aber GIT-Projekte, welche über Zugangsdaten gesichert sind und möchte man zukünftig diese Anmeldedaten speichern, so kann man das folgendermaßen erreichen:
(web_project) $ cd /var/customers/webs/web_project
(web_project) $ git config credential.helper store
(web_project) $ git pull
Da die Website verschiedene Python Pakete benötigt, müssen wir diese nun installieren lassen. Das geht mit dem vorhin aktualisierten Python Package-Installer und der im Webroot vorhandenen Datei requirements.prod:
(web_project) $ pip install -r requirements.prod
Prinzipiell können wir die virtuelle Umgebung von Python wieder verlassen, indem wir diese einfach deaktivieren:
(web_project) $ deactivate
Die Website stellt neben seinen Inhalten, auch eine Nutzerverwaltung für die Mitglieder bereit. Diese Daten benötigen eine Datenbank auf dem vServer. Hierbei greift man meist auf die bereits vorinstallierte MySQL Server Instanz zurück und legt dort ein neues Datenbank-Schema speziell für die Website an:
$ mysql -u root -p
mysql> create database web_project_db;
mysql> create user 'web_project_dba'@'localhost' identified by '<password>';
mysql> grant all privileges on web_project_db.* to 'web_project_dba'@'localhost' identified by '<password>';
mysql > exit;
Bei dem oberen Befehlen benötigt man den die Anmeldedaten des root-Users der MySQL Server Instanz. Diese sind verschieden von den Anmeldedaten des root-Users des vServers, auch wenn beide root heißen! Angelegt wird dann ein Datenbank-Schema namens web_project_db mit dem User web_project_dba und zugehörigem Passwort.
Hinweis: Anstatt den MySQL Server zu nutzen, kann man auch andere Datenbanken zum Einsatz bringen. Es ist sogar möglich, die Datenbank in einer SQLite Datei zu speichern. In diesem Fall muss man aber darauf achten, dass die richtigen Rechte für diese Datei gesetzt sind:
$ chmod 664 web_project/db.sqlite3
Nach der Vorbereitung der Datenbank, müssen wir noch die Einstellungen in der Website vornehmen, damit diese auch weiß, wohin sie die Daten speichern muss. Diese Einstellungen erfolgen in der Datei web_project/website/settings/settings_secure.py. Diese Datei gibt es initial noch nicht, sie kann aber aus dem Template web_project/website/settings_secure.example.py angelegt werden. Hier nun die Datenbankkonfiguration:
..
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
FORCE_SCRIPT_NAME = ''
ALLOWED_HOSTS = []
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'web_project_db',
'USER': 'web_project_dba',
'PASSWORD': '<password>',
'HOST': '127.0.0.1',
'PORT': '3306',
'OPTIONS': {
'autocommit': True,
},
}
}
..
Es gibt noch weitere Stellen in dieser Konfigurationsdatei zu ändern, was man aber in der Datei web_project/README.md nachlesen kann. Wichtig ist aber noch DEBUG = False zu setzen und ALLOWED_HOSTS mit den Einträgen aus server_name der Nginx-Konfiguration zu ergänzen (siehe Webserver-Konfiguration und Initialisierung der Website).
Weiter gilt es noch einige Dateien anzulegen, bzw. zu editieren. Fangen wir an mit der Datei web_project/.env:
$ cd /var/customers/webs/web_project
$ nano .env
In diese .env Datei tragen wir den Namen der virtuellen Python Umgebung ein, was uns dann den automatischen Wechsel in diese Umgebung mittels autoenv ermöglicht:
workon web_project
Testen können wir das Ganze, indem wir nun ein Verzeichnis höher wechseln und dann zurück ins Webroot gehen. Danach sollten wir wieder den Namen des virtuellen Environments als Präfix vor dem Prompt haben:
$ cd ..
$ cd web_project
(web_project) $
Dann benötigen wir noch die Einstellungen für unseren uWSGI Proxy, den wir später noch installieren müssen. Dazu legen wir die Datei web_project/uwsgi_params mit folgendem Inhalt an:
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
Nun benötigen wir noch die Datei web_project/sites_uwsgi.ini:
# sites_uwsgi.ini file
[uwsgi]
plugins = python3
# Django-related settings
# the base directory (full path)
chdir = /var/customers/webs/web_project
# Django's wsgi file
module = website.wsgi
# the virtualenv (full path)
home = /var/customers/venvs/web_project
# process-related settings
# master
master = true
# maximum number of worker processes
processes = 5
# the socket (use the full path to be safe
socket = /var/customers/webs/web_project/sites.sock
# ... with appropriate permissions - may be needed
uid = www-data
gid = www-data
chmod-socket = 666
umask = 0002
# clear environment on exit
vacuum = true
Damit hätten wir nun die grundlegenden Einstellungen für die Website Applikation getroffen. Fehlt noch die Webserver-Konfiguration, damit die Website auch dargestellt werden kann.
Webserver-Konfiguration und Initialisierung der Website
Wir nähern uns langsam der Zielgeraden. In den oberen Abschnitten haben wir ja schon richtig viel konfiguriert und auch im Artikel Nginx vor Apache - Reverse-Proxy mit Froxlor die Basis dafür geschaffen, dass wir nun unser erstes web_project mittels unseres Webservers anzeigen lassen können. Vorher benötigen wir aber noch den uWSGI Proxy, welcher sozusagen unsere in Django/Python geschriebenen Website, das Leben einhaucht, indem er den Pyhton Code ausführt. Alle statischen Inhalte der Website selbst, wie Bilder, Styles, o.ä., werden über Nginx ausgeliefert - Nginx und uWSGI funktionieren sozusagen als Tandem. Also los geht es mit der Installation von uWSGI als sudo-User:
(web_project) $ sudo apt-get install uwsgi-emperor uwsgi-plugin-python3
Damit wird nun ein Verzeichnis /etc/uwsgi-emperor angelegt, indem die Hauptkonfiguration des uWSGI-Proxys in der Datei emperor.ini vorliegt. An dieser braucht man im Grunde nichts ändern, sondern widmet sich dem Unterverzeichnis vassals/. In diesem Unterverzeichnis werden alle Konfiguration abgelegt, welche vom uWSGI-Proxy gestartet werden sollen. Weiter oben haben wir die Datei sites_uwsgi.ini angelegt, welche wir nun dorthin verlinken:
(web_project) $ cd /etc/uwsgi-emperor/vassals
(web_project) $ sudo ln -s /var/customers/webs/web_project/sites_uwsgi.ini web_project.ini
Nun kann man im Logfile /var/log/uwsgi/emperor.log kontrollieren, ob die Prozesse des Vassals auch gestartet wurden. Jedes Mal, wenn wir eine Änderung am Python-Code der Website vornehmen, müssen wir den uWSGI-Prozess neustarten. Am Einfachsten geht das, indem man die Konfigurationsdatei vom Zeitstempel her aktualisiert. Der uWSGI-Emperor bekommt das mit und startet dann automatisch die Vassals neu:
(web_project) $ sudo touch /etc/uwsgi-emperor/vassals/web_project.ini
Damit hätten wir den uWSGI-Proxy startklar, fehlt noch die Nginx Konfiguration. Dazu legen wir nun die Datei /etc/nginx/sites-available/20_manual_ssl_vhost_web_project.conf mit folgendem Inhalt an, wobei der Domain-Name webproject.eureDomain.de hierbei nur als Beispiel dient und auf den von euch registrierten geändert werden muss:
upstream web_project_app_server {
server unix:///var/customers/webs/web_project/sites.sock fail_timeout=0;
}
server {
listen <youripaddress>:80;
server_name webproject.eureDomain.de www.webproject.eureDomain.de;
include /etc/nginx/snippets/letsencrypt.conf;
access_log /var/customers/logs/web_project-access.log combined;
error_log /var/customers/logs/web_project-error.log error;
}
Diese Konfiguration ist aber nur die halbe Wahrheit, denn sie ermöglicht erstmal nur, dass wir mittels Letsencrypt nun das korrekte Zertifikat für unsere neue Domain abrufen können. Dies sollten wir tun, um die eigentliche SSL-Konfiguration unserer Seite nutzen zu können. Rufen wir nun also unser SSL-Zertifikat ab:
$ certbot certonly --rsa-key-size 4096 --webroot -w /var/www/letsencrypt -d webproject.eureDomain.de -d www.webproject.eureDomain.de
Wichtig ist, dass ihr alle Domain-Namen angebt, unter dem diese Website angezeigt werden soll. Im Normalfall ist das die Variante eurer Website mit und ohne www-Präfix. Erstellt wird dann ein Bundle-Zertifikat, welches beide Domain-Namen enthält. Vergesst ihr eines, meckert später eurer Browser rum, dass die Website kein gültiges Zertifikat aufweist und möglicherweise kompromitiert wurde.
War der Zertifikatsabruf erfolgreich, dann könnt ihr die Nginx-Konfiguration wie folgt vervollständigen:
upstream web_project_app_server {
server unix:///var/customers/webs/web_project/sites.sock fail_timeout=0;
}
server {
listen <youripaddress>:80;
server_name webproject.eureDomain.de www.webproject.eureDomain.de;
include /etc/nginx/snippets/letsencrypt.conf;
access_log /var/customers/logs/web_project-access.log combined;
error_log /var/customers/logs/web_project-error.log error;
if ($request_uri !~ ^/.well-known/acme-challenge/\w+$) {
return 301 https://$host$request_uri;
}
}
server {
listen <youripaddress>:443 ssl http2;
server_name webproject.eureDomain.de www.webproject.eureDomain.de;
ssl_protocols TLSv1 TLSv1.2;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS:!DH:!AES128;
ssl_ecdh_curve secp384r1;
ssl_prefer_server_ciphers on;
ssl_certificate /etc/letsencrypt/live/webproject.eureDomain.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/webproject.eureDomain.de/privkey.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/webproject.eureDomain.de/fullchain.pem;
include /etc/nginx/snippets/letsencrypt.conf;
access_log /var/customers/logs/web_project-access.log combined;
error_log /var/customers/logs/web_project-error.log error;
root /var/customers/webs/web_project/;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
include /var/customers/webs/web_project/uwsgi_params;
uwsgi_pass web_project_app_server;
break;
}
# Django configuration
charset utf-8;
location /media {
alias /var/customers/webs/web_project/media;
}
location /static {
alias /var/customers/webs/web_project/static;
}
}
Diese Nginx-Konfiguration müssen wir nun noch in das Verzeichnis /etc/nginx/sites-enabled verlinken:
(web_project) $ cd /etc/nginx/sites-enabled
(web_project) $ sudo ln -s ../sites-available/20_manual_ssl_vhost_web_project.conf .
Bevor wir nun den Webserver neustarten, damit er die Konfiguration auch lädt, müssen wir noch die Website Applikation initialisieren. Dazu wechseln wir zurück ins Webroot und führen die Datenbank-Migration durch, wobei nun alle notwendigen Datenbank-Objekte angelegt werden:
(web_project) $ cd /var/customers/webs/web_project
(web_project) $ ./manage.py migrate
Dann fehlen womöglich noch ein paar statische Dateien (Styles, Javascripte, ..), welche nicht auf dem GIT-Server liegen. Bevor man diese erzeugen kann, muss man nochmal in die Datei web_project/website/settings/settings_secure.py für die Einstellungen der Website gehen und folgende Änderung aktivieren:
..
# As we don't use static subfolders in our apps, to prevent some conflicts, we have to
# comment the STATIC_ROOT here. The STATIC_ROOT and STATICFILES_DIRS cannot be the same,
# but in development the STATICFILES_DIRS are only served.
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
# STATICFILES_DIRS = (
# os.path.join(BASE_DIR, 'static'),
# )
STATIC_URL = '/static/'
..
Nun kann man die statischen Dateien mit dem folgenden Befehl erzeugen:
(web_project) $ cd /var/customers/webs/web_project
(web_project) $ ./manage.py collectstatic
Danach macht man die Konfiguration an der Datei web_project/website/settings/settings_secure.py rückgängig:
..
# As we don't use static subfolders in our apps, to prevent some conflicts, we have to
# comment the STATIC_ROOT here. The STATIC_ROOT and STATICFILES_DIRS cannot be the same,
# but in development the STATICFILES_DIRS are only served.
# STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)
STATIC_URL = '/static/'
..
Als letzte Schritte muss man nun noch den uWSGI-Proxy und den Nginx-Server neustarten, bzw. deren Konfiguration neuladen:
(web_project) $ sudo touch /etc/uwsgi-emperor/vassals/web_project.ini
(web_project) $ sudo /etc/init.d/nginx reload
Wenn ihr nun euren Web-Browser öffnet und eure Website mit eurem Namen, aus obigen Bsp. http://webproject.eureDomain.de, aufruft, solltet ihr auf die sichere HTTPS-Variante umgeleitet werden und hoffentlich die noch recht leere Startseite anzeigen.
Nun sind wir auch schon wieder am Ende dieses etwas längeren Artikels. Ich hoffe, dass dieser euch geholfen hat. Solltet ihr Fragen haben, wendet euch über das Kontaktformular an mich. Wollt ihr mich unterstützen, damit ich noch viele andere spannende Artikel schreiben kann, dann schaut bitte in meiner Rubrik Crowdfunding vorbei.