Zum Inhalt

cors: Korrektes CORS für NGINX, einschließlich Preflight und Vary

Erfordert den Pro-Plan (oder höher) des GetPageSpeed NGINX Extras-Abonnements.

Installation

Sie können dieses Modul in jeder RHEL-basierten Distribution installieren, einschließlich, aber nicht beschränkt auf:

  • RedHat Enterprise Linux 7, 8, 9 und 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 und Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-cors
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-cors

Aktivieren Sie das Modul, indem Sie Folgendes am Anfang von /etc/nginx/nginx.conf hinzufügen:

load_module modules/ngx_http_cors_module.so;

Dieses Dokument beschreibt nginx-module-cors v1.0.0 veröffentlicht am 15. August 2026.


CORS für NGINX, das die Teile korrekt behandelt, die das add_header-Rezept falsch macht.

Warum nicht einfach add_header verwenden?

Das map $http_origin + add_header Access-Control-Allow-*-Rezept wird von jedem kopiert, und es ist auf vier spezifische Arten fehlerhaft:

  • add_header wird pro Ebene vererbt oder ersetzt. Sobald ein location einen eigenen Header hinzufügt, verschwinden alle add_header-Anweisungen darüber — einschließlich Ihrer CORS-Header — stillschweigend. Nichts warnt Sie.
  • Preflight benötigt einen Kurzschluss. Eine OPTIONS-Preflight-Anfrage muss mit einem 204 und den richtigen Headern beantwortet werden, ohne Ihre Anwendung zu erreichen. Das von Hand zu tun bedeutet einen if-Block, und dieser interagiert schlecht mit try_files und proxy_pass.
  • add_header feuert nur auf einer Whitelist von Statuscodes, es sei denn, Sie übergeben always. Dadurch verlieren Ihre Fehlerantworten ihre CORS-Header, und der Browser meldet einen undurchsichtigen CORS-Fehler anstelle des tatsächlich aufgetretenen 404 oder 502.
  • Vary: Origin wird vergessen. Wenn die Antwort vom Ursprung der Anfrage abhängt und Sie dies nicht angeben, wird jeder vorgeschaltete gemeinsame Cache oder jedes CDN die Antwort eines Ursprungs bereitwillig einem anderen ausliefern. Dies ist ein Cache-Poisoning-Fehler und der häufigste in der Praxis.

Dieses Modul erledigt alle vier Punkte korrekt, als Header-Filter plus einen Preaccess-Phasen-Handler, ohne if-Blöcke.

Übersicht

location /api/ {
    cors                on;
    cors_origin         https://app.example.com https://*.staging.example.com;
    cors_methods        GET HEAD POST PUT DELETE;
    cors_headers        Authorization Content-Type;
    cors_expose_headers X-Total-Count;
    cors_credentials    on;
    cors_max_age        86400;

    proxy_pass http://backend;
}

Eine Preflight-Anfrage sieht dann wie folgt aus und erreicht backend nie:

$ curl -i -X OPTIONS https://api.example.com/api/things \
    -H 'Origin: https://app.example.com' \
    -H 'Access-Control-Request-Method: PUT'
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
Vary: Origin, Access-Control-Request-Method

Direktiven

cors

Syntax: cors on | off;

Standard: cors off;

Kontext: http, server, location, if in location

Aktiviert die CORS-Verarbeitung. Jede andere Direktive wird unabhängig vererbt, sodass ein verschachtelter location, der eine davon setzt, den Rest nicht verliert — das ist der entscheidende Unterschied zu add_header.

cors_origin

Syntax: cors_origin * | any | <spezifikation> ...;

Standard: cors_origin *;

Kontext: http, server, location, if in location

Welche Ursprünge die Ressource lesen dürfen. Zwei reservierte Wörter und drei Spezifikationsformen:

