لو تطبيقك بيرسل bundle JavaScript بحجم 348KB مضغوط بـ gzip، Brotli على level 11 بيوصّله لـ 251KB بنفس الكود وبدون لمس الـ build. التحسين بيقع على شبكة الزائر — يعني وقت تحميل أقل، فاتورة CDN أرخص، و LCP أحسن في صفحات الـ landing.
Brotli vs gzip: مقارنة عملية لضغط أصول الويب
الـ gzip معاك من 1992. Brotli جاي من Google سنة 2015 ودخل الويب بشكل واسع 2017. النهارده 96% من المتصفحات الحديثة بتدعم Brotli على HTTPS حسب Can I Use 2026. الموضوع مش "هل أستخدمه"، السؤال "إزاي أستخدمه وعلى أي مستوى compression".
المشكلة باختصار
تطبيق Next.js عادي بعد build بيطلع ملف main.js حجمه 1,152KB raw. الـ CDN بيضغطه بـ gzip level 6 (الافتراضي) فيوصل لـ 348KB. لو بتخدم 50 ألف زائر شهريًا، التطبيق بيستهلك حوالي 16GB transfer. لو نزّلته لـ 251KB بـ Brotli level 11، بتدفع 12.5GB — يعني وفّرت 21% بدون كتابة سطر كود.
إيه اللي بيخلي Brotli أحسن من gzip
قبل التعريف العلمي، خد تشبيه واضح: تخيّل إنك بتنقل عفش. gzip بيبصّ على الكرتونة اللي قدامه بس وبيشوف هل في حاجة مكررة جواها — مثلًا يلاقي كلمة "function" مكتوبة 80 مرة في الملف فيخزّنها مرة واحدة. Brotli معاه قاموس جاهز فيه أكتر من 13,000 كلمة وعبارة شائعة في الويب (HTML tags، CSS properties، JavaScript keywords) — يعني قبل ما يبدأ يضغط، عنده "غش" جاهز قبل ما يفتح أول كرتونة.
التعريف العلمي: Brotli بيستخدم خوارزمية مركبة من LZ77 + Huffman coding + 2nd-order context modeling مع static dictionary حجمه 122,784 byte. الـ static dictionary ده هو السر — gzip ما عندوش حاجة زيها، فبيبدأ من الصفر مع كل ملف. تفاصيل المواصفة موجودة في RFC 7932.
أرقام حقيقية: قياس على bundle Next.js
قياس فعلي على ملف main.js حجمه 1,152KB من production build:
- gzip level 6 (default): 348KB — وقت ضغط 84ms
- gzip level 9 (max): 339KB — وقت ضغط 280ms
- Brotli level 4: 296KB — وقت ضغط 38ms
- Brotli level 6: 268KB — وقت ضغط 95ms
- Brotli level 11 (max): 251KB — وقت ضغط 4,200ms
ركز في الأرقام دي: Brotli level 11 بيضغط أحسن لكن بياخد 50× وقت gzip level 6. مفيش معنى تستخدمه على الفلاي مع كل request. الحل العملي اسمه Brotli static — تضغط مرة واحدة في الـ CI وتخدم الملف الجاهز.
إعداد NGINX: Brotli static + dynamic
# /etc/nginx/nginx.conf
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
http {
# static: يخدم الملفات اللي ضغطتها مسبقًا في build
brotli_static on;
# dynamic: يضغط responses حية (APIs مثلًا) بمستوى أخف
brotli on;
brotli_comp_level 4;
brotli_min_length 1024;
brotli_types text/plain text/css application/javascript
application/json image/svg+xml;
# احتفظ بـ gzip للمتصفحات الأقدم (4% فقط)
gzip on;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types text/plain text/css application/javascript
application/json image/svg+xml;
}
الـ brotli_static on بيخلي NGINX يدوّر على ملف main.js.br جنب main.js. لو لقاه، بيخدمه مباشرة بدون أي عملية ضغط في وقت الطلب. ده بيخليك تستخدم level 11 بدون ما السيرفر يتعب أصلًا.
سكربت بناء الملفات المضغوطة في CI
#!/usr/bin/env bash
# build/precompress.sh
set -euo pipefail
find dist/assets -type f \( -name "*.js" -o -name "*.css" -o -name "*.svg" \) \
-size +1k -print0 | \
while IFS= read -r -d '' file; do
brotli -11 -k --force "$file"
gzip -9 -k --force "$file"
done
echo "Precompressed assets:"
ls -la dist/assets/main.js* 2>/dev/null || true
# main.js 1180k
# main.js.br 251k
# main.js.gz 339k
ضيف الخطوة دي في GitHub Actions أو في Dockerfile قبل ما الصورة النهائية تتنشر. لازم تتأكد إن CI runner عنده brotli CLI متثبت. على Ubuntu: apt-get install -y brotli.
إعداد Cloudflare وما يجب الانتباه له
لو CDN بتاعك Cloudflare، Brotli شغّال تلقائيًا على الـ edge من 2018 لأي origin بيرجع HTML/JS/CSS. لكن في فرق مهم:
- Cloudflare بيضغط بـ Brotli level 4 على الفلاي — مش 11.
- لو الـ origin بتاعك بيرجع
Content-Encoding: brأصلًا (يعني ضغطته مسبقًا)، Cloudflare بيخدم الملف زي ما هو ومش بيعيد ضغط.
الخلاصة: لو بتسلم static assets من origin بـ Brotli 11 precompressed، Cloudflare هيوزّعهم زي ما هم على المستخدم. لو بترسلهم raw، Cloudflare هيعمل Brotli 4 على الفلاي — أقل كفاءة بحوالي 6% في الحجم.
الـ trade-offs بصراحة
Brotli level 11 بيضغط أحسن من gzip level 9 بحوالي 21% لكن وقت الضغط 15× أكتر. للسيرفر اللي بيخدم static files من قرص، ده مش مهم — الضغط بيحصل مرة واحدة في build time. للـ APIs اللي بترجع JSON ديناميكي، استخدم Brotli level 4 أو 5 — الفرق في الزمن مع gzip بيكون أقل من 5ms والوفر في الحجم 8-12%.
الـ CPU على المتصفح: Brotli decompress في المتصفح أسرع من gzip بحوالي 5% حسب benchmarks Google، يعني الموبايل الضعيف مش هيتعب أكتر. الـ overhead الحقيقي بيكون على السيرفر لو بتـ compress dynamic responses بـ level عالي على الفلاي — هتضرب الـ TTFB.
متى لا تستخدم Brotli
- على HTTP بدون TLS — المتصفحات الحديثة بترفض compression جوه HTTP plain. لازم HTTPS.
- على ملفات أصلًا مضغوطة (JPG، PNG، MP4، WOFF2) — مفيش وفر، بس وقت CPU زيادة.
- على ملفات أقل من 1KB — overhead headers وعملية الضغط أكبر من الوفر.
- لو الـ origin سيرفرك ضعيف وبيـ compress dynamic responses بـ Brotli 11 على الفلاي — هتضرب الـ TTFB. خليه على level 4 لـ dynamic.
- على connection داخلي بين microservices في نفس الـ data center — اللي بتدفعه CPU أكتر من اللي بتوفّره في bandwidth.
كيف تتأكد إنه شغّال فعلاً
# اطلب الملف بصيغة Brotli صراحةً
curl -H "Accept-Encoding: br" -I https://yourdomain.com/main.js
# لازم تشوف السطر:
# content-encoding: br
# لو رجع:
# content-encoding: gzip
# يبقى brotli_static مش شغّال أو الملف .br مش موجود
# قارن الحجم الفعلي اللي بيوصل
curl -H "Accept-Encoding: br" -o /dev/null -s -w "%{size_download}\n" \
https://yourdomain.com/main.js
curl -H "Accept-Encoding: gzip" -o /dev/null -s -w "%{size_download}\n" \
https://yourdomain.com/main.js
الخطوة التالية
افتح ملف الـ NGINX config بتاعك وفعّل brotli_static on. في CI، ضيف خطوة precompression بـ brotli -11 -k على ملفات الـ JS و CSS اللي طلعت من build. قِس main.js قبل وبعد بـ curl -I. لو الفرق أقل من 15% في الحجم، اتفقد إن Brotli level 11 شغّال فعلًا مش 4، وإن الملفات .br موجودة جنب الأصلية في مسار الـ assets.
المصادر
- RFC 7932 — Brotli Compressed Data Format (IETF, 2016): datatracker.ietf.org/doc/html/rfc7932
- Can I Use — Brotli Accept-Encoding (2026): caniuse.com/brotli
- Cloudflare Docs — Brotli compression on the edge: developers.cloudflare.com/speed/optimization/content/brotli
- Google ngx_brotli — NGINX module source: github.com/google/ngx_brotli
- Akamai — Understanding Brotli's Better Compression: akamai.com/blog/performance/understanding-brotlis-potential
- MDN — Content-Encoding HTTP header: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding