کشCDNسرعت سایتآموزش

کش سایت چیست و کش CDN چگونه کار می‌کند؟ راهنمای عملی

از cache hit و cache key تا Cache-Control و TTL؛ یاد می‌گیرید کش CDN را طوری تنظیم کنید که سایت سریع شود و صفحه‌های کاربر لاگین‌شده لو نرود.

nsin8 دقیقه مطالعه

کش کردن ارزان‌ترین بهینه‌سازی ممکن است: پاسخی که از لبه سرو می‌شود نه به سرور شما فشار می‌آورد، نه منتظر مسیر پرنوسان اینترنت بین‌الملل می‌ماند. اما بیشتر سایت‌هایی که CDN می‌گیرند ماه‌ها با نرخ hit پایین کار می‌کنند، چون کش را «روشن» کرده‌اند بدون اینکه بدانند لبه بر چه اساسی تصمیم می‌گیرد. اینجا همان تصمیم را باز می‌کنیم: کلید کش، هدرها، TTL و پاک‌سازی.

چرخه یک درخواست: Hit، Miss و Bypass

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

وضعیتیعنی چهمبدأ چه می‌بیند
HITنسخه معتبر در لبه بودهیچ درخواستی نمی‌رسد
MISSچیزی در لبه نبودیک درخواست کامل، پاسخ ذخیره می‌شود
EXPIREDنسخه بود ولی TTL تمام شدهیک درخواست شرطی، معمولاً پاسخ HTTP 304
BYPASSقانون یا هدر اجازه کش ندادههر بار درخواست کامل، هیچ چیز ذخیره نمی‌شود

تفاوت MISS و BYPASS مهم است: MISS یک بار رخ می‌دهد و بعد به HIT تبدیل می‌شود، ولی BYPASS یعنی عملاً کش ندارید. هر HIT هم‌زمان TTFB کاربر، بار مبدأ و ترافیک خروجی سرور را کم می‌کند؛ در nsin ترافیک در سه تعرفه جداگانه cached، proxied و direct حساب می‌شود، پس نرخ hit مستقیماً تعیین می‌کند بایت‌های شما در کدام ردیف بیفتند و روی هزینه پهنای باند اثر می‌گذارد.

کلید کش: لبه از کجا می‌فهمد دو درخواست «یکی» هستند؟

کلید کش (cache key) رشته‌ای است که لبه از روی درخواست می‌سازد و پاسخ را زیر آن ذخیره می‌کند: متد (فقط GET و HEAD کش می‌شوند)، اسکیم، هاست، مسیر و کوئری‌استرینگ. هر چیزی که در کلید نباشد ولی محتوای پاسخ را عوض کند، یک باگ بالقوه است.

کوئری‌استرینگ

?page=2 و ?page=3 دو صفحه متفاوت‌اند، پس کوئری‌استرینگ باید در کلید بماند. اما utm_source و gclid محتوا را عوض نمی‌کنند و اگر بمانند، یک صفحه واحد به ده‌ها نسخه تکه‌تکه می‌شود. اگر می‌خواهید نادیده‌شان بگیرید، حتماً از فهرست مجاز استفاده کنید؛ حذف کورکورانه همه پارامترها فاجعه است.

هدر Origin و کوکی‌ها

اگر پاسخی هدر Access-Control-Allow-Origin دارد و با Vary: Origin جواب می‌دهد، کلید هم باید بر اساس Origin تفکیک شود. وگرنه اولین پرکردن کش که بدون Origin انجام شده نسخه بدون CORS را ذخیره می‌کند و همان به مرورگری می‌رسد که از دامنه دیگری fetch کرده — خطای CORS مرموزی که با ریلود «درست می‌شود» و بعد برمی‌گردد. کوکی درخواست و Set-Cookie پاسخ هم به‌طور پیش‌فرض باید کش را دور بزنند؛ دلیلش پایین‌تر.

Cache-Control؛ زبان مشترک شما و لبه

