JSON Toolkit

Berhenti Melakukan Kompresi JSON dalam Urutan yang Salah: minify vs gzip vs brotli, Terukur

Intinya: Minify memangkas 30-50% dari JSON mentah, tetapi gzip menghapus sebagian besar keuntungan itu. Angka terukur pada bentuk data nyata, plus empat kasus ketika minifikasi justru memperburuk keadaan.

Menengah±7 menit bacaDiterbitkan 2026-10-31Diperbarui 2026-10-31
Kompresi JSON: perbandingan ukuran payload terukur antara minify, gzip, dan brotli

Intinya: pisahkan dulu kompresi transport dari pemangkasan struktural. Jika gzip atau brotli belum aktif, perbaiki konfigurasi server — gzip -6 saja mencapai 94,5% dan menyisakan minify hanya bernilai 0,9 poin. Minify dengan 46,3% penuh baru berlaku ketika tagihan byte dicatat secara lokal: request body, 6 MB di Lambda, 1 MiB di Firestore, atau JSON yang ditulis ke disk dan log.

Badge "menghemat 35%" di minifier favorit Anda jujur — ia mengukur byte mentah. API Anda berjalan di atas gzip atau brotli, dan keduanya memakan redundansi yang persis sama. Mengalikan kedua angka itu menghasilkan jawaban yang salah.

1. Pisahkan "kompresi" menjadi dua hal berbeda

Apa yang orang sebut kompresi JSON sebenarnya dua operasi yang tidak berhubungan, dan mencampuradukkannya adalah awal dari setiap kesimpulan yang salah.

minify (hapus whitespace) gzip / brotli (kompresi transport)
Hakikat Transformasi dokumen Transformasi transport
Output Masih JSON valid dan masih terbaca Biner; tidak terpakai sampai didekode
Di mana keuntungannya terjadi Disk, memori, log, batas ukuran Hanya di wire dan di cache
Setelah dekode Byte tetap lebih kecil Byte kembali ke ukuran asli
Siapa pemiliknya Skrip build Anda, argumen serializer Anda Nginx, CDN, framework server

Dua baris terakhir adalah yang penting. Batas platform diukur setelah dekode: batas payload 6 MB di AWS Lambda, batas 1 MiB per dokumen di Firestore. Mengaktifkan gzip tidak membelikan Anda satu byte pun di sana, karena keduanya menghitung hasil yang sudah didekode.

2. Minify menghapus tepat satu hal: whitespace struktural

RFC 8259 mengizinkan whitespace (space, \t, \n, \r) di sekitar karakter struktural apa pun. Tidak ada hal lain dalam dokumen JSON yang opsional: nama key adalah data, number mempertahankan setiap digit, dan isi string tidak boleh disentuh byte demi byte.

Itu membuat seluruh operasi menjadi satu baris:

const minified = JSON.stringify(JSON.parse(pretty));
// RFC 8259 hanya mengizinkan whitespace di sekitar karakter struktural,
// jadi spasi di dalam string adalah data, bukan format
JSON.stringify(JSON.parse('{"note": "two spaces"}'));
// {"note":"two spaces"} - kedua spasi di dalam tetap dipertahankan

Yang perlu disebut adalah tiga operasi yang dikelompokkan sebagai "kompresi" padahal sebenarnya mengubah dokumen: membuang key yang nilainya null, membulatkan float, dan mengurutkan ulang key. Itu mengubah dokumen, bukan memformat ulang, jadi alat apa pun yang mengiklankan "deep compression" perlu persetujuan konsumen Anda sebelum Anda mengaktifkannya.

Karena minify hanya menghapus whitespace, manfaatnya hampir sepenuhnya bergantung pada berapa banyak file Anda berupa indentasi. Perkirakan hal itu muncul berulang di tabel-tabel berikut.

3. Pengukuran: empat dokumen, tiga level gzip, plus brotli

Lingkungan: Node v22.13.1, zlib.gzipSync pada level 1 / 6 / 9, brotliCompressSync pada kualitas default (11). Empat sampel sengaja mencakup bentuk yang benar-benar Anda kirim: 50 order bersarang (struktur dalam, key berulang), config 6 level (key panjang, value pendek), array log 200 baris (string panjang, hampir tanpa indentasi), dan media-manifest.json asli dari situs ini (bentuk campuran, string padat URL).

Langkah satu: byte mentah.

Sampel pretty (2 spasi) pretty (4 spasi) setelah minify penghematan minify
50 order bersarang 29.551 B 40.761 B 15.881 B 46,3%
config 6 level 25.450 B 38.012 B 11.679 B 54,1%
array log 200 baris 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%

Dari sinilah badge "menghemat 35%" berasal, dan ia tidak berbohong. Masalahnya muncul di langkah dua.

