JSON Toolkit

Hören Sie auf, JSON-Komprimierung in der falschen Reihenfolge zu machen: minify vs. gzip vs. brotli, gemessen

Fazit: Minifizieren spart 30–50 % des rohen JSON, aber gzip löscht den größten Teil dieses Gewinns wieder aus. Gemessene Zahlen über reale Formen und die vier Fälle, in denen Minifizieren es schlimmer macht.

Fortgeschritten~7 Min. LesezeitVeröffentlicht 2026-10-31Aktualisiert 2026-10-31
JSON-Komprimierung: gemessener Payload-Größenvergleich von minify, gzip und brotli

Kurz gesagt: Trennen Sie zuerst Transportkomprimierung von strukturellem Beschneiden. Ist gzip oder brotli noch nicht aktiv, reparieren Sie die Serverkonfiguration – allein gzip -6 erreicht 94,5 % und lässt minify nur noch 0,9 Punkte wert. Die vollen 46,3 % von minify landen nur, wenn die Byte-Rechnung lokal geführt wird: Request-Bodies, Lambdas 6 MB, Firestores 1 MiB oder JSON, das auf Platte und in Logs geschrieben wird.

Das „35 % gespart“-Abzeichen Ihres Lieblings-Minifiers ist ehrlich – es misst rohe Bytes. Ihre API läuft auf gzip oder brotli, und diese beiden verzehren genau dieselbe Redundanz. Beide Zahlen zu multiplizieren ergibt die falsche Antwort.

1. „Komprimierung“ in zwei verschiedene Dinge zerlegen

Was Leute JSON-Komprimierung nennen, sind eigentlich zwei unzusammenhängende Operationen, und sie zu verwechseln ist der Anfang jeder falschen Schlussfolgerung.

minify (Whitespace entfernen) gzip / brotli (Transportkomprimierung)
Natur Eine Dokumenttransformation Eine Transporttransformation
Ausgabe Weiterhin gültiger, lesbarer JSON-Text Binär; bis zum Dekodieren unbrauchbar
Wo der Gewinn landet Platte, Speicher, Logs, Größenlimits Nur auf der Leitung und in Caches
Nach dem Dekodieren Bytes bleiben kleiner Bytes kehren zur Originalgröße zurück
Wer es besitzt Ihr Build-Skript, Ihre Serializer-Argumente Nginx, CDN, Server-Framework

Die letzten beiden Zeilen sind die entscheidenden. Plattformlimits werden nach dem Dekodieren gemessen: AWS Lambdas 6-MB-Payload-Grenze, Firestores 1 MiB pro Dokument. gzip einzuschalten bringt Ihnen dort keinen einzigen Byte, weil beide das dekodierte Ergebnis zählen.

2. minify entfernt genau eine Sache: strukturellen Whitespace

RFC 8259 erlaubt Whitespace (space, \t, \n, \r) um jedes Strukturzeichen. Nichts anderes in einem JSON-Dokument ist optional: Schlüsselnamen sind Daten, Zahlen behalten jede Ziffer, und String-Inhalte sind Byte für Byte unantastbar.

Das macht die ganze Operation zu einer Zeile:

const minified = JSON.stringify(JSON.parse(pretty));
// RFC 8259 allows whitespace only around structural characters,
// so spaces inside a string are data, not formatting
JSON.stringify(JSON.parse('{"note": "two spaces"}'));
// {"note":"two spaces"} - both inner spaces are preserved

Erwähnenswert sind drei Operationen, die unter „Komprimierung“ abgelegt werden, aber tatsächlich das Dokument verändern: Schlüssel mit dem Wert null entfernen, Floats runden und Schlüssel umordnen. Sie ändern das Dokument, statt es neu zu formatieren, jedes Werkzeug, das „tiefe Komprimierung“ bewirbt, braucht also den Segen Ihres Konsumenten, bevor Sie es aktivieren.

Weil minify nur Whitespace entfernt, hängt sein Nutzen fast vollständig davon ab, wie viel Ihrer Datei Einrückung ist. Das wird sich in den Tabellen unten immer wieder zeigen.

3. Die Messung: vier Dokumente, drei gzip-Stufen, plus brotli