مبدأ با هدر Cache-Control به لبه و مرورگر می‌گوید با پاسخ چه کنند. مهم‌ترین دستورها:

  • public / privateprivate یعنی فقط مرورگر خود کاربر حق نگه‌داشتن دارد، نه کش مشترک.
  • max-age=N — عمر پاسخ به ثانیه، برای همه کش‌ها از جمله مرورگر.
  • s-maxage=N — همان، ولی فقط برای کش مشترک و با اولویت بالاتر. ابزار شما برای «در لبه یک ساعت، در مرورگر ۶۰ ثانیه».
  • no-cache — برخلاف اسمش یعنی «ذخیره کن، ولی قبل از هر بار سرو اعتبارش را بپرس».
  • no-store — هیچ‌جا ذخیره نشود. فقط پنل کاربر، سبد خرید و احراز هویت.
  • must-revalidate — بعد از انقضا، سرو نسخه کهنه ممنوع است.
  • stale-while-revalidate=N — نسخه کهنه فوراً سرو می‌شود و به‌روزرسانی در پس‌زمینه انجام می‌گیرد. بهترین دوست LCP شما.
  • stale-if-error=N — اگر مبدأ خطا داد، نسخه کهنه سرو شود. nsin مستقل از این هدر هم می‌تواند وقتی مبدأ از دسترس خارج می‌شود صفحه‌های کش‌شده را سرو کند.
  • immutable — به مرورگر می‌گوید حتی با ریلود هم درخواست شرطی نفرست؛ فقط روی فایل هش‌دار.

هدر قدیمی Expires هر وقت Cache-Control شامل max-age یا s-maxage باشد نادیده گرفته می‌شود؛ روی Pragma هم اصلاً حساب نکنید.

اعتبارسنجی مجدد با ETag و Last-Modified

وقتی TTL تمام می‌شود لازم نیست کل فایل دوباره منتقل شود. لبه یک درخواست شرطی می‌فرستد: If-None-Match با ETag قبلی، یا If-Modified-Since با تاریخ Last-Modified. اگر محتوا عوض نشده باشد مبدأ یک HTTP 304 خالی برمی‌گرداند و همان نسخه با عمر تازه سرو می‌شود.

تله رایج: در مبدأهای چندسروره، ETag پیش‌فرض بعضی وب‌سرورها به inode فایل وابسته است و روی هر سرور فرق می‌کند، پس هیچ‌وقت 304 نمی‌گیرید. یا غیرفعالش کنید، یا مقداری بر پایه هش محتوا بدهید.

کش درست تنظیم‌شده بار مبدأ را حذف می‌کند؛ کش غلط تنظیم‌شده حریم خصوصی کاربران را.

TTL پیشنهادی بر اساس نوع محتوا

این جدول نقطه شروع است، نه قانون؛ بر اساس نرخ تغییر محتوای خودتان تنظیمش کنید.

نوع محتواTTL لبهTTL مرورگرنکته
فایل هش‌دار مثل app.9f2c1b.jsیک سالmax-age=31536000, immutableنام با هر بیلد عوض می‌شود، پس purge لازم نیست
تصویر، فونت، آیکونچند هفتهچند روزبا ETag ترکیب کنید
CSS و JS بدون هش در نامچند ساعتکوتاه + no-cacheراه‌حل واقعی، هش‌دار کردن نام است
HTML صفحه‌های عمومیچند دقیقه تا چند ساعتno-cacheحتماً با stale-while-revalidate
صفحه محصول و قیمتچند دقیقه0بعد از تغییر قیمت، purge هدفمند
خروجی GET یک API عمومیچند ثانیه تا چند دقیقه0با s-maxage از مرورگر جدا کنید
API کاربر، سبد خرید، پنلبدون کشprivate, no-storeاستثنا ندارد
sitemap.xml و robots.txtچند ساعتچند ساعتبرای خزنده‌ها همیشه در دسترس بماند

سه سطر اول بیشترین سود Core Web Vitals را می‌دهند؛ اگر LCP سایت‌تان هنوز بالای 2.5 ثانیه است، راهنمای Core Web Vitals قدم بعدی شماست.

خطر کش شدن صفحه کاربر لاگین‌شده