Wert Bedeutung
* Einen wörtlichen Access-Control-Allow-Origin: * ausgeben. Gleich für alle, daher wird kein Vary: Origin hinzugefügt. Kann nicht mit cors_credentials on kombiniert werden.
any Den eingehenden Origin zurückspiegeln. Credential-sicher, gibt Vary: Origin aus.
https://app.example.com Exakte Übereinstimmung, case-insensitiv.
https://*.example.com Wildcard-Subdomain.
~^https://(a\|b)\.example\.com$ Regulärer Ausdruck. ~* für case-insensitiv.

Mehrere Spezifikationen können aufgelistet werden. Wenn eine übereinstimmt, wird dieser Ursprung zurückgegeben. * und any können nicht mit anderen Werten gemischt werden.

Wildcards sind bewusst streng: Das * muss direkt nach :// stehen und von einem . gefolgt werden. https://*example.com wird beim Start abgelehnt, anstatt stillschweigend https://evilexample.com zu matchen. Eine Wildcard matcht eine beliebige Anzahl führender Labels (https://a.b.example.com matcht https://*.example.com), matcht aber nicht die nackte Apex-Domain und ignoriert keinen Port — ein Ursprung mit Port benötigt einen exakten Eintrag oder einen regulären Ausdruck.

Origin: null — was ein sandboxed iframe, ein data:-Dokument oder eine file://-Seite sendet — wird nur durch einen expliziten null-Eintrag in der Liste gematcht, wenn cors_credentials on ist. Weder any noch ein regulärer Ausdruck matcht es in diesem Fall. Das Zurückspiegeln von null mit Credentials würde jedem sandboxed Frame im Internet einen authentifizierten Lesezugriff auf die Antwort geben.

cors_methods

Syntax: cors_methods * | <methode> ...;

Standard: cors_methods GET HEAD POST OPTIONS;

Kontext: http, server, location, if in location

Der Access-Control-Allow-Methods-Wert, der bei einer Preflight-Anfrage gesendet wird. * kann nicht mit cors_credentials on kombiniert werden.

cors_headers

Syntax: cors_headers * | any | <name> ...;

Standard: — (der Header wird weggelassen)

Kontext: http, server, location, if in location

Der Access-Control-Allow-Headers-Wert, der bei einer Preflight-Anfrage gesendet wird. any spiegelt den Access-Control-Request-Headers der Anfrage wörtlich zurück; der Header wird weggelassen, wenn die Anfrage keine angefordert hat. * kann nicht mit cors_credentials on kombiniert werden.

cors_expose_headers

Syntax: cors_expose_headers <name> ...;

Standard: — (der Header wird weggelassen)

Kontext: http, server, location, if in location

Antwort-Header, die der Browser für Skripte lesbar machen soll, über die Safelist-Menge hinaus. Wird bei tatsächlichen Antworten gesendet, nicht bei Preflight-Anfragen.

cors_credentials

Syntax: cors_credentials on | off;

Standard: cors_credentials off;

Kontext: http, server, location, if in location

Gibt Access-Control-Allow-Credentials: true aus und erlaubt Cookies und HTTP-Authentifizierung bei Cross-Origin-Anfragen.

Die CORS-Spezifikation verbietet die Kombination von Credentials mit einer Wildcard, und jeder Browser setzt dies durch — eine Konfiguration, die beides tut, ist also eine Konfiguration, deren CORS nie funktioniert. NGINX weigert sich zu starten, anstatt Ihnen die Auslieferung zu erlauben:

cors_credentials on;
cors_origin      *;      # nginx: [emerg] ... cannot be combined with "cors_origin *"

Verwenden Sie eine explizite Liste oder cors_origin any zum Zurückspiegeln. Beachten Sie, dass any plus Credentials jeder Website erlaubt, authentifizierte Antworten von diesem Ort zu lesen; dies ist erlaubt und protokolliert eine Warnung beim Start.

cors_max_age

Syntax: cors_max_age <zeit>;

Standard: — (der Header wird weggelassen)

Kontext: http, server, location, if in location

