SSD Nodes Learn 🎉 VPS ab $4.99/Monat
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-08-07

HTTP erklärt: Leitfaden für Serveradministratoren

HTTP für Serveradministratoren erklärt: Methoden, Statuscodes, wichtige Header, nginx-Logs sowie die Rolle von TLS und HTTP/3 im Betrieb.

Was ist HTTP?

HTTP (Hypertext Transfer Protocol) ist ein Regelwerk, mit dem ein Client und ein Webserver Daten anfordern und zurücksenden. Der Client sendet eine Anfrage. Sie enthält eine Methode wie GET, einen Pfad wie /pricing, eine Protokollversion, eine Liste von Headern und manchmal einen Body. Der Server antwortet mit einem Statuscode wie 200, gefolgt von seinen eigenen Headern und normalerweise einem Body. Jeder Seitenaufruf und jeder API-Aufruf (Application Programming Interface) auf Ihrem Server besteht aus genau diesem wiederholten Austausch.

HTTP speichert selbst keinen Zustand. Der Server merkt sich nicht, was Sie vor einer Sekunde angefordert haben. Alles, was sich wie ein Speicher verhält, beispielsweise eine Anmeldesitzung, wird daher bei jeder einzelnen Anfrage in einem Header übertragen. Diese Eigenschaft erklärt vieles, was folgt: Caching wird vollständig über Header gesteuert, und ein Load Balancer kann Ihre nächste Anfrage an ein anderes Backend senden, ohne dass etwas dadurch beeinträchtigt wird.

Im Folgenden wird dieses Modell aus Sicht des Servers beschrieben: in Ihrem Access-Log und in Ihrer nginx-Konfiguration.

Eine unveränderte Anfrage und Antwort mit Erläuterungen

Hier sehen Sie eine vollständige HTTP/1.1-Anfrage. Eine Leerzeile beendet die Header. Alles danach ist der Body. Eine GET hat normalerweise keinen Body.

