چه کسی به این هدر گوش میدهد
یک پاسخ از چند کش عبور میکند: مرورگر بازدیدکننده (خصوصی — به یک نفر سرو میکند)، edge مربوط به CDN (مشترک — به همه سرو میکند)، و گاهی یک proxy در میانه. Cache-Control روشی است که مبدأ با همهی آنها همزمان حرف میزند، و به همین دلیل است که directiveهایی وجود دارند تا هرکدام را جداگانه خطاب کنند.
همین تمایز، بقیه چیزها را تعیین میکند. یک داشبورد کاربر واردشده ممکن است در مرورگر همان کاربر قابل کش باشد اما هرگز نباید روی edge کش شود، جایی که کاربر دیگری میتواند آن را دریافت کند. یک صفحهی معرفی عمومی برعکس است: روی edge محکم کش شود، در مرورگر کوتاه. اگر اول تصمیم بگیرید «خصوصی یا مشترک»، بقیهی انتخابها بدیهی میشوند.
directiveهایی که واقعاً اهمیت دارند
max-age=N — تا N ثانیه بدون پرسش دوباره قابل استفاده است. دستهی اصلی تنظیم.
s-maxage=N — همان، اما فقط برای کشهای مشترک (یعنی CDN). وقتی حاضر باشد، edge از آن پیروی میکند و max-age را نادیده میگیرد، پس میتوانید چیزی را یک ساعت روی edge نگه دارید در حالی که مرورگرها یک دقیقه نگهش میدارند.
public / private — public یعنی هر کشی میتواند ذخیرهاش کند؛ private ذخیرهسازی را به مرورگر همان کاربر محدود میکند. هر چیزی که مخصوص یک کاربر است باید private باشد.
no-cache — ذخیرهاش کن، اما پیش از هر استفادهی دوباره با مبدأ اعتبارسنجی کن. وقتی چیزی عوض نشده ارزان است: سرور میتواند بدون بدنه پاسخ 304 Not Modified بدهد.
no-store — هرگز آن را در هیچ کشی، نه در حافظه و نه روی دیسک، ننویس. این را برای پاسخهای واقعاً حساس نگه دارید؛ معنایش «لطفاً تازه باش» نیست، معنایش «هیچ نسخهای نگه ندار» است.
immutable — بدنهی این URL هرگز عوض نخواهد شد، پس حتی هنگام بارگذاری دوباره اعتبارسنجی نکن. تنها برای نامفایلهای نسخهدار راست است.
stale-while-revalidate=N — پس از انقضا، تا N ثانیه همان نسخهی کهنه را سرو کن در حالی که نسخهی تازه در پسزمینه گرفته میشود. بازدیدکننده هرگز منتظر دریافت دوباره نمیماند.
سه نسخهی آماده که بیشتر سایتها را پوشش میدهند
assetهای استاتیک نسخهدار — نام فایل هر بار که محتوا عوض میشود تغییر میکند، پس کش کردن آن URL برای همیشه امن است. این بزرگترین و امنترین بردِ کش موجود است.
صفحات HTML — آدرس ثابت میماند در حالی که محتوا عوض میشود، پس به عمر کوتاه نیاز دارد. یک max-age کوتاه بههمراه stale-while-revalidate سرعت را میدهد بدون آنکه صفحهی دیروز سرو شود.
پاسخهای خصوصی یا شخصیسازیشده — داشبوردها، سبد خرید، هر چیزی پشت ورود به حساب. آن را از کشهای مشترک بیرون نگه دارید؛ اگر برای شما امن است، مرورگر میتواند کوتاه نگهش دارد.
نقطههای شروع آماده — عددها را با ریتم انتشار خود تنظیم کنید
# versioned assets: /js/app.a1b2c3.js
Cache-Control: public, max-age=31536000, immutable
# HTML pages (short at the browser, longer at the edge, no waiting on refresh)
Cache-Control: public, max-age=60, s-maxage=600, stale-while-revalidate=86400
# per-user pages: never at the edge
Cache-Control: private, no-store
# an API response that changes often but can lag a little
Cache-Control: public, max-age=0, s-maxage=30, stale-while-revalidate=60
no-cache در برابر no-store: اشتباهی که ارزش پرهیز دارد
این دو مثل مترادف خوانده میشوند اما اصلاً شبیه هم رفتار نمیکنند. no-cache ذخیرهسازی را مجاز میکند اما پیش از استفادهی دوباره اعتبارسنجی میخواهد — نسخه سر جایش میماند و وقتی هنوز بهروز باشد، سرور با 304 و بدون بدنه پاسخ میدهد که بسیار ارزان است. no-store نگه داشتن پاسخ را در هر جایی ممنوع میکند.
استفاده از no-store وقتی منظورتان no-cache است، هر بهینهسازی را بدون هیچ سودی دور میریزد: هر درخواست به یک دانلود کامل تبدیل میشود، حتی وقتی چیزی عوض نشده است. no-store را فقط وقتی به کار ببرید که خودِ وجود یک نسخهی ذخیرهشده مسئله است — صورتحسابهای بانکی، صفحات بازیابی رمز عبور، هر چیزی که نباید در کش یک ماشین مشترک بنشیند. برای «همیشه آخرین نسخه را نشان بده»، no-cache پاسخ درست و بهمراتب ارزانتر است.
این هدر چطور با CDN شما تعامل میکند
هدرهایی که مبدأ شما میفرستد همان چیزی هستند که edge هنگام تصمیم دربارهی مدت نگهداری یک نسخه از آنها پیروی میکند — و همین Cache-Control را به سطح کنترل کل زنجیرهی تحویل شما تبدیل میکند، نه یک جزئیات مرورگری. s-maxage بفرستید و edge از آن پیروی میکند؛ no-store بفرستید و edge اصلاً کش نمیکند، پس هر درخواست تا مبدأ شما سفر میکند و عملاً CDN را برای آن پاسخ خاموش کردهاید.
روی cdn.com.tr وقتی نمیتوانید هدرهای اپلیکیشن را عوض کنید، میتوانید رفتار کش را بهازای هر delivery rule از پنل هم تنظیم کنید — که برای اپلیکیشنهای قدیمی که ترجیح میدهید دستکاری نشوند مفید است. و وقتی چیزی را که یک URL کششده برمیگرداند تغییر میدهید، یادتان باشد edge تا زمان انقضا هنوز نسخهی قبلی را در دست دارد: purge دقیقاً برای همین است.
بررسی اینکه واقعاً چه میفرستید
فرضها دربارهی هدرها اغلب اشتباه از آب درمیآیند — پیشفرض یک فریمورک، افزونه یا وبسرور معمولاً چیزی را که فکر میکنید پیکربندی کردهاید بازنویسی میکند. برای هر نوع URL با یک دستور بررسی کنید، و هم یک asset نسخهدار و هم یک صفحهی HTML را نگاه کنید، چون باید کاملاً متفاوت بهنظر برسند. همچنین مراقب یک Set-Cookie روی پاسخی باشید که قصد داشتید عمومی کش شود: بسیاری از کشها از ذخیرهی چنین پاسخهایی سر باز میزنند و این یکی از دلایل رایج آن است که یک صفحه بهطور مرموزی هرگز کش نمیشود.
هدرهای واقعی پاسخ را بخوانید
# see what the edge and origin actually say
curl -sI https://example.com/ | grep -i "cache-control\|age\|set-cookie"
# compare a versioned asset (should be a long max-age)
curl -sI https://example.com/js/app.a1b2c3.js | grep -i cache-control
پرسشهای پرتکرار
برای صفحات HTML چه max-ageای مناسب است؟
کوتاه — از چند ثانیه تا چند دقیقه — چون آدرس ثابت میماند در حالی که محتوا عوض میشود. آن را با یک s-maxage بلندتر روی edge و با stale-while-revalidate ترکیب کنید تا بازدیدکنندگان پاسخ آنی بگیرند و تازهسازی در پسزمینه انجام شود.
آیا هنوز باید Expires را کنار Cache-Control بفرستیم؟
نه. Cache-Control جای Expires را میگیرد و هرجا هر دو باشند برنده است. Expires فقط برای کلاینتهای بسیار قدیمی اهمیت دارد؛ فرستادنش بیضرر است اما چیزی اضافه نمیکند.
چرا صفحهی من با وجود max-age طولانی کش نمیشود؟
بیشتر اوقات بهخاطر یک هدر Set-Cookie روی پاسخ، یک directive از نوع private یا no-store جایی در زنجیره، یا یک query string که هر درخواست را به یک کلید کش یکتا تبدیل میکند. پیش از تغییر پیکربندی، هدرهای واقعی پاسخ را بررسی کنید.
آیا immutable واقعاً یعنی برای همیشه؟
یعنی «بدنهی این URL عوض نخواهد شد»، پس کشها اعتبارسنجی را کلاً رد میکنند. این فقط دربارهی نامفایلهای نسخهدار درست است. گذاشتن immutable روی URLی که بعداً بازنویسی میکنید، همان کاری است که بازدیدکنندگان را روی یک فایل قدیمی گیر میاندازد، بدون راه تمیزی برای اصلاح.