Die nginx map-Direktive gehört zu den Nginx-Bausteinen, die ich besonders dann schätze, wenn Konfigurationen zu viele Abzweigungen bekommen. Mit ihr leite ich einen Eingabewert wie Hostname, User-Agent oder Request-URI auf eine neue Variable um und halte Header, Routing-Hinweise und Variantenlogik deutlich sauberer. In diesem Artikel zeige ich, wie die Zuordnung funktioniert, welche Treffer Nginx zuerst prüft und wo die typischen Stolperfallen liegen.
Die wichtigsten Punkte zu `map`
-
maperzeugt eine neue Variable aus einem vorhandenen Eingabewert. - Die Direktive gehört in den
http-Kontext und wird erst bei Bedarf ausgewertet. - Exakte Treffer, Hostnamen-Masken und Regex werden in einer festen Reihenfolge geprüft.
- Große Zuordnungen lagere ich früh in eigene Dateien aus, statt sie in der Hauptdatei zu stapeln.
- Für reine Wertzuordnung ist
mapmeist sauberer als verschachtelte Logik. - Für Aktionen wie Redirect oder Rewrite brauchst du danach trotzdem die passende Nginx-Direktive.
Was die nginx map-Direktive im Alltag leistet
Ich setze map immer dann ein, wenn aus einem Request-Attribut eine klar berechenbare Zielvariable werden soll. Das ist keine Aktionslogik, sondern eine Lookup-Tabelle: Eingangswert rein, Ergebniswert raus. Der große Vorteil ist, dass Nginx die Variable erst dann auswertet, wenn sie später wirklich verwendet wird.
Praktisch heißt das für mich: Ich kann Schalter für Cache, Header, Mandanten oder kleine Weiterleitungsregeln zentral definieren, ohne dieselbe Bedingung an mehreren Stellen zu kopieren. Sogar mehrere Quellvariablen lassen sich im ersten Argument kombinieren, aber ich nutze das nur sparsam, weil die Lesbarkeit sonst schnell sinkt.
Damit das sauber funktioniert, muss die Direktive im http-Kontext stehen. Genau dort ist sie zu Hause, und genau dort lohnt sich auch die nächste Frage: Wie sieht der Aufbau der Zuordnung in der Praxis aus?
So ist die Syntax sauber aufgebaut
Der Grundaufbau ist kurz, aber jedes Element hat eine Aufgabe. Ich halte die Regeln meist so kompakt wie möglich und lagere große Tabellen früh in eigene Dateien aus.
map $http_host $backend {
default backend_standard;
example.de backend_de;
api.example.de backend_api;
~*^m\. backend_mobile;
}
Links steht der Quellwert, rechts die neue Variable. default fängt alles auf, was sonst nicht passt, und bei Regex-Regeln entscheide ich bewusst, ob ich case-sensitiv oder case-insensitiv arbeite. Für einfache Strings ist die Sache angenehmer, als viele erwarten: Nginx vergleicht sie ohne Beachtung der Groß- und Kleinschreibung.
Wenn ich mehr als eine Eingangsvariable brauche, kombiniere ich sie im ersten Argument, zum Beispiel als "$host$request_uri". Das funktioniert gut, solange man es gezielt einsetzt und nicht jede Regel künstlich zusammenschnürt.
map $http_host $site {
hostnames;
default main;
.example.de main;
}
Die Zeile hostnames; schaltet die Hostnamen-Masken frei, und die Kurzform .example.de deckt sowohl die nackte Domain als auch Subdomains ab. Wenn die Tabelle wächst, nutze ich zusätzlich include; volatile setze ich nur dann ein, wenn eine Variable im Request wirklich nicht cachebar sein darf. Für die meisten Setups ist das unnötig.
Wenn du den Aufbau verstanden hast, ist der nächste Punkt entscheidend: Die Reihenfolge der Treffer bestimmt, welcher Wert am Ende wirklich gewinnt.

