cors: Korrektes CORS für NGINX, einschließlich Preflight und Vary
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 oben in /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. Aug. 2026.
CORS für NGINX, das die Teile übernimmt, die das add_header-Rezept falsch macht.
Warum nicht einfach add_header verwenden?
Das Rezept map $http_origin + add_header Access-Control-Allow-* ist das,
das alle kopieren, und es ist auf vier konkrete Arten fehlerhaft:
add_headerwird pro Ebene vererbt oder ersetzt. Sobald einlocationeinen einzigen eigenen Header hinzufügt, verschwinden still und leise alleadd_header, die darüber gesetzt wurden — einschließlich Ihrer CORS-Header. Nichts warnt Sie.- Preflight benötigt einen Short-Circuit. Ein
OPTIONS-Preflight muss mit einem204und den richtigen Headern beantwortet werden, ohne Ihre Anwendung zu erreichen. Das von Hand zu tun bedeutet einenif-Block, und dieser interagiert schlecht mittry_filesundproxy_pass. add_headergreift nur bei einer Whitelist von Statuscodes, es sei denn, Sie übergebenalways. Dadurch verlieren Ihre Fehlerantworten ihre CORS-Header, und der Browser meldet einen undurchsichtigen CORS-Fehler anstelle des 404 oder 502, der tatsächlich aufgetreten ist.Vary: Originwird vergessen. Wenn die Antwort vom Request-Origin abhängt und Sie das nicht angeben, wird jeder Shared Cache oder CDN vor Ihnen fröhlich die Antwort eines Origins an einen anderen ausliefern. Dies ist ein Cache-Poisoning-Bug und der häufigste in freier Wildbahn.
Dieses Modul erledigt alle vier korrekt, als Header-Filter plus einem Handler in
der Preaccess-Phase, ohne if-Blöcke.
Synopsis
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;
}
Ein Preflight sieht dann so aus und erreicht nie backend:
$ 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;
Default: cors off;
Context: http, server, location, if in location
Aktiviert die CORS-Verarbeitung. Jede andere Direktive wird unabhängig
vererbt, sodass ein verschachteltes location, das eine von ihnen setzt, nicht
die übrigen verliert — was genau der Unterschied zu add_header ist.
cors_origin
Syntax: cors_origin * | any | <spec> ...;
Default: cors_origin *;
Context: http, server, location, if in location
Welche Origins auf die Ressource zugreifen dürfen. Zwei reservierte Wörter und drei Spec-Formen:
| Wert | Bedeutung |
|---|---|
* |
Gibt ein wörtliches Access-Control-Allow-Origin: * aus. Für alle gleich, daher wird kein Vary: Origin hinzugefügt. Kann nicht mit cors_credentials on kombiniert werden. |
any |
Reflektiert, welcher Origin auch immer ankommt. Credential-sicher, gibt Vary: Origin aus. |
https://app.example.com |
Exakte Übereinstimmung, Groß-/Kleinschreibung wird nicht beachtet. |
https://*.example.com |
Wildcard-Subdomain. |
~^https://(a\|b)\.example\.com$ |
Regulärer Ausdruck. ~* für Groß-/Kleinschreibung wird nicht beachtet. |
Mehrere Specs können aufgelistet werden. Wenn eine übereinstimmt, wird dieser
Origin zurückgegeben. * und any können nicht mit anderen Werten gemischt
werden.
Wildcards sind bewusst streng: Das * muss direkt nach :// kommen und von
einem . gefolgt werden. https://*example.com wird beim Start abgelehnt,
anstatt still und leise 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 den bloßen Apex und ignoriert keinen
Port — ein Origin mit einem 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. Weder any noch ein regulärer
Ausdruck werden es in diesem Fall matchen. null mit Credentials zu
reflektieren würde jedem sandboxed Frame im Internet einen authentifizierten
Lesezugriff auf die Antwort gewähren.
cors_methods
Syntax: cors_methods * | <method> ...;
Default: cors_methods GET HEAD POST OPTIONS;
Context: http, server, location, if in location
Der Access-Control-Allow-Methods-Wert, der bei einem Preflight gesendet wird.
* kann nicht mit cors_credentials on kombiniert werden.
cors_headers
Syntax: cors_headers * | any | <name> ...;
Default: — (der Header wird weggelassen)
Context: http, server, location, if in location
Der Access-Control-Allow-Headers-Wert, der bei einem Preflight gesendet wird.
any gibt die 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> ...;
Default: — (der Header wird weggelassen)
Context: http, server, location, if in location
Antwort-Header, die der Browser über die safelisted Menge hinaus für Skripte lesbar machen soll. Wird bei tatsächlichen Antworten gesendet, nicht bei Preflights.
cors_credentials
Syntax: cors_credentials on | off;
Default: cors_credentials off;
Context: 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 erzwingt dies — eine Konfiguration, die beides tut, ist also eine Konfiguration, deren CORS nie funktioniert. NGINX verweigert den Start, anstatt Sie das ausliefern zu lassen:
cors_credentials on;
cors_origin *; # nginx: [emerg] ... cannot be combined with "cors_origin *"
Verwenden Sie eine explizite Liste oder cors_origin any zum Reflektieren.
Beachten Sie, dass any plus Credentials jeder Website erlaubt,
authentifizierte Antworten von diesem Location zu lesen; es ist erlaubt und
protokolliert eine Warnung beim Start.
cors_max_age
Syntax: cors_max_age <time>;
Default: — (der Header wird weggelassen)
Context: http, server, location, if in location
Wie lange ein Browser das Preflight-Ergebnis cachen 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;
Default: cors_preflight on;
Context: http, server, location, if in location
Ob passende Preflights 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.
Ein Preflight wird nur dann kurzgeschlossen, wenn es tatsächlich eines ist — ein
OPTIONS, das sowohl Origin als auch Access-Control-Request-Method trägt —
und der Origin übereinstimmt. Alles andere fällt unberührt durch, sodass WebDAV
und anwendungsseitiges OPTIONS weiter funktionieren, und ein Preflight von
einem nicht erlaubten Origin erhält einfach kein Access-Control-Allow-Origin,
was den Browser dazu bringt, es abzulehnen.
Da der Handler in der Preaccess-Phase läuft, wird ein Preflight beantwortet,
bevor auth_basic, auth_request und deny die Chance bekommen, es
abzulehnen. Das ist beabsichtigt: Browser senden bei einem Preflight niemals
Credentials, sodass jede Authentifizierung davor CORS vollständig brechen würde.
Tatsächliche Anfragen an denselben Location werden weiterhin normal
authentifiziert.
cors_vary
Syntax: cors_vary on | off;
Default: cors_vary on;
Context: http, server, location, if in location
Ob Vary: Origin ausgegeben werden soll, wenn die Antwort vom Request-Origin
abhängt.
Lassen Sie dies eingeschaltet. Es wird immer dann ausgegeben, wenn die Policy
origin-abhängig ist — einschließlich, wenn der Origin nicht übereinstimmte und
wenn die Anfrage überhaupt keinen Origin trug, weil eine gecachte Kopie dieser
Antwort niemals an eine Anfrage wiedergegeben werden darf, deren Origin 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 Shared Cache vor diesem
Location sitzt oder Ihr CDN bereits auf Origin keyt.
Hinweise
- Ein bereits in der Antwort vorhandenes
Varywird erweitert, niemals ersetzt: Ein Upstream, derVary: Accept-Languagesendet, kommt alsVary: Accept-Language, Originheraus, und mehrere Upstream-Vary-Zeilen werden zu einer zusammengeführt. EinVary: *wird in Ruhe gelassen, da es gemäß RFC 9110 bereits alles subsumiert. - Zwei Dinge fügen ihr
Varynach diesem Modul hinzu und kommen daher als separate Feldzeile an, anstatt eingefügt zu werden: einadd_header Vary ...im selben Location und dasVary: Accept-Encodingvongzip_vary on. MehrereVary-Feldzeilen bedeuten genau dasselbe wie eine kombinierte Zeile (RFC 9110 §5.3) und jeder Cache kommt damit zurecht, das ist also korrekt — nur nicht ordentlich. Laden Sienginx-module-compression-vary, wenn Sie alles in einer Zeile zusammengeführt haben möchten. - Setzen Sie CORS-Header nicht auch noch mit
add_headerim selben Location. Dieses Modul ersetzt jedesAccess-Control-Allow-Origin, das es findet — zwei davon sind ein harter Fehler in jedem Browser —, aber die anderen Header würden dupliziert werden. - Header werden auch bei 304- und 206-Antworten sowie bei Fehlern ausgegeben.
- Subrequests werden übersprungen, was dem Verhalten von
add_headerentspricht.