المقدمة

هل سئلت نفسك قبل كده ليه موقع معين أسرع من حيث التحميل مقارنة بمواقع أخرى مشابهة ؟الإجابة علي السؤال دا هو الـ HTTP Cache أو التخزين المؤقت للبيانات من خلالـ تخزين بيانات وملفات الموقع محليًا (داخل جهاز المستخدم سواء موبايل او لابتوب او تابلت الخ..) في المقالـ دا هناقش ازاي الـ HTTP Cache  بيخزن ملفات الموقع و كيف نتحكم فيه .


تعريف الـ  HTTP Cache

الـ  HTTP Cache  هو تقنية تُستخدم لتسريع تحميل صفحات الويب عن طريق تخزين ملفات الموقع محليًا على جهاز المستخدم، عند زيارة موقع ويب لأول مرة، يقوم الـ browser بتنزيل الملفات المطلوبة زي الصور، الأكواد (HTML، CSS، JavaScript)، والخطوط. ويتم تخزين الملفات بحيث لا يحتاج الـ browser إلى إعادة تنزيلها في كل مرة بتزور فيها نفس الموقع.


كيفية عمل الـ HTTP Cache

أي request بيتم إرساله الى الـ server بيمر في الأول علي الكاش وبيقوم الـ browser بفحص الـ resource إلى راجع في ال response هل موجود عنده في الـ cache ولو موجود بيرجعة بدون ما يضطر يعمل request جديد ل الـ server ولو مش موجود بيحصل ما يسمى بالـ Cache miss وهنا يبدأ الـ browser يبعت request ل الـ server علشان يجيب الـ resource اللي محتاجه.

وبعد ما بيستلمه من ال browser بيبدأ يخزنه  في الـ Cache Database علي جهاز المستخدم ويبدأ الـ browser يعرضه للمستخدم في المرات القادمه اللي بيبعت فيها المستخدم يطلب نفس الـ   resource من الكاش بدون ما يضطر يبعت  request ل الـ server مرة كمان، وهنا بيجي سؤالـ مهم هو الـ browser بيعرف منين إنه الداتا اللي متخزنة في الكاش updated وأنه الـ cache الي عنده هي اخر نسخة موجودة على الـ server ؟ الاجابة علي السؤال دا تتلخص فيما يسمى  Cache Revalidation


 Cache Revalidation

 Cache Revalidation
Cache Revalidation

بعد أول request ال browser بيعملة  الـ server بيرجع مع الـ response header حاجة اسمها الـ E-Tag وهو عبارة عن string بيمثل Hash  ل الـ resource الي راجع في الـ response وبيتغير في كل مرة بيحصل تعديل على الـ resource دا في الـ remote server.

وبيتخزن الـ E-tag مع الـ resource في الـ cache DB و بيبدأ الـ browser يرسله في كل مرة بيحصل request جديد لنفس الـ resource ويبدأ الـ server يقارن بين الـ E-Tag الي بعته الـ browser والـ E-Tag الي موجود عنده ولو لقي اختلاف بينهم بيبعت النسخة المحدثة من الـ resource ولو لقيهم متطابقين بيرجع statusCode: 304 ومعناها انه بيقول ل الـ browser استخدم الـ Cached version الي عندك من الـ resource الي كنت طالبه .

💡
في بعض الحالات الـ E-Tag مش بيرجع مع الـ Response Header وهنا بيتم استخدام الـ Last-Modified Header وده عبارة عن تاريخ لآخر مرة حصل تعديل على الـ Resource وبشتغل كـ Fallback في حالة عدم وجود الـ E-Tag

كيف اتحكم في الـ Cache

ممكن نتحكم في الـ cache عن طريق الـ  request headers و response headers

اولاً: الـ عن طريق الـ Request headers ودا في أغلب الأحيان بيتم التحكم فيه من خلالـ الـ browser عن طريق انه بيرسل الـ الـ E-Tag في الـ header الي اسمه If-None-Match  (علشان الـ server بعد كده يقارنه بالـ E-Tag الموجود عنده) وبيرسل كمان تاريخ آخر تعديل تم على الـ resource في الـ header الي اسمه If-Modified-Since  وممكن نتحكم اكتر من خلالـ استخدام الـ fetch api مع اضافة الـ cache options .

