gzip، Brotli، Zstd ضغط HTTP للمساهمين الذين يضعون content-encoding: identity بشكل عفوي

تحديث في

كيف تعمل مفاوضة ضغط HTTP (Accept-Encoding / Content-Encoding)، مقارنة جانبية بين gzip و Brotli و Zstd، كيفية التحقق من أن الضغط يعمل فعلاً باستخدام curl، واربعة أخطاء في التكوين تُوقف الضغط بشكل سري.

gzip، Brotli، Zstd: ضغط HTTP للمطورين الذين يضعون content-encoding: identity بشكل عفوي 1
إعلان · حذف؟

تكوين nginx الخاص بك هو gzip on;. تُرجع تطبيقك بيانات JSON. يظل جسم الاستجابة 35 كيلو بايت غير مُضغوط. لا توجد أخطاء، ولا تحذيرات — لا يتم ضغط المحتوى بشكل سلبي.

هذا عادةً ما يكون أحد أربع مُعدّلات خاطئة. ولكن أولاً: كيف تعمل التفاوض في الواقع.

كيف تعمل تفاوض ضغط HTTP

أثنان من الرموز، لا شيء آخر. يُعلن العميل ما يمكنه تحليله في Accept-Encoding. يختار الخادم خوارزمية، ويُضغط جسم الاستجابة، ويُعلن اختياره في Content-Encoding:

GET /api/data HTTP/1.1
Host: example.com
Accept-Encoding: gzip, deflate, br, zstd
HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: br
Vary: Accept-Encoding

ال Vary: Accept-Encoding الرأس غير مُتطلّب إذا كنت تهتم بالدقة في تخزين CDN. بدونه، قد يخزن CDN استجابة مضغوطة بـ Brotli ويُقدّمها لعميل يعلن فقط عن gzip في Accept-Encoding. ثم يحاول العميل تحليل Brotli كـ gzip ويحصل على بيانات خاطئة. يضيف nginx هذا تلقائيًا. gzip_vary on; مُسمى تقنيًا صحيح — يعني "لا تشفير" — لكن لا أحد يضعه بشكل صريح. الوضع الفعلي هو العكس: عدم وجود

Content-Encoding: identity رأس في المقام الأول عندما كنت تتوقع وجوده. Content-Encoding العنوان في كل الحالات عندما كنت تتوقعه.

التحقق من أن الضغط يعمل فعليًا

قبل بدء التصحيح للتكوين، تأكد من المشكلة:

# Check headers only
curl -sI -H "Accept-Encoding: gzip, br, zstd" https://example.com/api/data   | grep -i "content-encoding\|vary"

# Compare compressed vs uncompressed size
curl -so /dev/null -w "uncompressed: %{size_download} bytes
" https://example.com/api/data
curl -so /dev/null --compressed -w "compressed:   %{size_download} bytes
" https://example.com/api/data

--compressed يُرسل Accept-Encoding: deflate, gzip, br, zstd تلقائيًا ويُحلّل الاستجابة. إذا كانت الأرقام متطابقة، فإن الضغط لا يعمل. إذا كنت ترغب في مراجعة جميع رموز الاستجابة وما تعنيه في السياق، فإن HTTP Header Analyzer سيُضيف عليها توضيحات بما في ذلك Vary, Content-Encoding، وتعليمات cache-control.

gzip مقابل Brotli مقابل Zstd

تُعتبر ثلاث خوارزميات مُهمة لـ HTTP اليوم. الأرقام المُختبرة أدناه من مراجعات Zstd الرسمية على مجموعة سيليسيا — مجموعة معيارية من ملفات واقعية مختلطة (HTML، كود مصدر، PDFs، قواعد بيانات)، تم اختبارها على معالج Core i7-9700K. تُضغط المحتويات النصية أو المُبسطة بشكل أفضل عادةً.

