آموزش کار با Cache-Control

15 اردیبهشت 1399
cashe-control

آموزش کار با Cache-Control: مفاهیم اولیه

Browser caching از مفاهیم پایه ای دنیای وب است که هر توسعه دهنده ای با آن آشنا است. اگر به تازگی وارد حوزه ی وب شده اید جای نگرانی نیست، آن را به صورت خلاصه برایتان توضیح می دهم اما قبل از آن باید با مفهوم حافظه ی کش (cache) آشنا شویم.

cache حافظه ای است که فایل هایی را به صورت موقت در خود نگه می دارد تا هر بار نیاز به دانلود آن ها نباشد (چه در نرم افزار های پیام رسان مانند تلگرام و چه در مرورگرها) اما زمانی که بحث به حوزه ی وب می رسد، کش اهمیت دو چندانی پیدا کرده و کمک بزرگی محسوب می شود.

بسیاری از فایل های وب سایت ما برای ماه ها یا حتی سال ها بدون تغییر خواهند بود ولی هر بار که کاربر بخواهد از سایت ما بازدید کند، باید دوباره همان فایل های تکراری را دانلود کند. Browser caching به مرورگرها این قابلیت را می دهد که فایل های استاتیک و ثابت (مانند جاوا اسکریپت، CSS یا حتی برخی از تصاویر و غیره) را در خود ذخیره کنند تا به جای دانلود دوباره ی آن ها، از همان فایل های محلی (قبلا دانلود شده) استفاده کند. این کار علاوه بر صرفه جویی در ترافیک مصرفی سرور و اینترنت کاربران، سرعت بالا آمدن سایت ما را نیز دو چندان می کند. حتما شما هم متوجه شده اید که بازدید از یک سایت برای بار اول بیشتر از دفعات بعدی طول می کشد. دلیل آن استفاده از همین قابلیت Browser caching می باشد.

برای استفاده از این قابلیت دو مرحله ی ساده را در پیش دارید:

  • تغییر header درخواست های منابع سایت (اضافه کردن cache به آن ها)
  • استفاده از یک منطق و استراتژی قابل قبول

برای تغییر header ها می توانیم از فایلی به نام htaccess. استفاده کنیم. این فایل در تمامی سرورها موجود است (حتی اگر موجود نبود خودتان می توانید فایلی به این نام را بسازید). روش ها و متدهای مختلفی برای پیاده سازی browser caching وجود دارد اما من می خواهم در این مقاله روی روش cache-control تمرکز کنم که یکی از روش های محبوب تر انجام این کار است.

Cache-Control چیست؟

Cache-Control یکی از header های پروتکل http است که زمان و نحوه ی کش شدن را برای یک فایل مشخص می کند. مثالی از این روش، کد زیر است:

Cache-Control: max-age=2592000, public

همانطور که می دانید، زمانی که فایلی توسط مرورگر ارسال می شود HTTP header های آن فایل نیز به همراه فایل دریافت می شوند. زمانی که مرورگر header مخصوص cache را به همراه فایل می بینید، طبق دستور العمل آن عمل خواهد کرد. به طور مثال اگر کاربری به وب سایت شما بیاید باید لوگوی سایت شما را هم به عنوان بخشی از صفحه دانلود کند (هیچ چیز بدون دانلود شدن نمایش داده نمی شود). اگر این لوگو را به همراه header های کش ارسال کرده باشید (مثلا 5 دقیقه زمان کش تعیین کنید)، تا 5 دقیقه ی آینده این لوگو در حافظه ی کش مرورگر ذخیره خواهد شد. بنابراین تا 5 دقیقه ی بعد، هر بار که کاربر وارد سایت شما شود، لوگو را از سرور شما دانلود نمی کند بلکه از حافظه ی کش خودش برمی دارد.

خصوصیت Max-Age و دامنه ی کش

خصوصیت max-age در ساختار Cache-control تعیین کننده ی زمان کش شدن یک فایل است. مثلا به کد زیر نگاه کنید:

max-age=2592000

max-age همیشه در واحد ثانیه محاسبه می شود بنابراین 2592000 ثانیه حدودا یک ماه است. هر فایلی که این خصوصیت را به همراه header کش داشته باشد برای یک ماه در حافظه ی کش مرورگر قرار خواهد گرفت. معمولا یکی از مقادیر زیر برای max-age در نظر گرفته می شود:

  • یک دقیقه: max-age=60
  • یک ساعت: max-age=3600
  • یک روز: max-age=86400
  • یک هفته: max-age=604800
  • یک ماه: max-age=2628000
  • یک سال: max-age=31536000

این تنها یکی از خصوصیات موجود در cache-control است و هنوز باید با خصوصیات بیشتری آشنا شویم. خصوصیت بعدی public (به معنی «عمومی») است. در واقع سه خصوصیت اصلی برای دامنه ی کش وجود دارد:

  • public
  • private
  • no-store

خصوصیت اول که public است می گوید هر فردی در هر سطحی می تواند این فایل را کش کند. در documentation اصلی آمده است که:

The "public" response directive indicates that any cache MAY store the response, even if the response would normally be non-cacheable or cacheable only within a private cache.

این تعریف بیش از حد فنی است. به زبان خودمان اگر فایل های مورد نظر شما فایل های حساس و امنیتی نیستند و فقط به دلیل بالا بردن سرعت سایت باید کش بشوند، از دستور public استفاده کنید.

خصوصیت دوم private است که به معنی «خصوصی» می باشد. اگر دقت کرده باشید در بعضی از سایت ها (مثل توییتر و اینستاگرام) کاربران می توانند صفحه ی خود را به حالت private در بیاورند تا محتوای آن را از دیگران مخفی کنند. با این حساب خود فرد زمانی که به پروفایل خودش می رود یک نوع محتوا را می بیند و زمانی که کاربران دیگر به صفحه اش می روند نوع دیگری از محتوا را می بینند. این یک مفهوم کلی است و ربطی به توییتر و غیره ندارد. هر جایی که محتوای بارگذاری شده به شخص کاربر وابسته است و با تغییر کاربر، تغییر می کند نمونه ای از private است. توجه داشته باشید که در این حالت اطلاعات ما حساس نیستند بلکه مخصوص یک نفر هستند.

documentation رسمی برای private می گوید:

The "private" response directive indicates that the response message is intended for a single user and MUST NOT be stored by a shared cache. A private cache MAY store the response and reuse it for later requests, even if the response would normally be non-cacheable.

به زبان ساده تر کش شدن فایل ها در این حالت به صورت عمومی نیست و مثلا اگر من به توییتر بروم و صفحه ای را refresh کنم، فایل هایی برای من کش می شوند که شاید برای شما کش نوشند و حتی قابل دیدن نباشند.

خصوصیت آخر نیز no-store است که documentation رسمی آن می گوید:

The "no-store" response directive indicates that a cache MUST NOT store any part of either the immediate request or response. This directive applies to both private and shared caches.

اگر از این خصوصیت استفاده نمایید، یعنی هیچ کدام از فایل های مشخص شده نباشد به هیچ عنوان کش بشوند. البته توجه داشته باشید که استفاده از no-store فایل های شما را ایمن نمی کند بلکه از کش شدنشان جلوگیری می کند.

کش کردن فایل ها: از کجا شروع کنم؟