Langkah dua: kompres setiap varian, lalu tanyakan berapa banyak yang masih ditambahkan minify.

Sampel di atas gzip -1 di atas gzip -6 di atas gzip -9 di atas brotli
50 order bersarang 21,0% 17,0% 8,9% 10,6%
config 6 level 74,2% 59,1% 58,0% 56,4%
array log 200 baris -0,7% -0,4% 0,6% -0,3%
media-manifest.json 1,5% 1,0% 1,0% 1,6%

Dokumen order bersarang yang sama yang kehilangan 46,3% byte mentahnya hanya mempertahankan 17,0% dari itu begitu gzip -6 berperan. Array log malah menjadi negatif: minify lalu kompres sedikit lebih besar daripada mengompres versi pretty aslinya. Nilai negatif bukan bug, itu justru yang seharusnya Anda harapkan, dan bagian berikut menjelaskan sebabnya.

Langkah tiga: pengurangan total terhadap dokumen pretty asli — angka yang benar-benar melintasi wire.

Sampel gzip -6 saja minify + gzip -6 minify + brotli
50 order bersarang 94,5% 95,4% 96,9%
config 6 level 96,0% 98,4% 99,0%
array log 200 baris 89,9% 89,9% 95,0%
media-manifest.json 83,5% 83,6% 86,1%

4. Mengapa gzip memakan kredit minify

gzip adalah LZ77 diikuti pengodean Huffman. Inti gerakan LZ77 adalah "lihat ke belakang melalui jendela 32 KB, temukan urutan berulang, ganti dengan pointer (jarak, panjang)." Indentasi adalah target termudah untuk gerakan itu: setiap level nesting mengeluarkan deret spasi identik, dan puluhan baris berdampingan mengulang kombinasi yang sama. Huffman lalu memberi kode pendek untuk simbol berfrekuensi tinggi, sehingga pola seperti \n plus delapan spasi — yang bisa berulang seribu kali dalam satu file — akhirnya hanya memakan dua atau tiga bit.

Jadi byte yang dihapus minify justru byte yang hampir gratis akan diruntuhkan oleh kompresor. Melakukannya sendiri berarti mengulang pekerjaan yang sudah dikuasai kompresor, dan 46% Anda menciut menjadi 17%. Itulah seluruh cerita di balik tabel di atas.

Hasil negatif pada array log layak penjelasan sendiri. Sampel itu hampir seluruhnya string panjang (baris log, URL, base64), yang berarti sedikit pengulangan dan entropi tinggi — sedikit redundansi untuk dihapus kompresor sejak awal. Newline di versi pretty kebetulan memberi matcher batas yang stabil. Hapus mereka dan LZ77 kehilangan jangkar itu: pada sampel ini, mengompres versi pretty asli dengan gzip -1 menghasilkan 1.508 byte, sementara minify lalu kompres menghasilkan 1.518 byte. Sepuluh byte lebih buruk.

Diurutkan berdasarkan besaran, tuas-tuasnya berbaris seperti ini:

Tuas Penghematan vs dokumen pretty asli (order bersarang) Biaya
minify 46,3% (byte mentah saja) Keterbacaan hilang; butuh langkah build
gzip -6 94,5% Sedikit CPU server; konfigurasi nyaris nol
minify + gzip -6 95,4% Dua hal di atas, dijumlahkan
minify + brotli 96,9% Butuh HTTPS dan modul brotli; pra-kompres aset statis

Lompatan dari 0 ke 94,5% adalah kerja gzip. Lompatan dari 94,5% ke 95,4% — sembilan persepuluh poin — adalah kerja minify. Tabel ini menetapkan di mana usaha Anda seharusnya berada.

5. Level kompresi adalah variabel tersembunyi

Argumen apa pun yang mengasumsikan satu level gzip tetap berdiri di atas pasir, karena level itu langsung mengubah nilai marginal minify:

Pengaturan transport Byte tambahan yang masih dihemat minify (order bersarang)
gzip -1 (default gzip_comp_level Nginx) 21,0%
gzip -6 (default di sebagian besar pustaka standar) 17,0%
gzip -9 8,9%
brotli (kualitas default) 10,6%

Semakin lemah kompresi transport Anda, semakin besar manfaat minify. Itu memberi aturan praktis: pada instalasi Nginx standar yang gzip_comp_level-nya masih 1, minify memberi Anda 21%. Setelah Anda menyetel ke 9 atau beralih ke brotli, angkanya turun ke 9-11% dan biasanya Anda bisa melewatinya.

Sebelum berdebat ke arah mana pun, ukur tingkat mana yang sebenarnya Anda pakai:

# Ambil respons yang sama dengan tiga nilai Accept-Encoding dan bandingkan apa yang diterima
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

