Cache-Control هو ترويس HTTP يحدد قواعد التخزين المؤقت للموارد على جانب العميل وخوادم الوكيل وشبكات CDN باستخدام مجموعة من التوجيهات. على عكس الترويس المتقادم Expires، يدعم Cache-Control عشرات التوليفات: يحدد max-age عمر المورد بالثواني، ويتحكمان private و public في توفر الذاكرة المؤقتة، و no-cache و no-store — التحقق الإجباري. وفقًا لـ Google Web Dev (2025)، يمكن للتكوين الصحيح لـ Cache-Control تقليص وقت تحميل الصفحات بنسبة 50-80% للزيارات المتكررة. وهذا يجعل هذا الترويس حاسمًا لأداء تطبيقات الويب والهواتف المحمولة.
النقاط الرئيسية
Cache-Control هو ترويس HTTP، موحد في HTTP/1.1 (RFC 7234)، يسمح للخادم بتحديد كيفية ومدة الزمن التي يمكن للعملاء والوكلاء و CDN تخزين الاستجابة مؤقتًا. على عكس Expires (HTTP/1.0)، يستخدم Cache-Control التوجيهات — أوامر نصية مدمجة بفواصل: Cache-Control: public, max-age=3600, must-revalidate. يوفر الترويس تحكمًا دقيقًا في كل حلقة في سلسلة التخزين المؤقت.
التخزين المؤقت هو آلية أساسية لأداء تطبيقات الويب والهواتف المحمولة. بدونه، كان كل طلب مستخدم يذهب مباشرة إلى الخادم، مما يسبب حملاً زائدًا وزمن استجابة طويلاً. يحدد Cache-Control ثلاثة مستويات للتخزين المؤقت: المتصفح/التطبيق (ذاكرة مؤقتة خاصة)، خوادم الوكيل (ذاكرة مؤقتة مشتركة) و CDN (ذاكرة مؤقتة موزعة). يقوم كل مستوى بتفسير التوجيهات بشكل مختلف.
تكوين Cache-Control غير الصحيح هو واحد من أكثر الأسباب شيوعًا لمشاكل الأداء. يؤدي التخزين المؤقت العدواني الزائد إلى رؤية المستخدمين لبيانات قديمة. يؤدي التخزين المؤقت الضعيف جدًا إلى طلبات زائدة للخادم وتحميل بطيء. وفقًا لـ Akamai (2025)، يقلل تحسين Cache-Control للمحتوى الثابت حمل الخادم بنسبة 70-90% ويحسن وقت التحميل بنسبة 40-60% لمستخدمي الهواتف المحمولة.
ظهر Cache-Control في HTTP/1.1 (RFC 2616, 1999) كبديل لـ Expires. كان لدى Expires مشكلة أساسية: كان يستخدم تاريخًا مطلقًا يعتمد على المناطق الزمنية للخادم والعميل. حل Cache-Control هذه المشكلة بالانتقال إلى الوقت النسبي (max-age بالثواني من لحظة استلام الاستجابة). لاحقًا، في RFC 7234 (2014)، تمت إضافة توجيهات جديدة: immutable للأصول الثابتة، stale-while-revalidate و stale-if-error للتحقق المؤجل.
يتضمن Cache-Control أكثر من 10 توجيهات مقسمة إلى ثلاث مجموعات: توجيهات الطلب (عميل → خادم)، توجيهات الاستجابة (خادم → عميل) والإمتدادات. في الممارسة، يستخدم تطوير الهواتف المحمولة 6-7 توجيهات استجابة رئيسية تغطي 95% من سيناريوهات التخزين المؤقت. دعنا ننظر إلى كل واحد مع أمثلة وتوصيات.
| التوجيه | المعنى | مثال |
|---|---|---|
| max-age | عمر المورد بالثواني من لحظة الاستجابة | max-age=3600 — ساعة واحدة |
| s-maxage | max-age للذاكرة المؤقتة المشتركة (وكيل، CDN) | s-maxage=86400 — يوم واحد لـ CDN |
| public | يسمح بالتخزين المؤقت للجميع (بما في ذلك الوكلاء) | public, max-age=3600 |
| private | يسمح بالتخزين المؤقت فقط للمتصفح/التطبيق | private, max-age=600 |
| no-cache | لا تستخدم دون تحقق (304 مطلوب) | no-cache |
| no-store | منع التخزين المؤقت بالكامل | no-store |
| must-revalidate | بعد max-age، يجب إعادة التحقق مع الخادم الأصلي | max-age=3600, must-revalidate |
| immutable | لن يتغير المورد (للأصول الثابتة ذات الإصدار) | max-age=31536000, immutable |
max-age هو أهم توجيه. يمنع العميل من إرسال طلب إلى الخادم لمدة زمنية محددة. بالنسبة للأصول الثابتة (CSS، JS، صور)، يتم تعيين max-age عادة من يوم واحد إلى سنة واحدة. لاستجابات API — من 0 ثانية (بيانات جديدة دائمًا) إلى 5-10 دقائق (بيانات مرجعية). s-maxage يسمح بتعيين أعمار مختلفة لـ CDN والمتصفح: تخزن CDN نسخة لمدة يوم واحد، والمتصفح لمدة ساعة واحدة.
كثيرًا ما يتم الخلط بين هذين التوجيهين. no-cache لا يمنع التخزين المؤقت — يتطلب التحقق من النسخة المخزنة مؤقتًا عند كل استخدام من خلال طلب شرطي (If-Modified-Since أو If-None-Match). إذا استجاب الخادم بـ 304 — يستخدم العميل الذاكرة المؤقتة. إذا 200 — يقوم بتحديثها. no-store، من ناحية أخرى، يمنع تمامًا حفظ الاستجابة في أي ذاكرة مؤقتة، بما في ذلك القرص والذاكرة المؤقتة. استخدم no-store فقط للبيانات الحساسة — الرموز، بيانات الدفع، الوثائق الشخصية.
يحدد ترويس Expires (HTTP/1.0) أيضًا عمر المورد ولكنه يستخدم تاريخًا مطلقًا: Expires: Thu, 03 Jul 2026 12:00:00 GMT. يستخدم Cache-Control max-age وقتًا نسبيًا من لحظة الاستجابة. الفرق حاسم للأنظمة الموزعة: إذا كان الخادم والعميل في مناطق زمنية مختلفة، فقد يتم تفسير Expires بشكل غير صحيح. Cache-Control ليس لديه هذه المشكلة — 3600 ثانية هي دائمًا 3600 ثانية.
عندما يكون كلا الترويسين موجودين، يحظى Cache-Control بالأولوية على Expires. هذا محدد في RFC 7234: “إذا كانت الاستجابة تتضمن حقل Cache-Control مع توجيه max-age، فإن المستلم يجب أن يتجاهل حقل Expires.” في الممارسة، يوصى بعدم إرجاع Expires بالنسبة للعملاء الحديثين، لأن Cache-Control يغطي جميع سيناريوهات Expires. ومع ذلك، للتوافق مع الوكلاء والمتصفحات القديمة، يمكن إرجاع كلا الترويسين.
بقي Expires بشكل أساسي للمحتوى الثابت على Nginx و Apache — تقوم هذه الخوادم بإضافة كلا الترويسين تلقائيًا. إذا واجه مشروعك Expires دون Cache-Control، استبدله بـ Cache-Control مع max-age: تتحسن دقة التحكم في التخزين المؤقت، وتتلاشى الاعتمادية على المنطقة الزمنية. للترحيل، يكفي تكوين الخادم لإضافة Cache-Control بدلاً من Expires.
# Nginx: Cache-Control للملفات الثابتة
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable, max-age=2592000";
}
# سياسات مختلفة لأنواع مختلفة من المحتوى
location /api/config {
expires -1;
add_header Cache-Control "no-cache, must-revalidate";
}
location /api/static-data {
expires 5m;
add_header Cache-Control "public, max-age=300";
}
في تكوين Nginx، يتم تعيين الملفات الثابتة (CSS، JS، صور) مع Cache-Control لمدة 30 يومًا باستخدام سمة immutable — تخبر هذه السمة المتصفح أن المورد لا يتغير أبدًا في هذا العنوان (إصدار عبر التجزئة في اسم الملف). تستخدم نقاط نهاية API no-cache للبيانات الديناميكية و public مع max-age قصير للبيانات المرجعية — القوائم التي يتم طلبها بشكل متكرر ونادراً ما تتغير.
في تطبيقات الهواتف المحمولة، يلعب Cache-Control دورًا خاصًا بسبب قيود شبكات الهواتف المحمولة: زمن استجابة عال، اتصال غير مستقر، حدود البيانات. يسمح التخزين المؤقت الصحيح بعرض البيانات للمستخدم فورًا، حتى خارج الاتصال، وتحديثها في الخلفية. يحتوي OkHttp على Android و URLSession على iOS على أنظمة تخزين مؤقت مضمنة تحترم Cache-Control.
OkHttp يستخدم CacheInterceptor، الذي يقرأ Cache-Control من الاستجابة ويدير التخزين المؤقت تلقائيًا. إذا أرجع الخادم Cache-Control: max-age=3600، فلن يرسل OkHttp طلبًا إلى الخادم لمدة ساعة. بعد انتهاء max-age، يرسل OkHttp طلبًا شرطيًا مع If-Modified-Since و If-None-Match. تكوين الذاكرة المؤقتة في OkHttp: OkHttpClient.Builder().cache(Cache(directory, maxSize)).
fun createCachedClient(cacheDir: File): OkHttpClient {
return OkHttpClient.Builder()
.cache(Cache(cacheDir, 10L * 1024 * 1024))
.addNetworkInterceptor { chain ->
val response = chain.proceed(chain.request())
response.newBuilder()
.header("Cache-Control",
"public, max-age=300")
.removeHeader("Pragma")
.build()
}
.build()
}
يقوم الكود بإنشاء OkHttpClient بذاكرة مؤقتة سعتها 10 ميغابايت ويلغي Cache-Control من خلال NetworkInterceptor. إذا لم يرجع الخادم Cache-Control أو يستخدم Expires، يضيف المعاوض public, max-age=300 (5 دقائق). يقوم المعاوض بإزالة الترويس القديم Pragma (HTTP/1.0) للتوافق. يعمل التخزين المؤقت على iOS بشكل مماثل من خلال URLCache.shared مع إعدادات memoryCapacity و diskCapacity.
يسمح التوجيه stale-while-revalidate بعرض ذاكرة مؤقتة قديمة للمستخدم بينما يقوم التطبيق بتحميل بيانات جديدة في الخلفية. يوفر هذا تأثير استجابة فورية: يرى المستخدم المحتوى فورًا، وبعد ثانية يتحدث إلى الإصدار الحالي. مدعوم في OkHttp منذ الإصدار 3.10 و URLCache على iOS 14+. مثال: Cache-Control: max-age=3600, stale-while-revalidate=300 — ساعة واحدة من الذاكرة المؤقتة الحديثة، ثم 5 دقائق من عرض البيانات القديمة مع تحديث خلفية.
تتطلب أنواع الموارد المختلفة استراتيجيات مختلفة للتخزين المؤقت. دعنا ننظر إلى التكوينات المثلى للسيناريوهات النموذجية في تطوير الهواتف المحمولة. بالنسبة للمحتوى الثابت باستخدام تجزئة في اسم الملف (bundle.abc123.js)، يمكنك تعيين max-age حتى سنة واحدة مع immutable. لقوائم API التي نادرًا ما يتم تحديثها (الأدلة، الفئات) — max-age من 5 دقائق إلى ساعة واحدة مع stale-while-revalidate.
| نوع المورد | Cache-Control | شرح |
|---|---|---|
| أصول ثابتة مزودة بإصدار | public, max-age=31536000, immutable | سنة واحدة، الملفات لا تتغير (تجزئة في URL) |
| أصول ثابتة غير مزودة بإصدار | public, max-age=86400, must-revalidate | يوم واحد مع إعادة تحقق إجبارية بعدها |
| API: بيانات مرجعية | public, max-age=600, stale-while-revalidate=60 | 10 دقائق ذاكرة مؤقتة + دقيقة قديمة |
| API: بيانات المستخدم | private, max-age=60 | دقيقة واحدة، فقط لمستخدم محدد |
| API: بيانات حساسة | no-store | منع كامل للتخزين المؤقت |
| صفحات HTML | no-cache, must-revalidate | التحقق عند كل طلب، 304 إذا لم يتغير |
من المهم تذكر الأمان: للاستجابات التي تحتوي على بيانات شخصية للمستخدم، قم دائمًا بتعيين private. بدون هذا التوجيه، يمكن لوكيل عام (على سبيل المثال، مؤسسي) تخزين الاستجابة مؤقتًا وتسليمها لمستخدم آخر. لرموز المصادقة ومعلومات الدفع، استخدم no-store — حتى الذاكرة المؤقتة الخاصة لا يجب أن تخزن هذه البيانات على القرص.
للتحقق من صحة Cache-Control، استخدم الترويس Age (ما مدة تخزين الذاكرة المؤقتة بالثواني) و X-Cache (hit/miss على CDN). في المتصفح — علامة التبويب Network، يظهر العمود Size “from disk cache” أو “304 Not Modified”. إذا كان يجب تخزين المورد مؤقتًا ولكنه يتم تحميله في كل مرة، تحقق مما إذا كان الخادم يضيف Cache-Control: no-cache أو Pragma: no-cache مع توجيهاتك.
الأسئلة الشائعة
يطبق max-age على جميع الذاكرات المؤقتة (بما في ذلك المتصفحات)، ويطبق s-maxage فقط على الذاكرات المؤقتة المشتركة (الوكلاء، CDN). إذا تم تحديد s-maxage، فإن CDN تتجاهل max-age وتستخدم s-maxage. يسمح هذا بتعيين أعمار مختلفة للمتصفح و CDN.
لا، بعد إرسال استجابة مع max-age، لن يقوم العميل بإرسال طلب حتى ينتهي المؤقت. لإبطال الذاكرة المؤقتة فورًا، تحتاج إلى تغيير URL المورد (إضافة إصدار/تجزئة) وإرسال إشعارات push أو رسائل WebSocket للإعادة التعيين الإجباري.
تخبر توجيه immutable (RFC 8246) المتصفح أن المورد لن يتغير أبدًا في هذا العنوان. لا يحاول المتصفح حتى إرسال طلب شرطي عند تحديث الصفحة — يستخدم الذاكرة المؤقتة حتى انتهاء max-age. تعمل فقط مع الملفات ذات الإصدار.
يأخذ Googlebot في الاعتبار Cache-Control: يسرع التخزين المؤقت الطويل الزحف المتكرر. noindex مع ذاكرة مؤقتة سريعة لا بأس به. يمكن لـ no-store أن يبطئ الفهرسة لأن Googlebot سيقوم بتحميل الصفحة من البداية في كل مرة. يزيد max-age القصير جدًا من حمل الخادم أثناء الزحف.
من خلال helmet أو middleware: res.set('Cache-Control', 'public, max-age=3600'). للملفات الثابتة، استخدم express.static مع وسيط maxAge: express.static('public', {maxAge: '1y'}). للمسارات الديناميكية — فرديًا في كل معالج.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.