حالا بحث جدیدی باز می شود: چه فایل هایی را کش کنیم و چه مدت زمانی را برایشان تعیین کنیم؟ من قانونی کلی را برایتان توضیح می دهم که معمولا توسط متخصصین سئو پیشنهاد می شود اما بنا بر نیاز خود باید آن را تغییر بدهید:

  • تصاویر (فایل های jpg یا png یا gif و غیره) تقریبا هیچ وقت تغییر نمی کنند. بنابراین می توانیم آن ها را در حدود یک سال کش کنیم.
  • مدت کش فایل های CSS بستگی به شما دارد. اگر مرتبا استایل های سایت خود را تغییر می دهید بهتر است مدت طولانی را برای آن ها انتخاب نکنید (مثلا یک هفته مناسب است) اما اگر استایل های خود را تغییر نمی دهید می توانید تا یک ماه هم آن ها را کش کنید.
  • تعیین مدت کش فایل های جاوا اسکریپت نیز بستگی به شما دارد. اگر مکررا آن ها را تغییر می دهید در حد 1 هفته مناسب است اما تا یک ماه نیز توصیه می شود.
  • فایل های ico یا همان favicon نیز معمولا تغییری نمی کنند بنابراین آن ها را روی 1 سال می گذاریم.

خطر browser caching

browser caching که به شکل منطقی پیاده سازی نشده باشد، خطر بزرگی محسوب می شود. فرض کنید یک فایل CSS را تا سه ماه کش کرده باشید و در همان ماه اول بخواهید استایل های وب سایت خود را تغییر دهید. اگر این کار را انجام بدهید، مرورگر باز هم از نسخه ی کش شده در حافظه ی خودش استفاده می کند و فایل جدید شما که روی سرور آپلود شده است را نادیده می گیرد! بدین ترتیب تمام استایل های سایت شما در مرورگر کاربر به هم می ریزد. بنابراین سعی کنید زمان های بسیار طولانی را برای فایل های خود تعیین نکنید.

سوال: به هر حال برخی اوقات نیاز به تغییر ناگهانی داریم. در این مواقع غیر منتظره چه کار باید کرد؟

پاسخ: شما می توانید از روشی به نام URL fingerprinting استفاده نمایید.

مبنای کش کردن فایل ها برای مرورگرها، نام فایل است. بنابراین با تغییر نام فایل می توانیم از استفاده از نسخه ی کش شده جلوگیری کنیم. به طور مثال اگر فایل شما main.css نام دارد، بعد از اعمال تغییرات نام آن را main_1.css بگذارید. با این کار مرورگر می بینید که چنین فایلی در کش خودش موجود نیست بنابراین مجبور می شود آن را دوباره از سرور دانلود کند.

چطور از Cache-Control استفاده کنم؟

همانطور که قبلا هم گفتم برای استفاده از قابلیت cache باید header های مربوط به آن را همراه فایل ها ارسال کنیم. راحت ترین روش انجام آن هم استفاده از فایل htaccess. است بنابراین نحوه ی کار با آن را برایتان توضیح می دهم.

Header set Cache-Control "max-age=2628000, public"

مشکل کد بالا این است که اگر آن را به htaccess. اضافه کنید، تمام فایل های سایت را کش می کند ولی ما می خواهیم برای هر نوع فایلی از کش خاصی استفاده کنیم بنابراین می توان گفت:

# One month for most static assets
<filesMatch ".(css|jpg|jpeg|png|gif|js|ico)$">
Header set Cache-Control "max-age=2628000, public"
</filesMatch>

کد بالا می گوید اگر فایل مورد نظر یکی از فایل های ذکر شده بود (CSS یا jpg یا jpeg یا png یا...) آنگاه header زیر را به آن اطلاق کن که همان header مربوط به cache است. خطی که با علامت # شروع شده است، کامنت است و کار خاصی نمی کند. در فایل htaccess. علامت # کامنت ساز است.

مثلا فرض کنید که بخواهیم فایل های تصویری یک سال و فایل های CSS و جاوا اسکریپتی یک ماه کش بشوند. برای این کار می گوییم:

# One year for image files
<filesMatch ".(jpg|jpeg|png|gif|ico)$">
Header set Cache-Control "max-age=31536000, public"
</filesMatch>
# One month for css and js
<filesMatch ".(css|js)$">
Header set Cache-Control "max-age=2628000, public"
</filesMatch>

وب سرورهای مختلف

