الذاكرة المؤقتة للمتصفح

الذاكرة المؤقتة للمتصفح (Browser Cache) هي مجموعة النسخ التي يحتفظ بها المتصفح من ملفات نزّلها من قبل: صفحات HTML والسكربتات وملفات التنسيق والصور والخطوط وبعض استجابات الواجهات البرمجية. وعندما يحتاج إلى ملف مرة أخرى ويكون استخدام النسخة المخزّنة ما زال مسموحاً، يأخذ النسخة بدلاً من تنزيل الملف من جديد. فتصبح الزيارات اللاحقة أسرع، وقد يرى المستخدم العائد شيئاً مختلفاً عمّا يراه زائر لأول مرة.

مثال: يطلق متجر زر «Pay now» باللون الأخضر الساعة 10:00. ويفتح عميل زار المتجر أمس صفحة الدفع الساعة 11:00 فيرى الزر الأزرق القديم، لأن المتصفح أعاد استخدام ملف التنسيق المخزّن منذ أمس دون أن يسأل الخادم. الإصدار يعمل، لكنه ببساطة لم يصل إلى هذا العميل.

لماذا يهم

المحتوى القديم (Stale Content) سبب شائع لعبارة «يعمل عندي» بعد الإصدار: فالمختبِر تحقق بمتصفح نظيف، بينما احتفظ العملاء بملفات قديمة. وتتراوح الآثار بين شعار قديم وإخفاقات خطيرة: HTML جديد يعمل مع JavaScript قديم يرسل طلبات لم يعد الخادم يقبلها، أو صفحة حساب تبقى ظاهرة بعد تسجيل الخروج.

والتخزين المؤقت أمر يضبطه الفريق: فترويسات الاستجابة وإعدادات البناء وشيفرة عامل الخدمة هي التي تقرر ما يُخزَّن ولأي مدة، وإذا خلت الاستجابة من ترويسات التخزين المؤقت فقد يقدّر المتصفح عمرها بنفسه. والإعداد الخاطئ أو الغائب عيب كأي عيب آخر. وإذا اختبرت دائماً بذاكرة مؤقتة فارغة فلن ترى أبداً ما يراه معظم مستخدميك.

التخزين في الذاكرة وعلى القرص وعمّال الخدمة

في درس «كيف تعمل المتصفحات» رأيت Chrome يكتب (memory cache) أو (disk cache) على بعض صفوف لوحة Network. فالذاكرة المؤقتة في الذاكرة (memory cache) تحفظ ملفات استخدمتها الصفحة المفتوحة من قبل، وهي سريعة وقصيرة العمر ولا تبقى بعد إغلاق التبويب. أما الذاكرة المؤقتة على القرص (disk cache) فهي ذاكرة HTTP المؤقتة للمتصفح، أي ذاكرة خاصة (private cache) بتعبير RFC 9111: تبقى بعد إعادة التشغيل، وترويسات الاستجابة هي التي تحدد ما يجوز لها تخزينه ولأي مدة.

النسخة الحديثة (fresh)، كتلك التي ما زالت ضمن عمرها المحدد بـ Cache-Control: max-age، تُستخدم دون الاتصال بالخادم. أما النسخة المتقادمة (stale) فيُعاد التحقق منها عادةً: يرسل المتصفح وسم إصدار النسخة (ETag) في If-None-Match، أو تاريخها (Last-Modified) في If-Modified-Since، فيجيب الخادم بـ 304 Not Modified (احتفظ بالنسخة) أو بـ 200 مع نسخة جديدة. وتعني no-cache «خزّن، لكن أعد التحقق قبل كل استخدام»، وتعني no-store «لا تخزّن». ويشرح درس التخزين المؤقت في HTTP هذه الترويسات بالتفصيل.

عامل الخدمة (Service Worker) سكربت يسجّله الموقع في المتصفح. يقف بين الصفحة والشبكة، ويستطيع الإجابة عن الطلبات من مخزنه الخاص Cache Storage حتى لو كانت ذاكرة HTTP المؤقتة فارغة. وافتراضياً يُثبَّت الإصدار الجديد منه في الخلفية وينتظر حتى تُغلق كل التبويبات التي تستخدم الإصدار القديم، لذلك لا تكفي إعادة التحميل العادية لتبديل الإصدار.