Tiga angka menjawab tiga pertanyaan sekaligus: apakah kompresi bahkan aktif, pada level berapa, dan berapa banyak ruang tersisa untuk minify. Jika identity dan gzip kembali hampir identik, kompresi tidak aktif sama sekali — dan yang perlu diperbaiki adalah Nginx, bukan JSON Anda:

gzip on;
gzip_comp_level 6;
gzip_min_length 256;      # lewati respons kecil, mereka hanya membesar
gzip_types application/json application/javascript text/css;

6. Kapan minify benar-benar bermanfaat

Satu uji yang menentukan: apakah byte yang Anda hemat akan tercatat di suatu tempat, atau Anda akan menabrak batas yang menghitung byte mentah? Jika ya, 46% penuh berlaku. Jika jawabannya "ia hanya ada di wire," maka 17% adalah nilai sebenarnya.

Pertama, request body HTTP. Sebagian besar server tidak mendekompres request body, dan gzip dikonfigurasi hampir eksklusif untuk respons. JSON yang Anda POST biasanya tidak punya perlindungan lapisan transport sama sekali, jadi penghematan minify datang dengan harga penuh. Hal yang sama berlaku untuk callback webhook, telemetri MQTT, dan frame WebSocket yang tidak pernah menegosiasikan permessage-deflate.

Kedua, kanal dengan plafon byte keras — dan semuanya menghitung byte yang sudah didekode:

Kanal Batas Bisakah gzip membantu?
Payload sinkron AWS Lambda 6 MB Tidak, diukur setelah dekode
Satu dokumen Firestore 1 MiB Tidak
Satu pesan Kafka (default) 1 MB Tidak, kompresi sisi broker adalah lapisan berbeda
Kuota localStorage ~5 MB (dihitung dalam karakter) Tidak
URL request GET Bergantung server, 8 KB umumnya Tidak

Di kategori ini minify bukan optimasi, melainkan pembeda antara request yang terkirim dan yang gagal. Menyematkan JSON ke query string URL adalah kasus paling tajam: URL tidak pernah dikompres dan mereka memang punya batas panjang, jadi minify dulu, lalu putuskan apakah Anda memerlukan base64 sama sekali.

Ketiga, JSON yang ditulis ke disk atau log. Log dan indeks menyimpan byte literal tanpa lapisan transport yang terlibat. Ada satu catatan yang sering menjegal orang: minify hanya membantu jika sejak awal Anda menulis JSON pretty. Sampel log 200 baris itu hanya menghemat 4,1%, karena barisnya sudah compact dan indentasi hampir tidak menyusun file itu. Sebaliknya, jika logger Anda memanggil JSON.stringify(obj, null, 2), menghapus argumen ketiga adalah uang gratis.

Satu catatan terakhir: ketika byte mentah benar-benar ketat, nama key biasanya tuas yang lebih besar daripada indentasi. Pada media-manifest.json, mengganti 14 key uniknya dengan k0 sampai k13 membawa bentuk terminifikasi dari 21.222 B turun ke 18.887 B — 11,0% lagi. Setelah dikompres, keunggulan tersisa itu menciut menjadi 2,4% di bawah gzip -6 dan 1,3% di bawah brotli. Mekanisme yang sama seperti semua di atas: indentasi dan nama key keduanya informasi struktural berulang, dan kompresor mengklaim dividen yang sama untuk keduanya.

7. Empat kasus ketika hal ini menjadi bumerang

Jangan minify file yang Anda commit. Ubah "qty": 2 menjadi 3 dan file pretty memperlihatkan satu baris bersih di git diff; file terminifikasi menulis ulang seluruh baris, dan dengan ratusan field Anda tidak bisa menemukan perubahannya sama sekali. Ini mekanisme yang sama yang dibahas di Cara Menyelesaikan Konflik Merge JSON — alat diff bekerja per baris, jadi JSON satu baris mengubah setiap edit menjadi "seluruh baris berbeda." Simpan file pretty di repositori dan minify hanya di output build atau saat menulis ke penyimpanan.

Jangan mengompres respons kecil. Respons yang hanya berisi {"ok": true} adalah 16 byte setelah indentasi ditambahkan, dan 36 byte setelah gzip -6 — dua puluh byte lebih besar, karena kamus dan header gzip sendiri memakan ruang. Ini bukan teori; justru inilah alasan gzip_min_length ada. Default Nginx-nya 20 byte jauh terlalu rendah untuk produksi, di mana 256 adalah lantai yang biasa.