الخوارزميةمستوىالنسبةضغطفك الضغط
gzip1 (سريع)2.74x69 ميغابايت/ثانية380 ميغابايت/ثانية
gzip6 (الافتراضي)2.97x29.9 ميغابايت/ثانية360 ميغابايت/ثانية
gzip9 (الحد الأقصى)3.10x18 ميغابايت/ثانية360 ميغابايت/ثانية
Brotli43.18x104 ميغابايت/ثانية440 ميغابايت/ثانية
Brotli11 (الحد الأقصى)3.74x0.4 ميغابايت/ثانية440 ميغابايت/ثانية
Zstd1 (سريع)2.88x430 ميغابايت/ثانية1,380 ميغابايت/ثانية
Zstd3 (الافتراضي)3.01x320 ميغابايت/ثانية1,350 ميغابايت/ثانية
Zstd19 (الحد الأقصى)3.40x17.5 ميغابايت/ثانية1,380 ميغابايت/ثانية

gzip هو المعيار. مستوى 6 هو الخيار المناسب للإدخال في الوقت الفعلي — إن استثمار 65% من المعالج لانتقال من مستوى 6 إلى مستوى 9 يُعطيك حوالي 4% من النسبة المثلى. ليس ذلك مبررًا للإجابات الديناميكية. الملفات الثابتة المُضغوطة هي حساب مختلف.

Brotli يُفوق بشكل حقيقي gzip عند تكلفة المعالج المماثلة على المستويات 4-6، ويُحلّل بسرعة ~20%. السبب: يمتلك Brotli قاموسًا ديناميًا مُعدّ خصيصًا للمحتوى الويب — عناصر HTML، أسماء الحقول في HTTP، كلمات JavaScript. يُحقق نسبًا أفضل من مُضغّط عام على نفس المحتوى. مستوى 11 مُتاح فقط للملفات الثابتة المُضغوطة مسبقًا؛ بسرعة ضغط 0.4 ميغابايت/ثانية، ستُضغط حوالي 25 ميغابايت في الدقيقة. هذا خطوة بناء، وليس مُعالج طلب.

Zstd هو القصة السريعة. المستوى الافتراضي (3) يُحقق نفس النسبة كما في gzip لكنه يُضغط 10 أضعاف أسرع ويُحلّل تقريبًا 4 أضعاف أسرع. الحدّ الأساسي هو دعم المتصفح: Chrome 118+ (أكتوبر 2023)، Firefox 126+ (مايو 2024)، Safari 18+ (نهاية 2024). ليس مناسبًا كمُدخل وحيد حتى الآن، لكن إذا كان الخادم يتفاوض بشكل صحيح، فإن إضافة Zstd تُكلفك خطوات بسيطة في التكوين وتساعد المُستخدمين الذين يعلنون عن ذلك. عند مستوى 19، يقترب Zstd من مستوى Brotli-11 في النسبة دون أن يُعاني من عواقب سلبية في سرعة الضغط، مما يجعله أكثر قابلية للاستخدام في المهام الديناميكية عند احتياجات ضغط عالية.

دعم المتصفح والعميل

الخوارزميةالكرومFirefoxرحلات السفاريحافةنود.جي اس
gzipالكلالكلالكلالكلمدمج (zlib)
deflateالكلالكلالكلالكلمدمج (zlib)
Brotli (br)51+44+11+15+v10.16+
Zstd118+126+18+118+v21+

ملاحظة مهمة: br و zstd يظهر فقط في Accept-Encoding على اتصالات HTTPS. يُقصِّد المتصفحات عدم إعلانها على اتصالات بسيطة — كوسيلة للدفاع ضد هجمات التدخل (MITM) التي يمكن أن تُدخل رموز التشفير. إذا كنت تختبر على http://localhost وتسأل لماذا ترى فقط gzip, deflate، فهذا السبب. اختبر عبر HTTPS أو استخدم curl مباشرة (لا يُطبّق هذا القيود).

أربعة مُعدّلات خاطئة تُوقف التفاوض بشكل سلبي

1. غياب gzip_proxied (مُحَوّل nginx)