اگر وب سرور شما آپاچی (Apache) باشد بهتر است از طریق فایل http.conf اقدام کنید چرا که سریع تر است. تنها باید این فایل را پیدا کرده و کدهای خود را به شکل کدهای بالا نوشته و درون آن قرار دهید. البته بسیاری از شرکت های هاستینگ به شما اجازه ی دسترسی به فایل http.conf را نمی دهند که در این صورت می توانید کدهایتان را در همان فایل htaccess قرار دهید.

اگر وب سرور شما NGINX می باشد داستان کمی متفاوت خواهد بود و باید از ساختار زیر استفاده کنید:

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 365d;
}

در این ساختار به جای تعیین max-age می گوییم expires (به معنی «منقضی می شود»). مثلا کد بالا گفته است که کش در 365 روز آینده منقضی می شود. در واقع ابتدا کلید location را دارید که پس از آن مسیر خاصی مشخص می شود. من ~* را گذاشته ام که یعنی در تمام مسیرها (فرقی ندارد چه پوشه ای). سپس نوع فایل ها را ذکر می کند که در کد بالا می بینید. نهایتا با دستور expires کار را تمام می کنید.

مثال دیگری که از دستور public نیز استفاده می کند بدین شکل است:

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

البته تمام این مثال ها حالت های پیچیده تری نیز دارند. به طور مثال برای NGINX به وب سایت رسمی آن ها مراجعه کنید.

در نهایت اگر از Litespeed استفاده می کنید احتمال دارد به صفحه ی تنظیمات آن دسترسی داشته باشید. اگر اینطور نبود می توانید مانند Apache از فایل htaccess. استفاده نمایید.

آیا راه های دیگری برای تنظیم header وجود دارد؟

قطعا! همانطور که گفتم، هدف اصلی ما اضافه کردن header است بنابراین می توانیم از هر روشی برای اضافه کردن آن استفاده کنیم. به طور مثال اگر از PHP استفاده می کنید می توانید با تابع header این کار را انجام بدهید. برای اینکه تابع header کار کند باید حتما آن را در اولین خط کد خود قرار دهید و هیچ اسپیس یا کد دیگری قبل از آن نداشته باشید (همانطور که در وب سایت رسمی PHP توضیح داده شده است). احتمالا دستور معروف زیر را در PHP دیده اید:

header('Location: http://www.example.com/');

بسیاری از توسعه دهندگان PHP از header بالا برای redirect کردن کاربران استفاده می کنند. حالا می توانیم از همین تابع برای ارسال cache header استفاده نماییم:

header('Cache-Control: max-age=84600');

به همین سادگی. البته توابع پیچیده تری نیز برای کار PHP نوشته شده اند که یک نمونه ی آن را در کد زیر می بینید:

<?php
//get the last-modified-date of this very file
$lastModified=filemtime(__FILE__);
//get a unique hash of this file (etag)
$etagFile = md5_file(__FILE__);
//get the HTTP_IF_MODIFIED_SINCE header if set
$ifModifiedSince=(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false);
//get the HTTP_IF_NONE_MATCH header if set (etag: unique file hash)
$etagHeader=(isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : false);

//set last-modified header
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $lastModified)." GMT");
//set etag-header
header("Etag: $etagFile");
//make sure caching is turned on
header('Cache-Control: public');

//check if page has changed. If not, send 304 and exit
if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])==$lastModified || $etagHeader == $etagFile)
{
       header("HTTP/1.1 304 Not Modified");
       exit;
}

//your normal code
echo "This page was last modified: ".date("d.m.Y H:i:s",time());

?>

این کد فایل مورد نظر شما را چک می کند تا ببیند از آخرین باری که نمایش داده شده است، تغییری کرده است یا خیر. اگر تغییری داشتیم یک هدر 304 not modified ارسال می کند و در غیر این صورت همان محتوای همیشگی را نمایش می دهد.

یکی از مقالات بسیار جامع در این زمینه را در این صفحه پیدا خواهید کرد. در صورتی که به این مبحث علاقه دارید، خواندن آن را به شما پیشنهاد می کنم.


منبع: سایت Varvy

نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.