يذهب الطلب إلى عامل الخدمة إن كان يتحكم في الصفحة؛ وإلا تُستخدم النسخة المخزّنة الحديثة دون سؤال الخادم، ويُعاد التحقق من النسخة المتقادمة، ولا يُنزَّل الملف إلا عند غياب أي نسخة. نموذج مفاهيمي
سؤال 1: هل يتحكم عامل خدمة في الصفحة؟تحقّق في Application > Service workers
  • الإجابة: نعم

    النتيجة: يجيب عامل الخدمةتختار شيفرته بين Cache Storage والشبكة، فقد يقدّم إصداراً قديماً(ServiceWorker)
  • الإجابة: لا

    سؤال 2: هل لدى المتصفح نسخة مخزّنة؟في الذاكرة المؤقتة في الذاكرة أو على القرص
    • الإجابة: لا

      النتيجة: لا نسخة (miss): يُنزَّل من الخادمثم يُخزَّن إن سمحت ترويسات الاستجابة بذلك200
    • الإجابة: نعم

      سؤال 3: هل ما زالت النسخة حديثة؟ضمن max-age وغير موسومة بـ no-cache
      • الإجابة: نعم

        النتيجة: إصابة (hit): تُستخدم دون سؤال الخادمتظهر بـ (memory cache) أو (disk cache)؛ ولا يصل إليها نشر جديد على العنوان نفسه
      • الإجابة: لا

        النتيجة: يُعاد التحقق منها مع الخادم304: تبقى النسخة؛ 200: تحل محلها نسخة جديدةIf-None-Match

نموذج مفاهيمي: تختلف المتصفحات في التفاصيل؛ فمثلاً يبحث Chrome في ذاكرته المؤقتة قصيرة العمر في الذاكرة حتى قبل عامل الخدمة. وإعادة التحميل القسرية تتجاوز النسخ المخزّنة وعامل الخدمة في تحميل واحد ولا تحذف شيئاً.

  • إعادة التحميل القسرية (Hard Reload) بـ Ctrl+Shift+R، أو Cmd+Shift+R على Mac: تطلب الصفحة وملفاتها من جديد وتتجاوز عامل الخدمة، في هذا التحميل وحده. ولا تحذف شيئاً، وقد تعيد إعادة التحميل العادية استخدام النسخ الحديثة.
  • «Empty Cache and Hard Reload» في Chrome (انقر بالزر الأيمن على زر إعادة التحميل وأدوات المطوّرين مفتوحة): تفرغ الذاكرة المؤقتة ثم تعيد التحميل قسرياً.
  • «Disable cache» في لوحة Network: لا ذاكرة HTTP مؤقتة ما دامت أدوات المطوّرين مفتوحة، لكن عامل الخدمة يستطيع أن يجيب رغم ذلك.
  • لوحة Application: يعرض قسم Service workers الإصدار النشط وأي إصدار منتظر، مع «Bypass for network» و«Unregister»؛ ويزيل «Clear site data» عامل الخدمة ومخزنه Cache Storage أيضاً.
  • النافذة الخاصة: ذاكرة مؤقتة فارغة ولا عامل خدمة، أي ما يراه زائر لأول مرة. أغلق النوافذ الخاصة الأخرى أولاً، فهي تتشارك جلسة واحدة وذاكرتها المؤقتة.

كسر الذاكرة المؤقتة: كيف يصل الإصدار إلى المتصفح

تريد الفرق شيئين معاً: ملفات تُنزَّل مرة واحدة ويُعاد استخدامها مدة طويلة، وإصدارات يتلقاها كل مستخدم فوراً. وكسر الذاكرة المؤقتة (Cache Busting) يحقق الأمرين: عندما يتغير الملف يتغير عنوانه. فأدوات البناء تضع بصمة (fingerprint)، أي تجزئة (hash) قصيرة محسوبة من محتوى الملف، في اسم الملف، فيصبح app.3f9c1e.js هو app.9b41d2.js؛ وتضيف بعض الفرق رقم الإصدار إلى سلسلة الاستعلام بدلاً من ذلك (app.js?v=2.15.0). ولم يخزّن المتصفح العنوان الجديد من قبل، فلا بد أن ينزّله.

ولا ينجح هذا إلا إذا كانت صفحة HTML التي تذكر أسماء الملفات حديثة هي نفسها. اقرأ الإعداد أدناه هكذا:

  • تُخدَم صفحة HTML بـ no-cache: فيُعاد التحقق منها في كل زيارة وتصل أسماء الملفات الجديدة.
  • يمكن تخزين الملفات ذات البصمة سنة كاملة، لأن أي تغيير يمنحها اسماً جديداً.
  • الملف الذي بلا بصمة، مثل logo.svg، يبقى قديماً بعد النشر مدةً تصل إلى قيمة max-age الخاصة به.
  • التبويب المفتوح قبل النشر ما زال يشغّل الشيفرة القديمة. فإذا طلب لاحقاً ملفاً حذفه النشر، مثل سكربت شاشة أخرى، يفشل الطلب بـ 404؛ لذلك تُبقي الفرق ملفات الإصدار السابق متاحة مدةً بعد النشر.