Jangan berharap parsing lebih cepat. Menskalakan kumpulan order yang sama ke 4.000 record, dokumen pretty berukuran 2.374.378 byte dibanding 1.282.358 terminifikasi — 46% lebih kecil — namun median waktu JSON.parse hanya bergerak dari 5,7 ms ke 5,0 ms, kira-kira 12% lebih cepat. Memangkas ukuran menjadi separuh tidak memangkas waktu menjadi separuh, karena parser menghabiskan sebagian besar anggarannya untuk memotong string dan mengonversi number; melewati whitespace hanyalah efek samping. Yang sebenarnya menentukan biaya parsing adalah jumlah key dan isi string, yang diuraikan detail oleh Parsing, Rendering dan Streaming File JSON Besar.

Jangan menambahkannya secara retrofit ke layanan yang sudah menjalankan brotli atau gzip -9. Manfaatnya turun ke 9-11%, sementara biayanya — kehilangan keterbacaan permanen saat Anda melakukan debugging — tetap ada. Periksa sebaliknya apakah gzip_types bahkan mencakup application/json. Banyak situs mengaktifkan gzip dan hanya pernah mengompres text/html, meninggalkan JSON sepenuhnya tanpa kompresi; dalam situasi itu minify bukan langkah pertama, memperbaiki konfigurasi Anda adalah.

8. Tabel keputusan: langkah mana lebih dulu

Situasi Anda Lakukan ini dulu Peringkat minify
Tidak ada kompresi transport sama sekali Konfigurasi gzip_types Nginx atau aktifkan kompresi otomatis CDN Lewati; sembilan persepuluh keuntungan menguap
Menjalankan gzip -1 (default Nginx) Naikkan gzip_comp_level ke 6 Kedua, bernilai 21%
Sudah memakai brotli atau gzip -9 Audit key panjang, tambahkan pagination atau pangkas field Lewati; hanya tersisa 9-11%
Menabrak 6 MB Lambda atau 1 MiB Firestore Minify langsung; 46% penuh dihitung Pertama
Body POST, webhook, MQTT Minify langsung; transport jarang berlaku di sini Pertama
File konfigurasi menuju Git Biarkan tetap pretty Hanya di output build
Log, telemetri, dump offline Hapus argumen null, 2 di logger Anda Itu adalah langkah pertama

Aturan satu kalimat: lihat neraca byte mana yang ingin Anda kecilkan — yang tercatat secara lokal (disk, kuota, batas platform, request body) atau yang hanya ada di wire. Lokal dulu berarti minify; hanya-wire berarti aktifkan brotli atau gzip dulu.

Dalam praktik, jalankan loop curl di atas untuk memastikan kompresi transport aktif, lalu dorong sampel melalui alat JSON Minify dan baca angka penghematannya. Jika angka itu di atas 40% sementara curl melaporkan identity dan gzip hampir sama ukurannya, hambatan Anda adalah konfigurasi server, bukan JSON. Jika dua angka curl sudah berdekatan, dokumen ini hampir tidak punya sisa untuk diberikan dan Anda sebaiknya memangkas field atau menambahkan pagination — subjek dari Esensi Desain Kontrak JSON API.

Apakah JSON terminifikasi masih JSON yang valid?

Ya. minify hanya menghapus whitespace yang diizinkan RFC 8259 di sekitar karakter struktural dan tidak pernah menyentuh isi string, sehingga JSON.parse menghasilkan nilai identik dengan aslinya. Satu-satunya korban adalah keterbacaan manusia.

brotli atau gzip?

Pada sampel terminifikasi yang sama, brotli menghemat 96,9% dan gzip -6 menghemat 95,4% — selisih sempit. Gunakan gzip -6 untuk respons dinamis agar CPU tetap rendah, dan sisakan brotli untuk aset statis yang dikirim melalui HTTPS yang bisa Anda pra-kompres menjadi file .br.

Saya sudah minify JSON tetapi API tidak menjadi lebih cepat. Mengapa?

Periksa respons dengan curl -sI untuk header Content-Encoding: gzip. Banyak pengaturan Nginx menetapkan gzip_types hanya ke text/html, sehingga JSON tidak pernah tercakup — dan kurang dari seperlima dari 46% Anda pernah mencapai wire.

Apakah file konfigurasi sebaiknya di-commit dalam bentuk terminifikasi?

Tidak. Alat diff membandingkan baris per baris, jadi JSON satu baris mengubah setiap edit menjadi "seluruh baris berubah," dan menyelesaikan konflik jauh lebih mahal daripada byte yang Anda hemat. Simpan versi pretty di repositori dan minify saat build.

Seberapa besar respons sebelum gzip layak dipakai?

Ambang umumnya 256 byte sampai 1 KB. Respons sekecil {"ok": true} — 16 byte dengan indentasi — membesar menjadi 36 byte di bawah gzip -6, karena kamus dan header memakan lebih banyak daripada yang mereka pulihkan. Itulah tepatnya yang dikendalikan gzip_min_length.