سناریو را دقیق تصور کنید: کاربر A وارد حساب می‌شود، مبدأ صفحه پروفایل او را همراه Set-Cookie برمی‌گرداند، و لبه — چون قانون «همه‌چیز را کش کن» فعال بوده — آن پاسخ را ذخیره می‌کند. از این لحظه هر بازدیدکننده‌ای که همان URL را باز کند، صفحه و در بدترین حالت نشست کاربر A را می‌گیرد. کلاسیک‌ترین حادثه امنیتی مربوط به کش همین است.

  1. روی هاستی که ورود کاربر دارد، تا وقتی قانون دور زدن بر اساس کوکی درخواست را نگذاشته‌اید، کش سراسری را فعال نکنید.
  2. پاسخ‌های دارای Set-Cookie یا کش نشوند، یا این هدر هنگام ذخیره حذف شود.
  3. کوکی‌های نشست را با نام دور بزنید. در وردپرس: wordpress_logged_in_*، comment_author_*، woocommerce_items_in_cart و کل مسیرهای /wp-admin و /wp-login.php.
  4. به Vary: Cookie تکیه نکنید؛ فنی درست است ولی هر کوکی متفاوت یک نسخه جدا می‌سازد و نرخ hit را صفر می‌کند.

پاک‌سازی کش و بی‌اعتبارسازی

بهترین purge، purgeای است که لازم نمی‌شود: اگر نام فایل‌های استاتیک هش محتوا داشته باشد، هر انتشار یک URL تازه می‌سازد. purge را برای چیزی نگه دارید که URL ثابت دارد — صفحه اصلی، صفحه محصول، فید، sitemap.

  • purge هدفمند بعد از انتشار مطلب یا تغییر قیمت: فقط همان URL و صفحه‌های فهرست مرتبط.
  • purge سراسری فقط بعد از تغییر قالب یا مهاجرت؛ پاک کردن کل کش در ساعت اوج، سیل درخواست را یک‌جا به مبدأ برمی‌گرداند.
  • ترتیب انتشار: اول مبدأ را به‌روزرسانی کنید، بعد purge بزنید. برعکسش یعنی لبه دوباره نسخه قدیمی را کش می‌کند.
  • TTL کوتاه به‌جای purge مکرر: اگر روزی چند بار purge دستی می‌زنید، TTL شما اشتباه است.

در پنل nsin کش فهرستی از قانون‌هاست؛ هر قانون دامنه خودش (هاست و مسیر)، TTL و رفتار مخصوص خودش را دارد و purge هم در دسترس است. لاگ‌های زنده هم نشان می‌دهند کدام مسیر hit می‌خورد و کدام bypass می‌شود. برای اتصال دامنه، ترتیب بی‌خطر کار در راه‌اندازی CDN بدون قطعی آمده و جزئیات فنی هدرها در مستندات.

پرسش‌های متداول

کش سایت چیست و با کش مرورگر چه فرقی دارد؟

کش مرورگر فقط برای همان یک کاربر روی همان دستگاه کار می‌کند، ولی کش CDN مشترک است: یک بار پر می‌شود و همان نسخه به همه بازدیدکننده‌های بعدی می‌رسد. به همین دلیل هم قدرتش بیشتر است و هم اشتباه در آن گران‌تر تمام می‌شود.

چرا با وجود فعال بودن CDN، درخواست‌ها همیشه MISS می‌شوند؟

معمولاً یکی از این چهار دلیل: مبدأ Cache-Control: no-store یا private می‌فرستد، پاسخ‌ها Set-Cookie دارند، کوئری‌استرینگ متغیر کلید کش را تکه‌تکه کرده، یا مسیر داخل دامنه هیچ قانون کشی نیست. اول هدرهای مبدأ را با curl -I ببینید.

آیا کش کردن صفحات به سئو آسیب می‌زند؟

برعکس. Googlebot هم مثل کاربر از لبه پاسخ می‌گیرد و پاسخ سریع‌تر یعنی بودجه خزش بهتر. فقط TTL صفحه‌های پرتغییر را منطقی نگه دارید تا نسخه کهنه ایندکس نشود.

بعد از تغییر سایت چقدر طول می‌کشد کش پاک شود؟

با purge بلافاصله؛ بدون purge تا پایان TTL همان قانون. اما کش مرورگر کاربران با purge پاک نمی‌شود و تا انقضای max-age می‌ماند — دلیل دیگری برای اینکه max-age فایل‌های بدون هش را بلند نگذارید.

کش را با یک قانون ساده شروع کنید: TTL بلند برای مسیرهای استاتیک، TTL کوتاه به‌همراه stale-while-revalidate برای HTML، و دور زدن صریح برای هر مسیری که کاربر لاگین‌شده دارد. بعد یکی دو روز لاگ‌ها را ببینید و TTLها را تنظیم کنید. هر دامنه جدید در پنل nsin هفت روز نسخه کامل Enterprise رایگان دارد، که برای همین آزمون و خطای اولیه کافی است.