Request                      Cache-Control                 After a deploy
GET /checkout                no-cache                      revalidated: new HTML
GET /assets/app.9b41d2.js    max-age=31536000, immutable   new name: downloaded
GET /assets/app.51c7aa.css   max-age=31536000, immutable   new name: downloaded
GET /images/logo.svg         max-age=86400                 old for up to 1 day
GET /api/cart                no-store                      never stored

جرّبها: محلّل ترويسات التخزين المؤقت جرّبها: منشئ ترويسات HTTP

عيوب المحتوى القديم وكيف تتعرّف عليها

إخفاق المحتوى القديم يُظهر نسخة من صفحة أو ملف أو بيانات أقدم مما يحمله الخادم الآن. والعيب وراءه يكون غالباً في إعداد التخزين المؤقت لا في الميزة قيد الاختبار: بصمة مفقودة، أو max-age أطول مما يجب، أو صفحات HTML أو استجابات واجهات برمجية ما كان ينبغي تخزينها، أو عامل خدمة لا يُحدَّث أبداً. وانتبه إلى معنيَي «القِدم» هنا: النسخة التي وراء هذا الإخفاق تكون غالباً ما زالت حديثة (fresh) بحسب ترويساتها، فلا يسأل المتصفح الخادم عنها. أما «متقادمة» (stale) في RFC 9111 فتعني فقط أن عمر النسخة انتهى، والنسخة المتقادمة يُتحقق منها مع الخادم.

ولتثبت أن الذاكرة المؤقتة هي السبب، قارن: كرّر الخطوات في نافذة خاصة، واقرأ عمود Size حيث تحل (memory cache) أو (disk cache) أو (ServiceWorker) محل عدد البايتات، وقارن أسماء الملفات ورقم البناء بما نُشر فعلاً. ومن الأعراض المعتادة:

  • جزء من الإصدار مفقود لدى بعض المستخدمين: HTML جديد مع تنسيقات قديمة، أو ميزة تُظهرها إعادة التحميل القسرية ولا يراها زميلك.
  • بيانات قديمة: تغيّر سعر في لوحة الإدارة، لكن المتجر يعرض السعر القديم لأن استجابة الواجهة البرمجية خُزّنت في المتصفح أو في شبكة توصيل المحتوى (CDN)، وهي مجموعة ذاكرات مؤقتة مشتركة بين المستخدمين والموقع.
  • إصدارات مختلطة: JavaScript قديم يستدعي واجهة برمجية تغيّرت، فيفشل الحفظ بـ 400 أو بخطأ في وحدة التحكم.
  • موقع عالق على إصدار قديم، أو يعرض صفحة عدم الاتصال وأنت متصل: عامل خدمة يقدّم نسخه المخزّنة.
  • بيانات شخصية بعد تسجيل الخروج: الضغط على زر الرجوع يُظهر صفحة الحساب مرة أخرى.

مثال تطبيقي: اختبار إصدار كمستخدم عائد

يغيّر الإصدار 2.15.0 لأحد المتاجر زرَّ الدفع وتنسيقاته. وتحتفظ المختبِرة بملف تعريف (profile) في المتصفح يؤدي دور عميل عائد. قبل النشر تفتح صفحة الدفع فيه وتحفظ قائمة أسماء الملفات المحمّلة، وبعد النشر تقارن هذه الذاكرة المؤقتة الممتلئة بذاكرة فارغة:

  • افتح صفحة الدفع في ملف التعريف ذاك بتنقّل عادي و«Disable cache» غير مفعّلة، وسجّل ما تراه ورقم البناء.
  • قارن صفوف لوحة Network بالقائمة المحفوظة: هل يصل كل ملف تغيّر من الخادم باسم جديد، أم من (disk cache) بمحتوى قديم؟
  • في تبويب بقي مفتوحاً منذ ما قبل النشر، افتح شاشة لم تزرها بعد وأرسل نموذجاً.
  • إن كان للموقع عامل خدمة، فابحث عن إصدار منتظر في Application > Service workers؛ ثم أغلق كل تبويبات الموقع وأعد فتحه وتأكد أن الإصدار الجديد هو النشط.
  • افتح نافذة خاصة: يجب أن يحصل الزائر لأول مرة على الإصدار الجديد أيضاً.
Release 2.15.0 on staging, returning-customer profile, normal navigation