مودول nginx gzip يُضغط الاستجابات التي يُنتجها بنفسه. بالنسبة للطلبات المُحَوّلة (التطبيق المُحَوّل إلى nginx إلى العميل)، تحتاج إلى gzip_proxied — وإلا فإن nginx يُضغط فقط على الاستجابات من أدوات المحتوى الخاصة به، وليس من proxy_pass الخوادم المُحَوّلة.

# This is NOT enough when nginx is a reverse proxy:
gzip on;
gzip_types text/plain application/json application/javascript text/css;

# You need this too:
gzip_proxied any;

العديد من تكوينات nginx هي مُحَوّلات. تُغفل معظم الدروس هذا العنصر. هذان الحقيقةان يفسران الكثير من الاستجابات غير المضغوطة. gzip_proxied. هذه المعلومتين تفسر الكثير من الاستجابات المُضغوطة بشكل سلبي.

2. نوع MIME غير موجود في gzip_types

الافتراضي لـ nginx gzip_types يكون text/html فقط. JSON، CSS، JavaScript، SVG — كلها غير مضغوطة ما لم تُدرج بشكل صريح:

gzip_types
  text/plain
  text/css
  text/xml
  text/javascript
  application/json
  application/javascript
  application/xml
  application/rss+xml
  image/svg+xml;

يُقارن nginx على النوع الأساسي للمحتوى، لذا فإن application/json يغطي application/json; charset=utf-8. لا حاجة لاستبدال الأنواع المُحَوّلة بشكل منفصل.

3. مُحَوّل وسيط يُزيل Accept-Encoding

AWS ALB، مُعدّات Cloudflare Workers الخاطئة، وبعض تكوينات بوابات API تُزيل أو تُعيد تشكيل Accept-Encoding قبل وصولها إلى مصدرك. لا يرى الخادم الرمز، يُفترض أنه لا يوجد ضغط، وكل من يقع تحته يظن أن الميزة مُعطلة بينما المشكلة الحقيقية هي في الوسيط. لا تظهر أي خطأ في السلسلة.

أعد التحقق من الاستجابة المصدرية مقارنة بالاستجابة CDN:

# Via CDN/proxy
curl -sI -H "Accept-Encoding: gzip, br" https://example.com/api/data

# Direct to origin (bypassing CDN via --resolve or direct IP)
curl -sI -H "Accept-Encoding: gzip, br" --resolve "example.com:443:ORIGIN_IP" https://example.com/api/data

إذا أرسلت المصدر مباشرة Content-Encoding: gzip لكن الاستجابة CDN لا تحتوي على Content-Encoding، فإن CDN يُزيل شيء ما — أكثر احتمالًا يُزيل Accept-Encoding مُدخلًا بحيث لا يُضغط المصدر في البداية.

4. يُضغط التطبيق المُحَوّل، ثم يحاول nginx الضغط مرة أخرى

إذا ضغطت تطبيقك مثل Node.js/Go/Python على جسم الاستجابة ووضع Content-Encoding: gzip، يجب أن يتجاهل nginx التكرار في الضغط — لكن هذا يعتمد على تسلسل الرموز. إذا أرسلت التطبيق الرمز أثناء التدفق أو إذا كان اكتشاف nginx يُتسارع، فقد تنتهي بملف مضغوط مزدوج يفشل فيه العميل في التحليل.

الحل النظيف: دع nginx يمتلك جميع الضغط. احذف وسيلة الضغط من تطبيقك (مودول compression في express، مودول gzip.Handlerفي Go، إلخ)، عدّل الاستجابات ببساطة، ودع nginx يُضغط في الحدود. تحقق من نفس الفوائد في الأداء، دون خطر التكرار في الضغط.

التكوينات المُعملة

nginx

gzip on;
gzip_vary on;         # adds Vary: Accept-Encoding automatically
gzip_proxied any;     # compress responses from proxied upstreams
gzip_comp_level 6;
gzip_min_length 256;  # skip tiny responses where overhead isn't worth it
gzip_types
  text/plain
  text/css
  text/xml
  text/javascript
  application/json
  application/javascript
  application/xml
  application/rss+xml
  image/svg+xml;