Wie lange ein Browser das Preflight-Ergebnis zwischenspeichern darf. cors_max_age 0; ist ein sinnvoller Wert und wird ausgegeben; das Weglassen der Direktive lässt den Header weg.

cors_preflight

Syntax: cors_preflight on | off;

Standard: cors_preflight on;

Kontext: http, server, location, if in location

Ob passende Preflight-Anfragen intern beantwortet werden sollen. Schalten Sie es aus, wenn Ihre Anwendung OPTIONS selbst implementiert und Sie nur die Antwort-Header hinzugefügt haben möchten.

Eine Preflight-Anfrage wird nur dann kurzgeschlossen, wenn sie wirklich eine ist — ein OPTIONS mit sowohl Origin als auch Access-Control-Request-Methodund der Ursprung übereinstimmt. Alles andere fällt unverändert durch, sodass WebDAV und OPTIONS auf Anwendungsebene weiterhin funktionieren, und eine Preflight-Anfrage von einem nicht erlaubten Ursprung erhält einfach kein Access-Control-Allow-Origin, was den Browser dazu bringt, sie abzulehnen.

Da der Handler in der Preaccess-Phase läuft, wird eine Preflight-Anfrage beantwortet, bevor auth_basic, auth_request und deny die Chance haben, sie abzulehnen. Das ist beabsichtigt: Browser senden niemals Credentials bei einer Preflight-Anfrage, sodass jede davor geschaltete Authentifizierung CORS vollständig brechen würde. Tatsächliche Anfragen an denselben Ort werden weiterhin normal authentifiziert.

cors_vary

Syntax: cors_vary on | off;

Standard: cors_vary on;

Kontext: http, server, location, if in location

Ob Vary: Origin ausgegeben werden soll, wenn die Antwort vom Ursprung der Anfrage abhängt.

Lassen Sie dies aktiviert. Es wird immer dann ausgegeben, wenn die Richtlinie ursprungsabhängig ist — einschließlich wenn der Ursprung nicht übereinstimmte und wenn die Anfrage gar kein Origin enthielt, weil eine zwischengespeicherte Kopie dieser Antwort niemals für eine Anfrage wiedergegeben werden darf, deren Ursprung andere Header erzeugt hätte. Es wird bewusst nicht für ein statisches cors_origin * ausgegeben, das für alle gleich ist und nicht variiert.

Schalten Sie es nur aus, wenn Sie wissen, dass kein gemeinsamer Cache vor diesem Ort liegt, oder Ihr CDN bereits auf Origin schlüsselt.

Hinweise

  • Ein bereits vorhandenes Vary auf der Antwort wird erweitert, nie ersetzt: Ein Upstream, der Vary: Accept-Language sendet, ergibt Vary: Accept-Language, Origin, und mehrere Vary-Zeilen vom Upstream werden zu einer zusammengefaltet. Ein Vary: * bleibt unverändert, da es laut RFC 9110 bereits alles umfasst.
  • Zwei Dinge fügen ihr Vary nach diesem Modul hinzu und kommen daher als separate Feldzeile an, anstatt eingefaltet zu werden: ein add_header Vary ... im selben location und gzip_vary on's Vary: Accept-Encoding. Mehrere Vary-Feldzeilen bedeuten genau dasselbe wie eine kombinierte Zeile (RFC 9110 §5.3), und jeder Cache verarbeitet das, also ist dies korrekt — nur nicht aufgeräumt. Laden Sie nginx-module-compression-vary, wenn Sie alles in einer Zeile zusammengefaltet haben möchten.
  • Setzen Sie CORS-Header nicht zusätzlich mit add_header im selben location. Dieses Modul ersetzt jedes Access-Control-Allow-Origin, das es findet — zwei davon sind ein harter Fehler in jedem Browser —, aber die anderen Header würden dupliziert.
  • Header werden bei 304- und 206-Antworten sowie bei Fehlern ausgegeben.
  • Subrequests werden übersprungen, entsprechend dem Verhalten von add_header.