ثانياً عن طريق الـ الـ Response headers ودا بيكون من خلالـ عمل configuration ل الـ server وبنقدر نتحكم فيه عن طريق Response header اسمها Cache-Control  ومن خلالها الـ Browser بياخد معلومات من الـ server عن مدة صلاحية الـ Cache وامتة يعمله validation وامتة لأ وهل يتخزن في الـ  intermediate caches (زي الـ CDNs والـ Proxy Servers) أو ﻷ، و بيكون ليها اكثر من قيمة:

  • الـ no-store:  ودي بتقول للسرفر متخزنش كاش في الـ browser او في الـ intermediate caches.
  • الـ no-ache: ودي بتقول للسرفر اعمل Cache validation قبل ما تستخدم الـ Cache علشان نتأكد انه الـ Cache الي هيُستخدم هو اخر نسخة موجودة في الـ server .
  • الـ private: ودي بتقول ل الـ browser انه البيانات دي خاصة بالمستخدم فاعملها تخزين عادي لكن الـ intermediate caches  زي الـ   CDNs متخزنش البيانات دي . 
  • الـ public: ودي بتقول ل الـ browser انه البيانات دي ممكن تتخزن عادي في الـ browser cache وفي الـ intermediate caches .
  • الـ max-age: ودي بتاخد قيمة بالثواني بتمثل المدة الي الـ resource دا بيكون فيها fresh ويمكن استخدامه مباشراً بدون عمل cache validation، بعد المدة دي ما تخلص الـ browser بيروح يعمل cache validation مرة تانية .
  • الـ Stale-while-revalidate: ودي بتاخد قيمة بالثواني بتعبر عن المدة الـ browser ممكن يستخدم فيها الـ  stale cache لحين حدوث الـ cache validation في الـ background،  علي سبيل المثال  Cache-Control:max-age=900، stale-while-revalidate=3600معناها انه بقول ل الـ browser لو طلبت الـ resource من 15ل60 دقيقة استخدم الـ stale cache لحين عمل الـ Cache Validation في الـ background .
  • الـ s-maxage: ودي بتاخد قيمة بالثواني بتعبر عن مدة ال freshness ل ال resource علي ال intermediate cache .
  • الـ stale-if-error: ودي بتقول ل ال browser يستخدم ال stale cache في حالة حدوث error زي مثلا ال response  مرجع 500,502,503,504 ك statusCode .
  • الـ no-transform : هنا ال server بيبلغ ال intermediate cache ميعملش اي تعديل الـ resource لانه احيانا بيحصل تعديل زي مثلا تصغير حجم الصورة وهكذا .
  • الـ must-revalidate : ودي بتبلغ ال server وال intermediate cache انه يعمل validation بعد ما المدة المحددة للكاش تخلص ودا لانه ال Http بيسمح للكاش انه يستخدم في بعض الحالات زي انه مفيش network أو حصل وانه الـ server رجع 503 timeout .
  • الـ proxy-revalidate : ودي نفس الـ must-revalidate لانها موجهه ل ال intermediate cache فقط .
  • الـ immutable: دي بتقول ل ال browser متعملش validation حتى لو حصل reload ل الـ browser .

دمج عناصر الـ Cache Control

عمليا يُمكن الدمج بين العناصر دي مع بعض زي مثلا Cache-Control: private،max-age=120 دي معناها انه ال server بيبلغ الـ browser انه يخزن البيانات دي لمدة دقيقتين قبل ما يرجع يعمل cache validation مرة تانيه وفي نفس الوقت بيبلغ الـ intermediate caches متخزنش البيانات دي لانه خاصة بالمستخدم (private) .

 مثال عملي: 

ازاي عمليًا نضيف الـ Cache Configration في السيرفر الخاص بينا الاجابة بتكون حسب كل  Server و معانا مثالين هنا  الأول وهو علي الـ Apache2 Server والثاني على الـ Nginx Server 

Apache2

<filesMatch ".(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">
Header set Cache-Control "max-age=84600، public"
</filesMatch>

Nginx

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 365d;
add_header Cache-Control "public";
}

كيف نختار قيم الـ Cache في الـ Server

السؤال دا بيعتمد على نوع الـ resource إلى الـ Browser بينادي عليه وهنا في نوعين:

  1. الأول وهو الـ versioned resouce على سبيل المثال main-12345.js.

الـ versioned resource دا بشوفه كتير في الـ Single page applications ودا لانها بستخدم bundlers زي WepPack مثلا فبنلاقي الـ WepPack بتغير في اسامي الفايلات او بتعملها versioning وبتحولها من unversioned resoucre زي main.js الي version resource زي main-12345.js من خلال إضافة Hash بيتغير في كل مرة بيحصل build ل الـ application.

وفي الحالات دي بنعمل set ل الـ Cache لأطول فترة ممكنة (Cache-Control: max-age=31536000) ودا لانه مع كل مرة بيحصل build بيحصل versioning لاسامي الفايلان وبالتالي الـ browser بيروح يجيب النسخة الجديدة من الـ resource ويخزنها عنده مره ثانيه .

  1. الثاني وهو الـ unversioned resource علي سبيل المثال main.js.

ودا الي بنحتاج نلقي نظرة عليه بعناية من خلالـ الـ flow chart  القادم .

Versioned Resources

Heuristic Expiration Time

في بعض الاوقات بيكون الـ server سايب مسألة الكاش ل الـ Browser ومش بيحدد مدة الكاش تكون قد ايه، في الحالة دي الـ Browser هو الي بيحدد مدة الكاش وبيكون قيمة محسوبة من تاريخ أخر تعديل تم على resource  وبتكون حوالي الـ 10% من الـ Last Modified Header .

يعني لو عندي فايل HTML آخر تعديل عليه كان من 10 ساعات والسيرفر مش محدد مدة الكاش للفايل ده قد ايه هيقوم Browser محددها ساعة واحدة (10% * 10 = 1) ولكن هنا بيكون في شرط وأنه الـ Status Code الي راجع مع الـ response لازم تكون cacheable  أمثلة على الـ Cacheable StatusCode مثلا (200 ، 203، 204، 206، 300، 301، 404، 405، 410، 414، 501) .

💡
لو عدى أكثر من 24 ساعة على ال Heuristic cache بيبدأ المتصفح يرجع Statuscode 113 علشان يعرف المستخدم إنه بقاله 24 ساعة بيستخدم ال Heuristic cache و إن المحتوي ممكن يكون Stale

في الختام

في المقال دا ناقشنا ال HTTP Cache فهمنا ازاي ال browser بيتعامل مع ال cache وعرفنا ازاي نتحكم فيه ونديرة  و الي اللقاء في مقال قادم ودمتم سالمين .