# Brotli requires the ngx_brotli module
# https://github.com/google/ngx_brotli
brotli on;
brotli_comp_level 4;
brotli_static on;     # serve pre-compressed .br files when they exist
brotli_types
  text/plain
  text/css
  application/json
  application/javascript
  image/svg+xml;

Apache

LoadModule deflate_module modules/mod_deflate.so
AddOutputFilterByType DEFLATE text/html text/plain text/css   application/json application/javascript image/svg+xml
Header append Vary Accept-Encoding

# mod_brotli requires Apache 2.4.26+
LoadModule brotli_module modules/mod_brotli.so
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/css   application/json application/javascript image/svg+xml

Caddy

Caddy يُفعّل gzip و Brotli بشكل افتراضي. للاضافة إلى Zstd بشكل صريح:

example.com {
  encode gzip zstd br
  reverse_proxy localhost:3000
}

لا توجد قائمة بأنواع MIME، ولا gzip_proxied حالات خاصة، معالجة صحيحة Vary بشكل مُسبق. الإجابة الصادقة على "أي خادم يمتلك أقل سطح مُعرّض للخطأ في الضغط" هي Caddy.

اختبار الضغط على محتواك الخاص

الأرقام المُختبرة على مجموعة سيليسيا تُخبرك بعلاقة الأداء، لكن محتواك الخاص يهم أكثر. استجابة API متكررة مع أسماء حقول متسقة تُضغط بشكل مختلف عن JavaScript المُبسط أو HTML المختلط. أدوات هذه تسمح لك بتجربة محتواك الخاص في المتصفح دون تشغيل خادم ضغط محلي:

مفيد عند اتخاذ القرار حول ما إذا كان من الأفضل تثبيت الملفات الثابتة مسبقًا بـ Brotli-11 أم فقط يُدير nginx ضغطًا في الوقت الفعلي. الصق محتواك الفعلي، وقارن النسب، واتخذ القرار بناءً على أرقام حقيقية.

أحيانًا تكون أبسط الأدوات هي الأكثر ديمومة.

إذا لم تُضغط الاستجابات و curl -sI يؤكد عدم وجود Content-Encoding، فإن الحل هو بالتأكيد أحد المُعدّلات الخاطئة الأربعة أعلاه — الأكثر احتمالًا هو gzip_proxied any; لـ nginx، أو أن CDN يأكل Accept-Encoding الرأس. تحقق من المصدر مباشرة قبل تحميل تكوين الخادم.

بالنسبة لاختيار الخوارزمية: مستوى gzip-6 مناسب للإجابات الديناميكية والذاتية، ويحمل تكلفة تكوين منخفضة. أضف Brotli للملفات الثابتة — ضغطها مسبقًا على مستوى 11 أثناء خطوة البناء، وقدمها مع brotli_static on، ودع nginx يعود إلى gzip للعميل الذي لا يعلن عن br. يُعتبر إضافة Zstd الآن مبررًا؛ تكلفة التكوين بسيطة وحجم المُستندات في المتصفح ينمو بسرعة. تقديم جميع الثلاثة مع معالجة صحيحة Vary: Accept-Encoding هي الموضع الصحيح لأي شيء جديد.

هل تريد حذف الإعلانات؟ تخلص من الإعلانات اليوم

تثبيت ملحقاتنا

أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع

أضف لـ إضافة كروم أضف لـ امتداد الحافة أضف لـ إضافة فايرفوكس أضف لـ ملحق الأوبرا

وصلت لوحة النتائج!

لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!

إعلان · حذف؟
إعلان · حذف؟
إعلان · حذف؟

ركن الأخبار مع أبرز التقنيات

شارك

ساعدنا على الاستمرار في تقديم أدوات مجانية قيمة

اشتري لي قهوة
إعلان · حذف؟