Name            Status  Size           Note
checkout        200     5.2 kB         new HTML, footer shows build 2.15.0
app.9b41d2.js   200     212 kB         new fingerprint
styles.css      200     (disk cache)   no fingerprint, max-age=604800
logo.svg        200     (disk cache)   not changed in 2.15.0: fine

النتيجة: الزر «Pay now» أزرق هنا (تنسيقات قديمة) وأخضر في نافذة خاصة.
العيب:   الملف styles.css بلا بصمة ويُخزَّن 7 أيام، فيرى العملاء العائدون
         صفحة الدفع في 2.15.0 بتنسيقات 2.14 حتى تنتهي صلاحيته.

جرّبها: محلّل ترويسات HTTP جرّبها: مقارنة النصوص (Diff)

أخطاء شائعة

  • اختبار الإصدار بذاكرة مؤقتة فارغة فقط (نافذة خاصة، أو إعادة تحميل قسرية، أو «Disable cache» مفعّلة دائماً)، فلا ترى أبداً ما يراه المستخدمون العائدون.
  • الرد على بلاغ محتوى قديم بعبارة «امسح الذاكرة المؤقتة». المستخدمون لا يمسحونها؛ أبلغ عن عيب التخزين المؤقت بدلاً من ذلك.
  • نسيان عامل الخدمة: تبدو الصفحة صحيحة بعد إعادة التحميل القسرية، ثم يقدّمها عامل الخدمة القديم في التحميل العادي التالي.
  • افتراض أن الصفحة تعرض البناء الجديد. تحقّق من رقم البناء أو بصمات الملفات قبل أن تختبر أي شيء آخر.
  • إغفال حالة الذاكرة المؤقتة في تقرير الخطأ: قيمة عمود Size، وأسماء الملفات وترويساتها، والبناء، وهل أعدت التحميل قسرياً.

من منظور ضمان الجودة

اطرح أسئلة التخزين المؤقت أثناء تحسين القصص (refinement) كلما مسّت قصةٌ الواجهة الأمامية: أي الملفات لها بصمة؟ وما ترويسة Cache-Control التي تحصل عليها صفحات HTML والملفات واستجابات الواجهات البرمجية؟ وكم تبقى الملفات القديمة متاحة؟ وكيف يُحدَّث عامل الخدمة ويطلب من المستخدمين إعادة التحميل؟

بعد كل نشر، اختبر أولاً بملف تعريف لمستخدم عائد، وأضف فحصاً واحداً يتجاوز كل ذاكرة مؤقتة، ثم قارن بينهما. وفي تقرير المحتوى القديم سجّل البناء المتوقع والبناء الظاهر، والعنوان المتأثر مع Cache-Control وقيمة عمود Size، والمتصفح وملف التعريف، وهل تُظهر النافذة الخاصة الإصدار الجديد. عندها يجد المطورون الملف ذا الإعداد الخاطئ بدلاً من التخمين.

تمرين عملي

توقّع ما يراه العميل العائد بعد الإصدار

المطلوب

يضيف الإصدار 3.4.0 سطر ضريبة القيمة المضافة إلى صفحة الفاتورة، ويغيّر لون العلامة التجارية، ويستبدل الشعار. وقد نُشر يوم الثلاثاء الساعة 10:00. فتح أحد العملاء الفاتورة مرة واحدة من قبل، يوم الاثنين الساعة 18:00، فخزّن المتصفح كل ملفاتها حينها، ويعود يوم الثلاثاء الساعة 11:00 بتنقّل عادي. لا يستخدم الموقع عامل خدمة.

باستخدام الترويسات أدناه، حدّد لكل طلب هل يستخدم المتصفح نسخته المخزّنة أم الخادم، وصِف ما يراه العميل، وسمِّ العيوب وكيف تؤكدها.

المادة
Release 3.4.0, live Tuesday 10:00. No service worker.

Request                         Cache-Control                 Note
GET /invoice/1001               no-cache                      ETag changed in 3.4.0
GET /static/invoice.81f2a0.js   max-age=31536000, immutable   new name in 3.4.0
GET /static/styles.css          max-age=604800                changed in 3.4.0
GET /static/logo.png            max-age=86400                 changed in 3.4.0
GET /api/invoices/1001          no-store                      VAT line data
الخطوات
  1. احسب حتى متى تبقى كل نسخة مخزّنة حديثة.
  2. حدّد لكل طلب: تنزيل، أو إعادة تحقق، أو نسخة مخزّنة، أو لا تخزين أبداً.
  3. صِف الصفحة التي يراها العميل.
  4. سمِّ العيوب وفحصاً يؤكدها.