GET /pricing HTTP/1.1
Host: example.com
User-Agent: curl/8.5.0
Accept: */*
Accept-Encoding: gzip
  • GET ist die Methode. Sie legt fest, welche Aktion ausgeführt werden soll. GET liest Daten, POST sendet Daten, PUT ersetzt Daten, DELETE entfernt Daten und HEAD fragt die Header einer GET ohne Body ab.
  • /pricing ist der Pfad. Der Hostname ist nicht Bestandteil der Request-Zeile. Deshalb gibt es den nächsten Header.
  • HTTP/1.1 ist die Protokollversion, die der Client verwendet.
  • Host: example.com bezeichnet die Website, die der Client anfordert. HTTP/1.1 schreibt diesen Header vor. nginx antwortet auf eine Anfrage ohne diesen Header mit 400 Bad Request.
  • Die übrigen Header enthalten Präferenzen. Accept-Encoding: gzip gibt an, dass der Client Daten dekomprimieren kann. Der Server darf den Body daher komprimieren.

Die Antwort hat denselben Aufbau. Oben steht eine Statuszeile.

HTTP/1.1 200 OK
Date: Thu, 06 Aug 2026 09:12:44 GMT
Server: nginx
Content-Type: text/html; charset=utf-8
Content-Length: 5310
Cache-Control: public, max-age=300

<!doctype html>...
  • 200 OK ist der Statuscode mit seiner Begründung. Entscheidend ist der Code. Die Begründung dient nur zur Information und wird von Clients ignoriert.
  • Content-Type teilt dem Client mit, wie er die folgenden Bytes behandeln soll.
  • Content-Length gibt die Größe des Bodys in Bytes an. Der Client weiß dadurch, wo der Body endet. Wenn die Größe vorab nicht bekannt ist, sendet der Server stattdessen Transfer-Encoding: chunked und kennzeichnet das Ende mit einem Chunk der Länge 0.
  • Cache-Control teilt dem Browser und allen dazwischengeschalteten Caches mit, wie lange sie diese Antwort speichern dürfen.
  • Die Leerzeile nach den Headern trennt die Header in beiden Richtungen vom Body.

Header-Namen unterscheiden nicht zwischen Groß- und Kleinschreibung. Jede Zeile endet mit einem Carriage Return gefolgt von einem Line Feed und nicht mit einem einzelnen Newline. Sie werden diese Zeichen nicht von Hand eingeben. In einem Paketmitschnitt werden Sie ihnen jedoch begegnen.

Um ein echtes Anfrage-Antwort-Paar zu beobachten, führen Sie den folgenden Befehl gegen eine Website aus, die Ihnen gehört:

curl -sS -o /dev/null -D - https://example.com/

-D - schreibt die Response-Header in Ihr Terminal. -o /dev/null verwirft den Body. Verwenden Sie dies bevorzugt gegenüber curl -I, weil -I eine HEAD-Anfrage sendet. Ein Anwendungsserver, der HEAD anders behandelt als GET, und das tun viele, zeigt Ihnen dadurch Header, die kein Browser empfängt. curl -v gibt beide Seiten aus. Request-Zeilen werden mit > und Response-Zeilen mit < gekennzeichnet.

So sieht die Request-Zeile in Ihrem nginx-Access-Log aus

nginx liefert ein combined-Logformat mit. Es ist wie folgt definiert:

log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

Eine davon erzeugte Zeile:

203.0.113.45 - - [06/Aug/2026:09:12:44 +0000] "GET /pricing HTTP/1.1" 200 5310 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) Chrome/127.0.0.0 Safari/537.36"
  • 203.0.113.45 ist $remote_addr, die Adresse, von der aus die TCP-Verbindung (Transmission Control Protocol) geöffnet wurde. Hinter einem Proxy ist dies die Adresse des Proxys, nicht die des Besuchers.
  • Das erste - ist ein fester Platzhalter. Das zweite ist $remote_user und wird nur ausgefüllt, wenn HTTP-Basic-Authentifizierung verwendet wird.
  • "GET /pricing HTTP/1.1" ist $request, also die Request-Zeile exakt in der Form, in der sie eingegangen ist.
  • 200 ist der Status, den Ihr Server zurückgegeben hat, nicht der Status, den der Besucher wahrgenommen hat.
  • 5310 ist $body_bytes_sent, also ausschließlich der Response-Body. Response-Header werden nicht mitgezählt. Dieser Wert ist daher immer kleiner als die tatsächlich gesendete Byte-Anzahl.
  • Die letzten beiden Felder in Anführungszeichen sind Referer und User-Agent. Beide stammen vom Client und können daher beliebige Inhalte enthalten.

Da $request unverändert übernommen wird, erscheint auch fehlerhafter Inhalt unverändert. Ein Client, der an Ihrem unverschlüsselten Port 80 TLS (Transport Layer Security) verwendet, hinterlässt eine 400-Zeile, deren Request-Feld mit maskierten Bytes wie "\x16\x03\x01\x02\x00\x01" beginnt. \x16 ist der TLS-Handshake-Record-Typ. Diese Bytes sind daher der Anfang eines ClientHello und überhaupt keiner Request-Zeile. Ihr Server verhält sich korrekt. Etwas richtet HTTPS an einen HTTP-Port.

Nehmen Sie außerdem $server_protocol in Ihr Logformat auf. Damit wird HTTP/1.1, HTTP/2.0 oder HTTP/3.0 ausgegeben. So lässt sich am schnellsten nachweisen, dass eine Protokolländerung tatsächlich wirksam geworden ist.

Was die gängigen Statuscodes bedeuten, wenn die eigene Website sie zurückgibt

Die erste Ziffer bezeichnet die Klasse. Diese Klasse sollten Sie zuerst prüfen.

2xx bedeutet: Die Anfrage war erfolgreich. 200 OK steht für einen normalen Lesezugriff. 201 Created wird nach einem POST zurückgegeben, durch das eine Ressource erstellt wurde. 204 No Content steht für eine erfolgreiche Anfrage ohne Antwortinhalt und ist die übliche Antwort auf ein DELETE.

3xx bedeutet: Die Anfrage soll an anderer Stelle fortgesetzt werden. 301 ist dauerhaft. Browser speichern diesen Status oft hartnäckig im Cache, teilweise bis der Benutzer sein Profil löscht. Ein 301, das auf den falschen Hostnamen verweist, lässt sich deshalb nur schwer korrigieren. Verwenden Sie 302, solange Sie eine Weiterleitung noch testen. 304 Not Modified bedeutet Erfolg und keinen Fehler: Der Client hat If-None-Match mit einem ETag (Entity-Tag) gesendet, das Sie weiterhin erkennen. Deshalb haben Sie nur Header und keinen Antwortinhalt zurückgegeben. Ein Log mit vielen 304s zeigt, dass das Caching funktioniert.

4xx bedeutet: Die Anfrage war fehlerhaft. 400 Bad Request steht für fehlerhafte Eingaben. 401 Unauthorized bedeutet tatsächlich nicht authentifiziert und muss einen WWW-Authenticate-Header enthalten, der das Schema angibt. 403 Forbidden bedeutet, dass die Anfrage verstanden, aber trotzdem abgelehnt wurde. 404 Not Found bezeichnet einen nicht vorhandenen Pfad. 405 Method Not Allowed bezeichnet den richtigen Pfad mit der falschen Methode. Das gibt beispielsweise ein POST für ein statisches Dateiverzeichnis zurück. 413 bedeutet, dass der Anfrageinhalt größer als nginx's client_max_body_size ist. Dieser Wert beträgt standardmäßig 1 Megabyte. Das Fehler-Log bestätigt dies mit client intended to send too large body.

Ein 403 bei einer statischen Datei liegt fast immer am Dateisystem und nicht an einer HTTP-Regel. Lesen Sie /var/log/nginx/error.log, bevor Sie eine Konfiguration ändern. open() "/srv/site/index.html" failed (13: Permission denied) bedeutet, dass der nginx-Worker-Benutzer die Datei nicht lesen kann. Meist fehlt einem übergeordneten Verzeichnis die Ausführungsberechtigung für andere Benutzer. directory index of "/srv/site/" is forbidden bedeutet, dass der Pfad auf ein Verzeichnis aufgelöst wurde, das keine Indexdatei enthält, während autoindex deaktiviert ist.

5xx bedeutet: Auf Ihrer Seite ist ein Fehler aufgetreten. 500 bezeichnet einen nicht behandelten Fehler in Ihrer Anwendung. 502 Bad Gateway bedeutet, dass nginx keine verwendbare Antwort vom Upstream erhalten konnte. Das Fehler-Log nennt die Ursache: connect() failed (111: Connection refused) while connecting to upstream bedeutet, dass unter der Adresse in proxy_pass kein Prozess auf Verbindungen wartet. 504 Gateway Timeout bedeutet, dass der Upstream die Verbindung angenommen und innerhalb von proxy_read_timeout keine Antwort gesendet hat. Standardmäßig beträgt dieser Zeitraum 60 Sekunden. Das Log vermerkt dies als upstream timed out (110: Connection timed out) while reading response header from upstream. 503 Service Unavailable ist eine absichtliche Ablehnung. Beachten Sie, dass der nginx-eigene Rate Limiter 503 zurückgibt, weil limit_req_status standardmäßig auf 503 gesetzt ist. Wenn Sie in Ihrem Log nach 429 Too Many Requests suchen und stattdessen 503 finden, liegt das daran. Setzen Sie limit_req_status 429;, um den korrekten Statuscode zu erhalten.

Die wichtigen Header beim Betrieb des Servers

Host wählt die Site aus. Eine IP-Adresse kann Hunderte Hostnamen bedienen. nginx gleicht Host mit server_name ab, um zu entscheiden, welcher server-Block antwortet. Wenn keine Übereinstimmung vorliegt, verwendet nginx den Standardserver. Das ist der erste Block, der an dieser Adresse und diesem Port lauscht, sofern kein anderer als default_server markiert ist. Wenn ein neuer virtueller Host die falsche Site liefert, liegt das fast immer daran: Der Name stimmte nicht überein. Die Anfrage fiel deshalb auf den Standardserver zurück. Testen Sie dies, ohne DNS zu ändern:

curl -sS -o /dev/null -D - -H 'Host: app.example.com' http://127.0.0.1/

User-Agent ist eine vom Client bereitgestellte Selbstbeschreibung und enthält freien Text. Verwenden Sie ihn beim Lesen von Logs als Hinweis. Verwenden Sie ihn niemals als Steuerungsmerkmal. Ein Client, der darüber lügen will, tut es einfach. Wenn Sie einen Scraper anhand von User-Agent blockieren, filtern Sie daher nur die höflichen Clients heraus.

Content-Type legt fest, wie die Bytes interpretiert werden: application/json für eine API-Anfrage und text/html; charset=utf-8 für eine Seite. nginx ordnet Dateierweiterungen mit /etc/nginx/mime.types Inhaltstypen zu. Das mitgelieferte nginx.conf legt default_type application/octet-stream; fest. Eine Datei mit einer nginx unbekannten Erweiterung wird dadurch als Download angeboten, statt gerendert zu werden. Sichtbar ist das an einer Seite, die ohne Formatierung geladen wird, während die Browserkonsole Refused to apply style from ... because its MIME type ('text/plain') is not a supported stylesheet MIME type ausgibt. MIME steht hier für Multipurpose Internet Mail Extensions. Dieses Schema liefert die Namen dieser Inhaltstypen.

Cache-Control steuert jeden Cache zwischen Ihrem Server und dem Leser. public, max-age=31536000, immutable eignet sich für Ressourcen, deren Dateiname einen Inhaltshash enthält. Der Name ändert sich dann zusammen mit dem Inhalt. no-store gehört zu allen Inhalten, die benutzerspezifisch sind. Ein gemeinsamer Cache, der eine angemeldete Seite speichert, würde sie sonst der nächsten Person mit derselben URL liefern. private ist die mittlere Einstellung: Der Browser darf den Inhalt behalten, ein gemeinsamer Cache jedoch nicht.

X-Forwarded-For wird benötigt, weil ein Proxy den Besucher verbirgt. Sobald eine Anfrage einen Reverse Proxy durchlaufen hat, ist $remote_addr die Adresse des Proxys. Ihre Logs, Ihre Geolokalisierung und Ihre Ratenbegrenzung sehen dann nur einen Client. Der Proxy muss die ursprüngliche Adresse weitergeben:

proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

Der empfangende Server muss anschließend angewiesen werden, dieser Adresse zu vertrauen. Dabei muss genau festgelegt werden, wem er vertrauen darf:

set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;

Listen Sie nur Bereiche auf, die Sie kontrollieren. X-Forwarded-For ist reiner Text, den jeder Client senden kann. set_real_ip_from 0.0.0.0/0; ermöglicht es einem Besucher daher, die geloggte Adresse und die von der Ratenbegrenzung gezählte Adresse selbst festzulegen.

X-Forwarded-Proto verhindert einen bestimmten und sehr häufigen Fehler. Ihr Proxy beendet TLS und leitet die Anfrage über unverschlüsseltes HTTP an die Anwendung weiter. Die Anwendung sieht eine unverschlüsselte Anfrage, entscheidet, dass der Besucher HTTPS verwenden soll, und antwortet mit 301 https://example.com/. Der Browser folgt dieser Antwort. Der Proxy beendet erneut TLS und leitet wieder unverschlüsseltes HTTP weiter. Die Schleife wiederholt sich, bis der Browser mit ERR_TOO_MANY_REDIRECTS abbricht. Wenn Sie X-Forwarded-Proto: https senden, teilen Sie der Anwendung mit, dass der Besucher bereits HTTPS verwendet. Sie beendet dann die Weiterleitungen.

HTTP/1.1 vs HTTP/2 vs HTTP/3: Was sich für Sie ändert

HTTP/1.1 verwendet Text und verarbeitet pro Verbindung jeweils eine Anfrage. Connection: keep-alive ermöglicht, dass die nächste Anfrage dieselbe TCP-Verbindung wiederverwendet. Dadurch entfällt der Aufwand für den Verbindungsaufbau. Antworten werden jedoch weiterhin in der Reihenfolge ihrer Anfragen zurückgegeben. Eine langsame Antwort blockiert alles, was dahinter wartet. Das wird als Head-of-Line-Blocking bezeichnet. Browser umgehen dieses Problem, indem sie gleichzeitig mehrere Verbindungen zu demselben Hostnamen öffnen.

HTTP/2 verwendet weiterhin dieselben Methoden und Statuscodes, ändert aber die Rahmung in ein Binärformat. Viele Anfragen teilen sich eine Verbindung als unabhängige Streams. Wiederholte Header-Texte werden komprimiert. Das ist relevant, weil eine moderne Anfrage viele Header enthält. Die Verbindung läuft weiterhin über TCP. Daher hält ein verlorenes Paket jeden Stream auf dieser Verbindung an, bis die erneute Übertragung eintrifft. Das Head-of-Line-Blocking ist nicht verschwunden. Es ist von HTTP auf die Transportschicht verlagert worden. Server Push war Bestandteil von HTTP/2 und wird praktisch nicht mehr verwendet, weil Chrome die Unterstützung dafür 2022 entfernt hat.

HTTP/3 behält erneut dieselbe Semantik bei und ersetzt TCP durch QUIC, ein auf UDP (User Datagram Protocol) basierendes Transportprotokoll. QUIC-Streams sind bis zur untersten Schicht unabhängig. Daher hält ein verlorenes Paket nur den Stream an, zu dem es gehörte. TLS 1.3 ist in den QUIC-Handshake integriert und nicht darüber geschichtet. Deshalb benötigt eine neue Verbindung weniger Round-Trips. Daraus ergeben sich zwei praktische Konsequenzen: UDP-Port 443 muss in jeder Firewall entlang des Pfads geöffnet sein. Außerdem führen Netzwerke, die UDP drosseln oder blockieren, dazu, dass Clients auf HTTP/2 zurückfallen.

Was sich für Sie konkret ändert: Browser beginnen nie mit HTTP/3. Sie verbinden sich über HTTP/2 oder HTTP/1.1, sehen im Response einen Alt-Svc: h3=":443"; ma=86400-Header und verwenden HTTP/3 bei späteren Verbindungen zu diesem Host. Der Header ist daher keine optionale Dekoration. Er dient zur Erkennung. In nginx wurde HTTP/2 ab Version 1.25.1 zu einer eigenen Direktive (http2 on; innerhalb des server-Blocks; sie ersetzt den alten listen ... http2-Parameter). QUIC kam in der Mainline-Version 1.25.0 hinzu. Eine HTTP/3-Site benötigt dort listen 443 quic reuseport; zusätzlich zu dem gewöhnlichen listen 443 ssl;.

Proxyserver unterscheiden sich hier beim Reifegrad. Prüfen Sie daher die Version, die Sie tatsächlich einsetzen. Stand August 2026 stellt Caddy HTTP/3 standardmäßig ohne Konfiguration bereit. nginx benötigt den expliziten quic-Listener sowie den oben beschriebenen Alt-Svc-Header. Traefik aktiviert HTTP/3 pro Entry Point über eine explizite http3-Option. Wenn Sie TLS bei Traefik vor mehreren Docker-Anwendungen terminieren, wird die von Besuchern verwendete Protokollversion dort festgelegt. Die Verbindung vom Proxy zu Ihrem Container läuft unabhängig von der Aushandlung durch den Browser normalerweise über einfaches HTTP/1.1.

Überprüfen Sie die Konfiguration, statt Annahmen zu treffen. curl --http3 -sS -o /dev/null -D - https://example.com/ funktioniert nur, wenn curl -V HTTP3 unter seinen Funktionen aufführt. Die meisten Distributions-Builds enthalten diese Funktion nicht. Die zuverlässigste Prüfung erfolgt über Ihr eigenes Log: Fügen Sie dem Format $server_protocol hinzu und lesen Sie ab, welches Protokoll reale Browser aushandeln. Prüfen Sie vorher, ob UDP 443 tatsächlich geöffnet ist. Eine Firewall, die nur TCP 443 erlaubt, lässt HTTP/3 unbemerkt fehlschlagen, während die Site weiterhin über HTTP/2 funktioniert. Zu wissen, welche Ports auf Ihrem Linux-Server geöffnet sind und auf Verbindungen warten, ist der erste Prüfschritt.

HTTPS: HTTP ist das Protokoll, TLS ist die Verschlüsselung

HTTPS ist kein separates Protokoll. Es handelt sich um dieselben Anfragen und Statuscodes, die innerhalb einer TLS-Sitzung übertragen werden. Port 80 überträgt sie unverschlüsselt, Port 443 verschlüsselt. Zuerst wird der TLS-Handshake abgeschlossen. Danach wird die HTTP-Anfrage innerhalb des verschlüsselten Kanals übertragen. Deshalb ist einem Zertifikatsproblem niemals ein Statuscode zugeordnet: Der Fehler tritt auf, bevor auch nur ein einziges HTTP-Byte gesendet wurde. Daher gibt es keine nummerierte Antwort.

Auf einem Server, der mehrere Websites hostet, ist die Reihenfolge wichtig. Das Zertifikat wird anhand von SNI (Server Name Indication) ausgewählt. Dabei handelt es sich um ein Feld im TLS-Handshake, das den Hostnamen unverschlüsselt überträgt, bevor ein HTTP-Header vorhanden ist. Der Server wählt daher zuerst anhand von SNI ein Zertifikat und anschließend anhand des Headers Host einen virtuellen Host. Das sind zwei getrennte Zuordnungen, die normalerweise übereinstimmen. Wenn sie es nicht tun, zeigt der Browser einen Namenskonflikt wie NET::ERR_CERT_COMMON_NAME_INVALID an und sendet überhaupt keine Anfrage, weil das Zertifikat Ihres Standardservers für einen Namen angeboten wurde, den es nicht abdeckt.

Für eine öffentliche Website sollten Sie ein gültiges Zertifikat beziehen und automatisch erneuern lassen. Certbot mit Let's Encrypt unter nginx trägt die Zertifikatspfade in Ihren Server-Block ein und richtet den Erneuerungs-Timer für Sie ein. Für einen Hostnamen, den keine öffentliche Zertifizierungsstelle validieren kann, etwa einen internen Namen oder eine nicht weiter spezifizierte IP-Adresse in Ihrem eigenen Netzwerk, ist ein selbst signiertes Zertifikat unter Ubuntu die sachgerechte Option. Sie müssen dann allerdings jedem Client mitteilen, dass er diesem Zertifikat vertrauen soll.

Sobald TLS funktioniert, leiten Sie alles von Port 80 an Port 443 weiter:

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

Fügen Sie Strict-Transport-Security nur hinzu, wenn Sie sicher sind. Der Header add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; weist Browser an, für diesen Hostnamen zwei Jahre lang kein unverschlüsseltes HTTP zu verwenden. Sie befolgen diese Vorgabe aus ihrem eigenen Cache. Das bedeutet, dass das spätere Entfernen des Headers die Einstellung nicht rückgängig macht. Beginnen Sie mit einem max-age von einigen Stunden. Prüfen Sie, ob wirklich jede Subdomain HTTPS verwendet, und erhöhen Sie den Wert anschließend.

FAQ

Was ist der Unterschied zwischen HTTP und HTTPS?

HTTPS ist HTTP innerhalb einer TLS-Sitzung (Transport Layer Security). Die Methoden und Statuscodes sind identisch. Die Bytes werden jedoch zwischen dem Client und der Instanz verschlüsselt, die TLS beendet. Außerdem wechselt der Standardport von 80 zu 443. Da der TLS-Handshake abgeschlossen wird, bevor das erste HTTP-Byte gesendet wird, erzeugt ein Zertifikatsfehler niemals einen Statuscode. Deshalb zeigt eine Browserwarnung zum Zertifikat einen Fehlernamen wie NET::ERR_CERT_COMMON_NAME_INVALID statt einer Zahl wie 403 an.

Warum gibt meine Website 502 Bad Gateway zurück?

Ein 502 von nginx bedeutet, dass nginx keine verwendbare Antwort vom Upstream erhalten konnte, an den es die Anfrage weiterleitet. Die Anfrage des Besuchers war daher in Ordnung, aber ein Dienst hinter nginx nicht. Lesen Sie /var/log/nginx/error.log. connect() failed (111: Connection refused) while connecting to upstream bedeutet, dass unter der Adresse und dem Port in proxy_pass nichts lauscht. Prüfen Sie daher, ob die Anwendung läuft und an die erwartete Adresse gebunden ist. no live upstreams while connecting to upstream bedeutet, dass jeder Server im Upstream-Block nach wiederholten Fehlern als nicht verfügbar markiert wurde. Vergleichen Sie dies mit 504 Gateway Timeout. Das bedeutet, dass der Upstream die Verbindung angenommen, aber innerhalb von proxy_read_timeout nicht geantwortet hat.

Warum zeigt mein Access-Log für jeden Besucher dieselbe IP-Adresse?

Weil $remote_addr die Adresse der TCP-Verbindung protokolliert. Hinter einem Reverse Proxy oder einem Content Delivery Network ist diese Adresse die des Proxy. Die Adresse des Besuchers steht stattdessen im Header X-Forwarded-For. Setzen Sie proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; auf dem Proxy. Setzen Sie anschließend auf dem empfangenden nginx set_real_ip_from auf den Adressbereich des Proxy und real_ip_header X-Forwarded-For;. Listen Sie nur Bereiche auf, die Sie kontrollieren. Dieser Header enthält beliebigen Text, den jeder Client senden kann. Wenn Sie ihm aus dem gesamten Internet vertrauen, kann ein Besucher die protokollierte Adresse und die Adresse für die Ratenbegrenzung selbst festlegen.

Muss ich HTTP/2 oder HTTP/3 aktivieren?

HTTP/2 sollten Sie aktivieren. Bei einer Website mit bereits eingerichtetem TLS ist dafür nur eine Direktive erforderlich. Außerdem entfällt dadurch die Begrenzung der Anfragen pro Verbindung, die Seiten mit vielen kleinen Dateien verlangsamt. HTTP/3 bringt einen geringeren und weniger sicheren Vorteil. Dafür benötigen Sie zusätzlich einen offenen UDP-Port 443 und einen Proxy-Build mit QUIC-Unterstützung. Beachten Sie, dass Browser erst dann auf HTTP/3 wechseln, wenn sie in einer früheren Antwort einen Header Alt-Svc erhalten haben. Ohne diesen Header ändert sich daher nichts, unabhängig davon, was in Ihrer listen-Zeile steht. Fügen Sie $server_protocol zu Ihrem Log-Format hinzu und messen Sie, welches Protokoll Besucher tatsächlich aushandeln, bevor Sie Zeit dafür aufwenden.

Was bedeutet 403 Forbidden, wenn die Datei vorhanden ist?

Auf einer statischen Website weist ein 403 normalerweise auf ein Dateisystem-Berechtigungsproblem und nicht auf eine HTTP-Regel hin. open() ... failed (13: Permission denied) in /var/log/nginx/error.log bedeutet, dass der nginx-Worker-Benutzer die Datei nicht lesen kann. Meist fehlt einem übergeordneten Verzeichnis die Ausführungsberechtigung für andere Benutzer. Der Dateimodus selbst ist dann nicht die Ursache. directory index of ... is forbidden bedeutet, dass die Anfrage auf ein Verzeichnis ohne Indexdatei aufgelöst wurde, während autoindex deaktiviert ist. Eine explizite deny-Regel im passenden location-Block gibt ebenfalls 403 zurück. Lesen Sie daher diesen Block, wenn das Error-Log keine Hinweise enthält.

#http#https#web-server#headers#http3