Umgebung: Node v22.13.1, zlib.gzipSync auf Stufe 1 / 6 / 9, brotliCompressSync mit Standardqualität (11). Die vier Beispiele decken bewusst die Formen ab, die Sie tatsächlich ausliefern: 50 verschachtelte Bestellungen (tiefe Struktur, wiederholte Schlüssel), eine Konfiguration mit 6 Ebenen (lange Schlüssel, kurze Werte), ein Log-Array mit 200 Zeilen (lange Strings, fast keine Einrückung) und die echte media-manifest.json dieser Website (gemischte Form, URL-lastige Strings).

Schritt eins: rohe Bytes.

Beispiel hübsch (2 Leerzeichen) hübsch (4 Leerzeichen) nach minify minify spart
50 verschachtelte Bestellungen 29.551 B 40.761 B 15.881 B 46,3 %
Konfiguration mit 6 Ebenen 25.450 B 38.012 B 11.679 B 54,1 %
Log-Array mit 200 Zeilen 14.582 B 14.982 B 13.981 B 4,1 %
media-manifest.json 25.149 B 28.114 B 21.222 B 15,6 %

Hierher kommt das „35 % gespart“-Abzeichen, und es lügt nicht. Das Problem erscheint in Schritt zwei.

Schritt zwei: jede Variante komprimieren und dann fragen, wie viel minify noch bringt.

Beispiel zusätzlich zu gzip -1 zusätzlich zu gzip -6 zusätzlich zu gzip -9 zusätzlich zu brotli
50 verschachtelte Bestellungen 21,0 % 17,0 % 8,9 % 10,6 %
Konfiguration mit 6 Ebenen 74,2 % 59,1 % 58,0 % 56,4 %
Log-Array mit 200 Zeilen -0,7 % -0,4 % 0,6 % -0,3 %
media-manifest.json 1,5 % 1,0 % 1,0 % 1,6 %

Dasselbe Dokument mit verschachtelten Bestellungen, das 46,3 % seiner rohen Bytes verliert, behält davon nur 17,0 %, sobald gzip -6 im Spiel ist. Das Log-Array geht glatt ins Negative: minify-dann-komprimieren ist etwas größer als das hübsche Original zu komprimieren. Negativ ist kein Bug, es ist genau das, was Sie erwarten sollten, und der nächste Abschnitt erklärt warum.

Schritt drei: Gesamtreduktion gegenüber dem ursprünglichen hübschen Dokument – die Zahl, die tatsächlich über die Leitung reist.

Beispiel nur gzip -6 minify + gzip -6 minify + brotli
50 verschachtelte Bestellungen 94,5 % 95,4 % 96,9 %
Konfiguration mit 6 Ebenen 96,0 % 98,4 % 99,0 %
Log-Array mit 200 Zeilen 89,9 % 89,9 % 95,0 %
media-manifest.json 83,5 % 83,6 % 86,1 %

4. Warum gzip den Verdienst von minify frisst

gzip ist LZ77 gefolgt von Huffman-Kodierung. Der zentrale LZ77-Schritt ist „im 32-KB-Fenster zurückblicken, eine wiederholte Sequenz finden, sie durch einen (Distanz, Länge)-Zeiger ersetzen“. Einrückung ist das leichteste mögliche Ziel dafür: Jede Verschachtelungsebene erzeugt eine identische Folge von Leerzeichen, und Dutzende benachbarter Zeilen wiederholen dieselbe Kombination. Huffman weist dann häufigen Symbolen kurze Codes zu, sodass ein Muster wie \n plus acht Leerzeichen – eines, das tausendmal in einer Datei vorkommen kann – am Ende zwei oder drei Bits kostet.

Die Bytes, die minify löscht, sind also genau die Bytes, die der Kompressor fast umsonst kollabiert hätte. Es selbst zu tun heißt, Arbeit zu wiederholen, in der der Kompressor bereits glänzt, und Ihre 46 % schrumpfen auf 17 %. Das ist die ganze Geschichte hinter der obigen Tabelle.