إظهار الإجابة النموذجية
الإجابة النموذجية

الأجزاء الجديدة تصل: يُعاد التحقق من /invoice/1001 بسبب no-cache، ولأن قيمة ETag تغيّرت تصل صفحة HTML الجديدة مع سطر الضريبة؛ وinvoice.81f2a0.js عنوان لم يخزّنه المتصفح من قبل فيُنزَّل؛ واستجابة الواجهة البرمجية ذات no-store تأتي دائماً من الخادم.

الأجزاء القديمة تبقى: خُزّن styles.css يوم الاثنين الساعة 18:00 مع max-age=604800 (7 أيام)، فيبقى حديثاً حتى الاثنين التالي الساعة 18:00 ويُستخدم دون سؤال الخادم. أما logo.png فله max-age=86400 (يوم واحد) ويبقى حديثاً حتى الثلاثاء الساعة 18:00.

يرى العميل الفاتورة الجديدة مع سطر الضريبة، لكن باللون القديم والشعار القديم، وقد يظهر سطر الضريبة بلا تنسيقاته الجديدة. العيب: styles.css وlogo.png بلا بصمة، فيحصل المستخدمون العائدون على الصفحة الجديدة بتنسيقات الإصدار السابق مدةً تصل إلى 7 أيام، وبالشعار القديم مدةً تصل إلى يوم واحد. واختيار الإصلاح، كإضافة بصمة إلى الملفين، يعود إلى المطورين.

التأكيد: في ملف تعريف حمّل الفاتورة قبل الإصدار، افتحها بشكل عادي واقرأ عمود Size الذي يُظهر (disk cache) للملفين؛ ثم افتح الصفحة في نافذة خاصة حيث يظهر اللون والشعار الجديدان. هذا الفرق يُثبت أن السبب هو الذاكرة المؤقتة لا الإصدار.

أسئلة شائعة

ما الفرق بين إعادة التحميل القسرية ومسح الذاكرة المؤقتة؟

إعادة التحميل القسرية تتجاهل النسخ المخزّنة في تحميل واحد: يطلب المتصفح الصفحة وملفاتها من جديد ويتجاوز عامل الخدمة، لكنه يُبقي الذاكرة المؤقتة. أما مسح الملفات المخزّنة مؤقتاً فيحذف النسخ. ولا يزيل أيٌّ منهما عامل الخدمة؛ يزيله «Clear site data» في لوحة Application أو «Unregister».

إذا أصلح مسحُ الذاكرة المؤقتة المشكلة، فهل تبقى عيباً؟

نعم. فالعملاء لا يمسحون ذاكراتهم المؤقتة، فيستمرون في رؤية الإخفاق. والعيب في إعداد التخزين المؤقت، كبصمة مفقودة أو max-age أطول مما يجب. وتتوقف خطورته على ما يُفسده المحتوى القديم: الشعار القديم عيب ثانوي (Minor)، أما السعر القديم أو الدفع الفاشل فليس كذلك.

هل يوقف «Disable cache» في أدوات المطوّرين عامل الخدمة أيضاً؟

لا. فهو يعطّل ذاكرة HTTP المؤقتة ما دامت أدوات المطوّرين مفتوحة، لكن عامل الخدمة يستطيع أن يجيب من Cache Storage. في Chrome استخدم «Bypass for network» في Application > Service workers، أو اختبر في نافذة خاصة.

المراجع

  • RFC 9111: HTTP CachingIETF · 2022كيف تحدد Cache-Control وExpires وETag والمتحقِّقات ما إذا كان يمكن تخزين الاستجابة وإعادة استخدامها.
  • HTTP caching — MDNMDN Web Docs (Mozilla)التخزين المؤقت الخاص مقابل المشترك، والحداثة، والتحقق عبر ETag / Last-Modified، وأنماط Cache-Control الشائعة.
  • Service Worker API — MDNMDN Web Docs (Mozilla)عمّال الخدمة (Service Workers): سكربتات تقف بين تطبيق الويب والشبكة لاعتراض الطلبات وإدارة التخزين المؤقت وإبقاء التطبيق عاملاً دون اتصال.

تدرّب بأدوات متقن

تحليل ترويسات طلبات واستجابات HTTP الخام إلى جدول منظّم مع الشروحات.

الويب

تكوين ترويسات HTTP باستخدام إعدادات مسبقة للتخزين المؤقت وCORS والأمان والتفاوض على المحتوى.

الويب

للتعمّق أكثر

تقدّمك في هذا الموضوع

جزء من مسار تعلم ضمان الجودة