Welche Einträge Nginx zuerst prüft
Nginx arbeitet beimap nicht nach Zufall, sondern nach einer festen Priorität. Genau daran scheitern viele Konfigurationen, wenn eine breite Regex zu früh greift oder ein scheinbar exakter Treffer doch anders behandelt wird als erwartet.
| Priorität | Was passt dazu | Beispiel | Praktische Folge |
|---|---|---|---|
| 1 | Exakter String ohne Maske | example.de |
Der präziseste Treffer gewinnt sofort. |
| 2 | Längste Prefix-Maske | *.example.de |
Die spezifischste Subdomain-Maske schlägt allgemeinere Varianten. |
| 3 | Längste Suffix-Maske | mail.* |
Auch hier zählt die längste passende Maske zuerst. |
| 4 | Erster passender regulärer Ausdruck | ~*Mobile |
Die Reihenfolge im File ist hier entscheidend. |
| 5 | default |
default backend_standard; |
Greift erst, wenn alles andere nicht passt. |
Für Regex-Regeln gilt deshalb eine einfache Redaktionsregel, die ich auch in Konfigurationen anwende: zuerst speziell, dann allgemein. Ein breites Match gehört nach unten, nicht nach oben. Und wenn ein Schlüssel selbst default, hostnames oder include heißen soll, musst du ihn mit einem Backslash escapen, sonst liest Nginx ihn als Spezialparameter.
Mit diesem Raster im Kopf lassen sich die typischen Anwendungsfälle deutlich leichter lesen, und genau dort wird map im Alltag interessant.
Praktische Muster aus echten Konfigurationen
In produktiven Setups sehe ich immer wieder dieselben drei Muster: Header vereinfachen, Hostnamen bündeln und kleine Varianten sauber trennen. Das sind keine spektakulären Tricks, aber sie sparen Konfigurationszeilen und machen Änderungen weniger riskant.
WebSocket-Header sauber setzen
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
Das ist ein gutes Beispiel dafür, warum ich map mag: Die eigentliche Proxy-Konfiguration bleibt ruhig, und die Sonderlogik steckt in einer kurzen Zuordnung. Wenn der Client keinen Upgrade-Header sendet, wird sauber auf close umgeschaltet, ohne dass ich dafür eine verschachtelte Bedingung basteln muss.
Geräte grob unterscheiden
map $http_user_agent $device_class {
default desktop;
~*Mobile mobile;
~*Tablet tablet;
}
Für kleine Layout-Unterschiede oder Reporting reicht das oft aus. Für Security wäre es die falsche Stelle, weil ein User-Agent leicht zu fälschen ist. Genau diese Grenze sollte man sauber ziehen, statt aus einer bequemen Technik eine Schein-Lösung zu machen.
Lesen Sie auch: Nginx Proxy Manager 502 - Bad Gateway schnell beheben
Mehrere Hostnamen übersichtlich bündeln
map $http_host $tenant {
hostnames;
default public;
.example.de main;
api.example.de api;
shop.example.de shop;
}
Gerade bei Mandanten, Marken oder mehreren Subdomains hält das die Konfiguration aufgeräumt. Ich ziehe solche Listen bei Wachstum früh in eine eigene Datei, weil die Hauptkonfiguration sonst zu schnell zur Sammelstelle für alles wird.
Die Grenze zwischen sauberer Zuordnung und echter Fehlkonfiguration liegt oft in kleinen Details. Deshalb lohnt sich ein Blick auf die Fehler, die ich in Reviews am häufigsten wiedersehe.
Typische Fehler und Grenzen im Alltag
Die meisten Probleme mit map entstehen nicht durch die Direktive selbst, sondern durch Kontext, Reihenfolge oder falsche Erwartungen. Wenn du diese Punkte im Griff hast, läuft die Sache erstaunlich stabil.
| Problem | Typisches Symptom | Saubere Lösung |
|---|---|---|
map steht im falschen Kontext |
directive is not allowed here |
Immer im http-Bereich platzieren. |
default fehlt |
Leerer String statt erwartetem Fallback | Immer einen expliziten Fallback setzen. |
| Regex steht zu weit oben | Eine breite Regel fängt zu viel ab | Speziellere Treffer vor allgemeinen Regeln platzieren. |
| Case-Verhalten wird falsch eingeschätzt | Strings reagieren anders als erwartet | Normale Strings sind case-insensitiv, Regex bewusst wählen. |
| Große Tabelle in einer Datei | Unübersichtliche Konfiguration oder Hash-Warnungen | Mit include auslagern und Hash-Größen prüfen. |
| Header werden für Sicherheit genutzt | Scheinbar zuverlässige, aber manipulierbare Entscheidungen | Für Zugriffskontrolle echte Access- oder Auth-Mechanismen nutzen. |
Wenn Nginx bei großen Zuordnungen über Hash-Werte meckert, erhöhe ich map_hash_bucket_size oder map_hash_max_size Schritt für Schritt und teste danach erneut. Wichtig ist dabei nicht, blind zu drehen, sondern die Konfiguration danach mit nginx -t zu prüfen. Genau so bleibt die Änderung nachvollziehbar.
Damit ist auch die letzte Frage praktisch beantwortet: Wann ist map die bessere Wahl als andere Nginx-Bausteine, und wann nicht?
So halte ich Konfigurationen mit map langfristig wartbar
Ich trenne bei Nginx nicht dogmatisch zwischen map und if, sondern zwischen Wertzuweisung und Steuerlogik. Für reine Zuordnungen nehme ich fast immer map; für echte Aktionen wie Redirect, Return oder Rewrite bleibt die passende Direktive zuständig.
| Aufgabe | Besserer Baustein | Warum ich ihn bevorzuge |
|---|---|---|
| Wert aus einem Request-Attribut ableiten | map |
Lesbar, deklarativ und erst bei Bedarf ausgewertet. |
| Eine feste Variable setzen | set |
Kürzer, wenn keine Zuordnungstabelle nötig ist. |
| Antwort senden oder umleiten |
return / rewrite
|
map liefert nur den Wert, die Aktion kommt danach. |
| Datei-, Verzeichnis- oder Regex-Bedingungen prüfen |
if sparsam |
Dafür ist es gedacht, aber nicht als allgemeiner Tabellenersatz. |
- Ich setze immer einen klaren
default-Wert. - Ich sortiere Regeln von spezifisch nach allgemein.
- Ich lagere große Listen in eigene Dateien aus.
- Ich teste jede Änderung vor dem Reload mit
nginx -t. - Ich halte Security-Entscheidungen von bloßen Headern fern, wenn sie manipulierbar wären.
Wenn du diese fünf Punkte beachtest, bleibt die Konfiguration klein genug, um in Reviews verständlich zu sein, und robust genug für den Betrieb. Für mich ist genau das der eigentliche Wert von map: weniger Sonderlogik im Ablauf, mehr Klarheit in der Konfiguration und deutlich weniger Raum für spätere Überraschungen.