Das negative Log-Array-Ergebnis verdient eine eigene Erklärung. Dieses Beispiel besteht fast vollständig aus langen Strings (Logzeilen, URLs, Base64), was wenige Wiederholungen und hohe Entropie bedeutet – wenig Redundanz, die der Kompressor überhaupt entfernen könnte. Die Zeilenumbrüche in der hübschen Version geben dem Matcher zufällig stabile Grenzen. Entfernt man sie, verliert LZ77 diese Anker: In diesem Beispiel ergibt das Komprimieren des hübschen Originals mit gzip -1 1.508 Bytes, während minify-dann-komprimieren 1.518 Bytes ergibt. Zehn Bytes schlechter.

Nach Größe geordnet stellen sich die Hebel so auf:

Hebel Gespart gegenüber dem ursprünglichen hübschen Dokument (verschachtelte Bestellungen) Kosten
minify 46,3 % (nur rohe Bytes) Lesbarkeit weg; braucht einen Build-Schritt
gzip -6 94,5 % Ein wenig Server-CPU; nahezu keine Konfiguration
minify + gzip -6 95,4 % Die beiden oben, addiert
minify + brotli 96,9 % Braucht HTTPS und das brotli-Modul; statische Assets vorab komprimieren

Der Sprung von 0 auf 94,5 % ist gzips Arbeit. Der Sprung von 94,5 % auf 95,4 % – neun Zehntel eines Punktes – ist minifys. Diese Tabelle entscheidet, wohin Ihr Aufwand gehört.

5. Die Kompressionsstufe ist die versteckte Variable

Jede Argumentation, die eine einzige feste gzip-Stufe annimmt, steht auf Sand, denn die Stufe ändert direkt den Grenzwert von minify:

Transporteinstellung Zusätzliche Bytes, die minify noch spart (verschachtelte Bestellungen)
gzip -1 (Nginx gzip_comp_level-Standard) 21,0 %
gzip -6 (Standard in den meisten Standardbibliotheken) 17,0 %
gzip -9 8,9 %
brotli (Standardqualität) 10,6 %

Je schwächer Ihre Transportkomprimierung, desto mehr zahlt sich minify aus. Das ergibt eine praktische Regel: Auf einer Standard-Nginx-Installation, bei der gzip_comp_level noch 1 ist, bringt minify 21 %. Haben Sie auf 9 getunt oder auf brotli gewechselt, sind es nur noch 9–11 %, und Sie können es meist überspringen.

Bevor Sie in die eine oder andere Richtung argumentieren, messen Sie, auf welcher Stufe Sie tatsächlich stehen:

# Fetch the same response with three Accept-Encoding values and compare what landed
for enc in identity gzip br; do
  size=$(curl -s -o /dev/null -w '%{size_download}' \
    -H "Accept-Encoding: $enc" https://api.example.com/v1/orders)
  echo "$enc -> $size bytes"
done

Drei Zahlen beantworten drei Fragen auf einmal: Ist Komprimierung überhaupt an, auf welcher Stufe, und wie viel Raum bleibt für minify. Kommen identity und gzip nahezu identisch zurück, ist Komprimierung gar nicht aktiv – und zu reparieren ist Nginx, nicht Ihr JSON:

gzip on;
gzip_comp_level 6;
gzip_min_length 256;      # skip tiny responses, they only grow
gzip_types application/json application/javascript text/css;

6. Wann minify wirklich etwas bringt

Ein Test entscheidet: Werden die gesparten Bytes irgendwo festgehalten, oder stoßen Sie an ein Limit, das rohe Bytes zählt? Ja → die vollen 46 % landen. Lautet die Antwort „es existiert nur auf der Leitung“, dann sind 17 % sein echter Wert.

Erstens, HTTP-Request-Bodies. Die meisten Server dekomprimieren Request-Bodies nicht, und gzip ist fast ausschließlich für Antworten konfiguriert. Das JSON, das Sie POSTen, hat meist gar keinen Transportschutz, minifys Ersparnis kommt also zum vollen Preis an. Dasselbe gilt für Webhook-Callbacks, MQTT-Telemetrie und WebSocket-Frames, die nie permessage-deflate verhandelt haben.

Zweitens, Kanäle mit harter Byte-Obergrenze – und jeder davon zählt dekodierte Bytes:

Kanal Limit Kann gzip helfen?
AWS Lambda synchroner Payload 6 MB Nein, nach dem Dekodieren gemessen
Firestore einzelnes Dokument 1 MiB Nein
Kafka einzelne Nachricht (Standard) 1 MB Nein, Broker-seitige Komprimierung ist eine andere Schicht
localStorage-Quota ~5 MB (in Zeichen gezählt) Nein
GET-Anfrage-URL Serverabhängig, 8 KB ist üblich Nein

In dieser Kategorie ist minify keine Optimierung, es ist der Unterschied zwischen einer Anfrage, die abgeschickt wird, und einer, die scheitert. JSON in einen URL-Query-String einzubetten ist der schärfste Fall: URLs werden nie komprimiert und haben eine Längenobergrenze, also zuerst minifizieren und dann entscheiden, ob Sie überhaupt Base64 brauchen.

Drittens, JSON, das auf Platte oder in Logs geschrieben wird. Logs und Indizes speichern wörtliche Bytes ohne Transportebene. Es gibt einen Vorbehalt, der Leute stolpern lässt: minify hilft nur, wenn Sie überhaupt hübsches JSON geschrieben haben. Dieses Log-Beispiel mit 200 Zeilen sparte nur 4,1 %, weil seine Zeilen bereits kompakt waren und Einrückung fast nichts der Datei ausmachte. Umgekehrt, wenn Ihr Logger JSON.stringify(obj, null, 2) aufruft, ist das Weglassen des dritten Arguments gratis Geld.

Eine letzte Anmerkung: Sind rohe Bytes wirklich knapp, sind Schlüsselnamen meist ein größerer Hebel als Einrückung. Bei media-manifest.json bringt das Ersetzen der 14 eindeutigen Schlüssel durch k0 bis k13 die minifizierte Form von 21.222 B auf 18.887 B – weitere 11,0 %. Komprimiert schrumpft dieser verbleibende Vorsprung auf 2,4 % unter gzip -6 und 1,3 % unter brotli. Derselbe Mechanismus wie oben: Einrückung und Schlüsselnamen sind beide wiederholte strukturelle Information, und der Kompressor holt sich so oder so dieselbe Dividende.

7. Vier Fälle, in denen es nach hinten losgeht

Committete Dateien nicht minifizieren. Ändern Sie "qty": 2 zu 3, und die hübsche Datei zeigt eine saubere Zeile in git diff; die minifizierte Datei schreibt die ganze Zeile neu, und bei Hunderten von Feldern finden Sie die Änderung überhaupt nicht. Das ist dieselbe Mechanik wie in JSON-Merge-Konflikte auflösen – Diff-Werkzeuge arbeiten zeilenweise, einzeiliges JSON macht also jede Bearbeitung zu „die ganze Zeile unterscheidet sich“. Behalten Sie hübsche Dateien im Repository und minifizieren Sie nur in Build-Ausgaben oder beim Schreiben in den Speicher.

Kleine Antworten nicht komprimieren. Eine Antwort, die nur {"ok": true} enthält, ist mit Einrückung 16 Bytes und nach gzip -6 36 Bytes – zwanzig Bytes größer, weil gzips eigene Wörterbuch- und Kopfdaten Platz belegen. Das ist nicht theoretisch; genau deshalb existiert gzip_min_length. Sein Nginx-Standard von 20 Bytes ist für die Produktion viel zu niedrig, 256 ist die übliche Untergrenze.

Kein schnelleres Parsen erwarten. Skaliert man dieselbe Bestellmenge auf 4.000 Datensätze, ist das hübsche Dokument 2.374.378 Bytes gegenüber 1.282.358 minifiziert – 46 % kleiner – doch die mediane JSON.parse-Zeit bewegt sich nur von 5,7 ms auf 5,0 ms, etwa 12 % schneller. Die Größe zu halbieren halbiert nicht die Zeit, weil der Parser den größten Teil seines Budgets für das Schneiden von Strings und das Konvertieren von Zahlen ausgibt; Whitespace zu überspringen ist nebensächlich. Was die Parse-Kosten tatsächlich treibt, sind Schlüsselzahl und String-Inhalt, was Große JSON-Dateien parsen, rendern und streamen im Detail aufschlüsselt.

Nicht nachträglich auf einen Dienst aufsetzen, der bereits brotli oder gzip -9 fährt. Der Nutzen fällt auf 9–11 %, während die Kosten – dauerhafter Verlust der Lesbarkeit beim Debuggen – bleiben. Prüfen Sie stattdessen, ob gzip_types überhaupt application/json abdeckt. Viele Seiten aktivieren gzip und komprimieren nur text/html, sodass JSON völlig unkomprimiert bleibt; in dieser Situation ist minify nicht Schritt eins, Ihre Konfiguration zu reparieren ist es.

8. Entscheidungstabelle: welcher Schritt zuerst

Ihre Situation Zuerst tun Wo minify rangiert
Gar keine Transportkomprimierung Nginx gzip_types konfigurieren oder CDN-Auto-Komprimierung aktivieren Überspringen; neun Zehntel des Gewinns verdampfen
gzip -1 läuft (Nginx-Standard) gzip_comp_level auf 6 erhöhen Zweitens, 21 % wert
Bereits auf brotli oder gzip -9 Lange Schlüssel prüfen, Paginierung ergänzen oder Felder beschneiden Überspringen; nur 9–11 % übrig
Lambda 6 MB oder Firestore 1 MiB erreicht Direkt minifizieren; die vollen 46 % zählen Erstens
POST-Bodies, Webhooks, MQTT Direkt minifizieren; Transport greift hier selten Erstens
Konfigurationsdateien, die nach Git gehen Hübsch halten Nur in Build-Ausgaben
Logs, Telemetrie, Offline-Dumps Das null, 2-Argument im Logger entfernen Das ist Schritt eins

Die Ein-Satz-Regel: Sehen Sie darauf, welche Byte-Rechnung Sie verkleinern wollen – eine lokal geführte (Platte, Quota, Plattformlimit, Request-Body) oder eine, die nur auf der Leitung existiert. Lokal zuerst heißt minify; nur-Leitung heißt zuerst brotli oder gzip einschalten.

In der Praxis führen Sie die curl-Schleife oben aus, um zu bestätigen, dass Transportkomprimierung live ist, und schicken dann ein Beispiel durch das JSON-Minify-Werkzeug und lesen dessen Ersparniszahl. Liegt diese Zahl über 40 %, während curl identity und gzip bei nahezu gleicher Größe meldet, ist Ihr Engpass die Serverkonfiguration, nicht JSON. Sind die beiden curl-Zahlen bereits nahe beieinander, hat dieses Dokument wenig zu geben, und Sie sollten stattdessen Felder beschneiden oder paginieren – das Thema von Kernpunkte für den Entwurf von JSON-API-Verträgen.

Ist minifiziertes JSON noch gültiges JSON?

Ja. minify entfernt nur den Whitespace, den RFC 8259 um Strukturzeichen erlaubt, und berührt String-Inhalte nie, JSON.parse erzeugt also einen identischen Wert. Das einzige Opfer ist die menschliche Lesbarkeit.

brotli oder gzip?

Beim selben minifizierten Beispiel spart brotli 96,9 % und gzip -6 95,4 % – eine schmale Lücke. Verwenden Sie gzip -6 für dynamische Antworten, um die CPU niedrig zu halten, und reservieren Sie brotli für per HTTPS ausgelieferte statische Assets, die Sie vorab in .br-Dateien komprimieren können.

Ich habe mein JSON minifiziert, aber die API wurde nicht schneller. Warum?

Prüfen Sie die Antwort mit curl -sI auf einen Content-Encoding: gzip-Header. Viele Nginx-Setups setzen gzip_types standardmäßig nur auf text/html, sodass JSON nie abgedeckt war – und weniger als ein Fünftel Ihrer 46 % erreichte je die Leitung.

Sollten Konfigurationsdateien minifiziert committet werden?

Nein. Diff-Werkzeuge vergleichen zeilenweise, einzeiliges JSON macht also jede Bearbeitung zu „die ganze Zeile hat sich geändert“, und Konflikte aufzulösen kostet weit mehr als die gesparten Bytes. Behalten Sie hübsche Versionen im Repository und minifizieren Sie zur Build-Zeit.

Wie groß sollte eine Antwort sein, bevor gzip sich lohnt?

Der übliche Schwellenwert ist 256 Bytes bis 1 KB. Eine Antwort so klein wie {"ok": true} – mit Einrückung 16 Bytes – wächst unter gzip -6 auf 36 Bytes, weil Wörterbuch und Header mehr kosten, als sie zurückgewinnen. Genau das steuert gzip_min_length.