Cache پاسخ اشتباه را هم میتواند بسیار سریع تحویل دهد. اگر Key دو کاربر را یکی بداند، داده شخصی Share میشود؛ اگر Invalidation جا بماند، قیمت یا خبر قدیمی میماند؛ و اگر همه Entryها همزمان منقضی شوند، Origin زیر موج بازسازی میرود. بنابراین «کش چیست» فقط پرسش سرعت نیست؛ پرسش هویت پاسخ، تازگی، سازگاری، تابآوری و شواهد صحت است.
در این راهنما Cache را از Browser و CDN تا API، Application و Redis بهصورت یک سیستم تصمیم بررسی میکنیم. اگر هدف شما تنظیم WP Rocket، Object Cache، استثنای Checkout یا رفع نمایش نسخه قدیمی در WordPress است، راهنمای اجرایی کش وردپرس را بخوانید؛ این مقاله مرجع معماری عمومی وب است.
پاسخ کوتاه: کش چیست؟
Cache یک نسخه موقت و قابلبازسازی از Response یا نتیجه پردازش را در لایهای نگه میدارد که استفاده از آن از تولید دوباره کمهزینهتر است. هنگام Request بعدی، سیستم با Cache key دنبال نسخه مناسب میگردد؛ اگر Entry قابلاستفاده باشد HIT، اگر نباشد MISS، اگر از Freshness گذشته باشد Stale و اگر Rule عمداً Cache را کنار بزند BYPASS رخ میدهد.
| اصل | معنا | پیامد طراحی |
|---|---|---|
| Copy | Cache نسخه اصلی نیست | حذف آن نباید داده تجاری را نابود کند |
| Identity | فقط Requestهای همارز Share میشوند | Cache key بخشی از مدل داده است |
| Bounded staleness | کهنگی باید حد داشته باشد | TTL تنها کنترل نیست؛ Event هم لازم است |
| Regeneration | MISS هزینه دارد | Origin و Stampede باید طراحی شوند |
| Evidence | HIT به معنی صحت نیست | Header، Version و Source parity لازماند |
Cache با Storage، Session، Queue و Backup فرق دارد
| جزء | وظیفه | اگر حذف شود | نمونه |
|---|---|---|---|
| Cache | کاهش هزینه تکرار | با هزینه بیشتر بازسازی شود | HTML، Query result |
| Primary storage | حقیقت پایدار | از دست رفتن داده رخ میدهد | Order database |
| Session | State Journey/Identity | کاربر Logout یا Journey خراب میشود | Cart session |
| Queue | کارهای در انتظار | Job گم یا تکرار میشود | ارسال ایمیل/پردازش |
| Backup | بازیابی پس از خرابی | Recovery ممکن نیست | Snapshot دیتابیس |
یک فناوری مانند Redis میتواند برای چند نقش استفاده شود، اما Persistence، Eviction، Isolation و Recovery آن نقشها یکسان نیست. «پاککردن Redis» بدون دانستن Scope ممکن است Cache، Session یا Queue برنامه دیگری را هم متاثر کند.
کش چه زمانی ارزش دارد؟
نامزد مناسب Cache معمولاً چهار ویژگی دارد: Read تکراری، هزینه تولید قابلتوجه، خروجی نسبتاً پایدار و تحمل مشخص برای Staleness. Cache همیشه انتخاب درست نیست.
| سیگنال | Cache محتمل است | Cache پرریسک/کمارزش است |
|---|---|---|
| Reuse | همان نتیجه بارها خوانده میشود | هر Request تقریباً یکتا است |
| Generation cost | DB/API/Render گران است | ساخت نتیجه از Lookup Cache ارزانتر است |
| Change rate | تغییر قابل پیشبینی/رویداددار است | داده لحظهای و پرریسک است |
| Correctness impact | کهنگی محدود قابلقبول است | Balance/Payment/Permission باید جاری باشد |
| Working set | بخش پرتکرار در Memory میگنجد | Cardinality بالا و Reuse پایین است |
| Operations | Owner/Monitor/Invalidation دارید | هیچکس Rule و Incident را مالک نیست |
بهبود باید نسبت به TCO سنجیده شود: Latency و Origin load کمتر در برابر Memory، Complexity، Invalidation، Security review، Testing و Incident cost. برای اتصال Performance به Journey و Outcome، چارچوب سرعت سایت و اثر تجاری را ببینید.
معماری لایهای Cache در وب
| لایه | چه چیزی Cache میشود؟ | نزدیک به | Key/Policy | Failure نمونه |
|---|---|---|---|---|
| Browser HTTP cache | Response/Asset | User | URL + HTTP semantics | Asset قدیمی |
| Service Worker/Cache API | Response طبق کد Client | User/App | منطق برنامه | Offline shell ناسازگار |
| CDN/Edge | Asset، HTML یا API عمومی | User region | Edge rule/HTTP | PoP قدیمی یا Key اشتباه |
| Reverse proxy | Response Application | Origin | VCL/Nginx/Host rule | Cookie/Route bypass ناقص |
| Full-page/Fragment | HTML یا بخش Template | Application | Page/Fragment identity | Personalization مشترک |
| API gateway | Endpoint response | Service boundary | Method/Path/Query/Auth | Scope/Permission غلط |
| Application/Object | Entity/Computation | Code | Entity/version/tenant | Stale object |
| Database/result | Plan/Page/Query result | Data source | DB-specific | Query بد پنهان میشود |
| DNS | Name→record | Resolver | Name/type/class | Cutover دیر همگرا |
هر Layer مستقل Freshness و Purge دارد. کاهش TTL مرورگر Edge را پاک نمیکند؛ Purge CDN Object cache برنامه را تازه نمیکند؛ و پاکسازی Plugin DNS resolver را تغییر نمیدهد.
چرخه تصمیم: Request تا Response
- Eligibility: Method، Status، Authentication و Rule اجازه Storage میدهند؟
- Selection: Cache key و
Varyکدام Response ذخیرهشده را انتخاب میکنند؟ - Freshness: Age هنوز در Freshness lifetime است؟
- Reuse: Request directive و Policy اجازه استفاده مستقیم میدهند؟
- Validation: Entry stale با Origin و Validator تازه میشود؟
- Forward/Generate: MISS/BYPASS به Layer بعد یا Origin میرود؟
- Store: Response جدید با Metadata و TTL ذخیره میشود؟
- Invalidate/Evict: Event، Expiry یا فشار Memory Entry را کنار میزند؟
RFC ۹۱۱۱ درباره HTTP Caching مرجع هنجاری رفتار Cacheهای HTTP است. Cache برنامه یا Redis الزاماً همان Semantics را ندارد؛ قرارداد آن باید جدا تعریف شود.
قرارداد Cache: تصمیم را قابلاجرا کنید
| فیلد | پرسش | شاهد |
|---|---|---|
| Artifact | Response، Fragment، Entity یا Query چیست؟ | Content/data model |
| Source of truth | نسخه معتبر کجاست؟ | DB/API owner |
| Eligibility | چه Method/Status/Persona قابل Cache است؟ | Route policy |
| Identity | چه اجزایی Key را میسازند؟ | Key schema |
| Freshness budget | حداکثر کهنگی مجاز چیست؟ | Risk/SLO |
| Invalidation | چه Eventی چه Dependencyهایی را پاک میکند؟ | Event/graph |
| Failure mode | Stale، Fail-open یا Fail-closed؟ | Runbook/test |
| Observability | HIT/MISS/Age/Version چگونه دیده میشود؟ | Header/log/metric |
| Owner | چه کسی Rule، Change و Incident را پاسخ میدهد؟ | RACI/on-call |
Cache key هویت پاسخ است
Key بیشازحد کلی Under-partition میکند: Requestهای متفاوت یک Response میگیرند. Key بیشازحد جزئی Over-partition میکند: Entryهای فراوان با Reuse کم ساخته میشوند. هر دو خطا هزینه دارند، اما اولی میتواند Correctness یا Privacy را نقض کند.
| جزء Request | چه زمانی در Key؟ | خطر حذف | خطر افزودن |
|---|---|---|---|
| Scheme/Host | Origin/Response متفاوت | Host contamination | Fragmentation |
| Path | تقریباً همیشه | Resource اشتباه | Aliasهای بیدلیل |
| Query | Parameter محتوایی | Search/Variant اشتباه | UTM/Noise و Cardinality |
| Accept-Encoding | Representation متفاوت | Encoding ناسازگار | Variation بیقاعده |
| Language | Server-side locale متفاوت | زبان/RTL اشتباه | Header variation زیاد |
| Device | Markup سرور واقعاً متفاوت | نسخه ناسازگار | Hit کمتر |
| Tenant/Permission | اگر Cache مجاز و Scope جداست | Cross-tenant leak | Per-user cache کمارزش |
مستند Cache keys در Cloudflare نمونهای از تأثیر Query، Header، Cookie، Host و ویژگی User بر Key است. قابلیت و Plan هر CDN فرق دارد؛ طراحی مفهومی را با Rule اجراشده همان Provider تطبیق دهید.
Parameter registry بسازید
| Parameter | Purpose | Output را عوض میکند؟ | Key policy | Owner |
|---|---|---|---|---|
utm_source | Attribution | معمولاً خیر | Normalize/Exclude پس از تست | Marketing |
lang | Locale | بله | Include یا Path مستقل | Product |
variant | SKU selection | ممکن است | طبق Response contract | Commerce |
preview | Draft access | بله/حساس | Shared bypass | CMS |
Cookie را کورکورانه وارد Vary نکنید
Cookie میتواند ترکیب بسیار پرCardinality بسازد. برای محتوای شخصی، Bypass Shared cache یا Key محدود و صریح اغلب امنتر از Variation روی کل Cookie header است. Presence کوکی بهتنهایی Response را Private نمیکند.
Private cache و Shared cache
راهنمای HTTP caching در MDN Private cache مانند Browser یک کاربر را از Shared cache مانند Proxy/CDN جدا میکند. Response شخصی اگر در Shared cache ذخیره شود ممکن است به User دیگر برسد. private استفاده Shared را منع میکند، اما رفتار Managed CDN و Ruleهای Override نیز باید بررسی شوند.
| Response | Private cache | Shared cache | کنترل اصلی |
|---|---|---|---|
| Asset عمومی | مناسب | مناسب | Version + max-age |
| HTML عمومی | مشروط | مشروط | s-maxage/Purge/Validator |
| Profile/Account | طبق حساسیت | نامناسب | private/no-store + Rule |
| Payment result | اغلب نامناسب | نامناسب | no-store/Bypass + Backend truth |
| Public API | طبق Contract | طبق Contract | Auth/Query/Vary/TTL |
Cache-Control را دقیق بخوانید
| Directive | معنا | مرزبندی |
|---|---|---|
max-age | Freshness lifetime برای Cache هدف | از زمان تولید Response محاسبه میشود، نه صرفاً دریافت |
s-maxage | Freshness ویژه Shared cache | بر max-age/Expires در Shared cache تقدم دارد |
public | ذخیره Shared را حتی در بعضی حالتها مجاز میکند | به معنی «همه Data امن است» نیست |
private | Shared cache نباید ذخیره کند | Private cache ممکن است |
no-cache | پیش از Reuse باید Validate شود | ذخیره را منع نمیکند |
no-store | Cache نباید Response را ذخیره کند | History/Implementation nuance را هم تست کنید |
must-revalidate | پس از Stale بدون Validation reuse نشود | Failure mode را سختتر میکند |
immutable | در Freshness نیاز به Revalidate نیست | برای URL Versioned مناسب است |
مرجع Cache-Control در MDN Directiveها و تفاوت Request/Response را توضیح میدهد. ترکیب Copy-paste شده برای همه Routeها نسازید؛ Header باید خروجی Cache contract باشد.
Asset fingerprinting، TTL بلند را کمریسک میکند
وقتی نام فایل از Content hash میآید، تغییر محتوا URL جدید میسازد. نسخه قبلی میتواند با immutable و عمر بلند بماند و HTML جدید به Asset جدید اشاره کند. ترتیب Deploy باید مانع HTML جدید + Asset غایب یا HTML قدیمی + Asset حذفشده شود. Minify/Bundle/Delay JS موضوع جداست و در راهنمای بهینهسازی CSS و JavaScript بررسی شده است.
Freshness، Age و Expiration یکی نیستند
TTL در زبان عملی معمولاً «عمر» است، اما در HTTP Freshness از زمان تولید، Headerها و Age محاسبه میشود. Response پس از پایان Freshness فوراً حذف نمیشود؛ Stale میشود و شاید Validate، Reuse محدود یا Evict شود.
| مفهوم | پرسش | خطای رایج |
|---|---|---|
| Freshness lifetime | تا چه زمانی بدون تماس با Origin قابل استفاده است؟ | برابر دانستن با وجود فیزیکی Entry |
| Current age | Response عملاً چند ثانیه سن دارد؟ | نادیده گرفتن زمان Cacheهای قبلی |
| Stale | از Freshness گذشته؟ | فرض اینکه حتماً حذف شده |
| Expiration | Policy برنامه چه زمانی Entry را منقضی میکند؟ | قول ماندگاری تا آن زمان |
| Eviction | Memory policy Entry را زودتر حذف کرده؟ | تفسیر هر MISS به TTL |
Validation: تازهسازی بدون دریافت دوباره Body
Validatorهایی مانند ETag و Last-Modified به Cache اجازه میدهند با Request شرطی بپرسد Representation تغییر کرده است یا نه. اگر تغییر نکرده باشد، Origin میتواند 304 Not Modified بدون Body کامل برگرداند. Validator جای Invalidation نیست؛ فقط Revalidation را کارآمد میکند.
| Validator | Request شرطی | Strength/Use | ریسک |
|---|---|---|---|
ETag | If-None-Match | شناسه Representation | تولید ناسازگار میان Nodeها |
Last-Modified | If-Modified-Since | زمان تغییر | Resolution/Clock/تغییر سریع |
| Version key | Application lookup | نسخه Entity/Build | فراموشی bump وابستگی |
Vary بخشی از Selection است
Vary: Accept-Language میگوید انتخاب Response ذخیرهشده به Header زبان Request وابسته است. Vary: * Reuse را عملاً محدود میکند. Vary: User-Agent میتواند Variantهای بسیار زیاد بسازد؛ اگر Feature detection یا Responsive CSS کافی است، Variation دستگاه را نسازید.
Method و Status را در Cacheability لحاظ کنید
GET/HEAD معمولاً محور Cache HTTP هستند، اما Method تنها شرط نیست. Authorization، Directive، Status، Rule محلی و Semantics Response هم دخیلاند. عملیات State-changing را با Cache پاسخ خواندنی اشتباه نگیرید.
| حالت | خطر | Policy لازم |
|---|---|---|
| Authenticated GET | داده شخصی در Shared cache | صریح و پیشفرض محافظهکارانه |
| 301/308 | Redirect قدیمی طولانی میماند | Migration و TTL آگاهانه |
| 404 | پس از ایجاد Resource، Not-found قدیمی | Negative-cache budget/Purge |
| 429 | Throttle یک User به دیگران تعمیم مییابد | Identity/Retry contract |
| 5xx | خطا بهجای Recovery Cache میشود | عدم ذخیره یا TTL بسیار کنترلشده |
| Range/206 | Partial response ناقص reuse میشود | پشتیبانی استاندارد لایه |
Negative caching برای جلوگیری از فشار Resourceهای ناموجود مفید است، اما TTL بلند روی ۴۰۴ یا Failure میتواند Recovery را پنهان کند. Status policy را مثل Content policy مستند کنید.
Cache-Status: HIT/MISS را استانداردتر گزارش کنید
Providerها Headerهایی مانند X-Cache یا CF-Cache-Status دارند، اما معنی و Syntax آنها یکسان نیست. RFC 9211 هدر استاندارد Cache-Status را برای توضیح رفتار Cache تعریف میکند: hit، علت Forward، TTL باقیمانده، Stored بودن، Request collapse و حتی نمایش اختیاری Key.
| سیگنال | چه میگوید؟ | چه نمیگوید؟ |
|---|---|---|
hit | Request از همان Cache پاسخ گرفته | Response درست یا تازه تجاری است |
fwd=uri-miss | URI قابل استفاده در Cache نبود | چرا Layerهای دیگر MISS بودند |
fwd=stale | Entry بود اما برای Reuse کافی نبود | علت Root تغییر/TTL |
ttl | Freshness باقیمانده طبق همان Cache | Freshness همه Layerها |
collapsed | Requestها هنگام Forward همگرا شدند | ظرفیت Origin در Burstهای دیگر |
RFC همچنین هشدار میدهد که افشای جزئیات Key/Status میتواند به تحلیل رفتار Cache یا حمله کمک کند. Header Debug حساس را فقط برای Client مجاز یا بدون Detail خطرناک ارائه دهید.
CDN و Edge Cache: نزدیکی کافی نیست
CDN زمانی ارزش دارد که Route/Asset واقعاً Cacheable، PoP برای Audience مفید و Origin/Purge درست متصل باشند. انتخاب Provider، پوشش شبکه ایران، امنیت Origin، WAF، Contract و Exit در راهنمای انتخاب CDN برای سایت ایرانی جدا بررسی شده است.
| تصمیم Edge | پرسش | شاهد |
|---|---|---|
| Eligibility | کدام Content type/Status/Route ذخیره میشود؟ | Cache rule trace |
| Key | Query/Cookie/Host/Language چگونهاند؟ | Key inspection/variant test |
| Origin control | Header Origin اجرا یا Override میشود؟ | Rule precedence |
| Purge | URL/Prefix/Tag و Propagation SLA چیست؟ | API response + probe |
| Resilience | Origin down چه نسخهای سرو میشود؟ | Failure exercise |
| Privacy | Authenticated/personal route Bypass است؟ | Cross-session test |
مستند Cache response در Cloudflare نمونهای از Statusهای HIT/MISS/BYPASS/EXPIRED/STALE/REVALIDATED و شرایط محصول است. از معنای یک Provider برای دیگری استفاده نکنید.
Browser TTL و Edge TTL را جدا طراحی کنید
ممکن است بخواهید HTML در Browser زود Validate شود اما Edge مدت بیشتری نگه دارد و با Purge کنترل شود. s-maxage یا Header/Rule مخصوص CDN میتواند این تفکیک را بسازد. نتیجه را روی Chain واقعی تست کنید؛ Layer میانی ممکن است Directive را Override کند.
Purge propagation بخشی از SLO است
پاسخ ۲۰۰ API پاکسازی لزوماً به معنی تازهشدن همه PoPها در همان لحظه نیست. زمان Event→Provider accepted→PoP visible را با Version marker و Region probes اندازه بگیرید. برای خبر اصلاحی، قیمت یا Incident این فاصله Outcome مهم است.
Service Worker Cache با HTTP Cache یکی نیست
Cache API در MDN Storage برنامهپذیر برای Request/Response pairs است و از HTTP cache-control headers بهصورت خودکار برای Expiration پیروی نمیکند؛ برنامه باید Version، Update و Delete را مدیریت کند. بنابراین Hard refresh یا Purge CDN ممکن است نسخه Service Worker را عوض نکند.
| Strategy | رفتار | Fit | ریسک |
|---|---|---|---|
| Cache first | Cache سپس Network | Asset Versioned/Offline | Update دیر |
| Network first | Network سپس Fallback Cache | HTML/Data تازه | Latency/Offline timeout |
| Stale-while-revalidate | Cache فوری + Update پسزمینه | کهنگی کوتاه قابلقبول | نمایش نسخه قدیمی |
| Network only | بدون Cache API | Transaction/sensitive | عدم Offline |
نام Strategy شبیه Directive HTTP است اما Implementation و پشتیبانی یکی نیست. Client cache را در Inventory مستقل ثبت کنید.
الگوهای Cache در Application
| Pattern | Read | Write | مزیت | ریسک |
|---|---|---|---|---|
| Cache-aside | Cache→MISS→Primary→Store | Primary سپس Invalidate/Update | فقط Working set | Cold miss/Stale race |
| Read-through | Cache خودش Primary را میخواند | طبق Provider | Code سادهتر | Coupling/Provider fit |
| Write-through | Cache آمادهتر | Primary و Cache همگام | Read hit بیشتر | Dual-write failure/Cache pollution |
| Write-around | MISS پس از Write محتمل | مستقیم Primary | داده کمخوانده Cache را پر نمیکند | Read-after-write کند |
| Write-behind | از Cache | Cache سپس Async Primary | Write throughput | Durability/ordering/data loss |
راهنمای الگوهای Cache در AWS Cache-aside و Write-through را مقایسه میکند. Write-behind را فقط چون سریعتر است انتخاب نکنید؛ اگر Cache پیش از Durable write از دست برود، داده ممکن است گم شود. برای Order/Payment معمولاً Source of truth پایدار و Idempotency مقدماند.
Cache-aside ساده است، Race ساده نیست
جریان معمول: Read Cache؛ در MISS از Primary بخوان؛ Store با TTL. هنگام Update معمولاً ابتدا Primary را تغییر و سپس Key را Invalid میکنیم. اگر Delete قبل از Commit یا همزمان با Reader انجام شود، Reader ممکن است مقدار قدیمی را دوباره Store کند. Transaction boundary، Versioned key، event ordering و Retry لازماند.
مستند Cache-aside در Redis TTL-bounded staleness، Invalidate-on-write و خطر Stampede روی Key محبوب را توضیح میدهد. عددهای Performance آن محصول/سناریو را Benchmark عمومی سایت خود ندانید.
Invalidation: تغییر Source باید به Dependency برسد
TTL فقط سقف زمانی است؛ اگر داده پیش از آن تغییر کند، Event باید Entryهای وابسته را تازه کند. Dependency همیشه یکبهیک نیست.
| Change event | Dependencyها | Invalidation | Acceptance |
|---|---|---|---|
| ویرایش مقاله | Article، Home، Category، Related | URL/Tag purge | Version در همه Surfaceها |
| تغییر SKU | PDP، Listing، Search، API | Entity event fan-out | Page/API/Cart parity |
| تغییر Permission | Policy/Profile cache | User/role version bump | Access فوری درست |
| Deploy Template | همه Responseهای Template | Surrogate tag/Generation | Build ID همگرا |
| Delete Resource | Entity، Lists، Negative cache | Tombstone + dependent purge | نه Ghost، نه resurrect |
Generation key از Delete انبوه ارزانتر است
بهجای پیدا کردن همه Keyهای قدیمی، Namespace نسخهای مانند catalog:v42:... میتواند با تغییر Generation، Readهای جدید را به مجموعه تازه ببرد. اما Entryهای قدیمی تا Eviction Memory میگیرند و Bump اشتباه Full cold start میسازد.
Tag purge برای Graph مناسبتر از URL تنهاست
اگر Responseهای Product و Listing Tag مشترک SKU داشته باشند، Event تغییر SKU میتواند همه را هدف بگیرد. Support و Limit Provider، Cardinality Tag، Propagation و Failure باید سنجیده شوند.
Expiry، Eviction و Invalidation سه رویداد متفاوتاند
| رویداد | علت | قابل پیشبینی؟ | کنترل |
|---|---|---|---|
| Expiry | زمان سیاست تمام شده | تقریباً | TTL/Jitter/Refresh |
| Invalidation | Source/Dependency تغییر کرده | Event-driven | Purge/Version/Update |
| Eviction | Memory/Policy/Pressure | نه برای هر Key | Sizing/Policy/Working set |
| Manual purge | Operator/Incident | بله | Scope/Approval/Log |
مستند Eviction در Redis Policyهای LRU/LFU/TTL/random/noeviction و شاخصهای Memory/Hit/Miss/Evicted را توضیح میدهد. MISS ناگهانی را فقط به TTL نسبت ندهید؛ Eviction یا Namespace churn ممکن است علت باشد.
Stale پاسخ بد نیست؛ پاسخ بدون بودجه بد است
در بعضی Routeها نسخه چندثانیهای قدیمی بهتر از Error یا Latency زیاد است؛ در Balance، Permission یا Payment چنین نیست. RFC 5861 دو کنترل را تعریف میکند:
stale-while-revalidate: در بازه محدود نسخه Stale سرو و Revalidation پسزمینه انجام شود؛stale-if-error: در خطای Origin، نسخه Stale تا بازه تعیینشده استفاده شود.
| Artifact | SWR fit | SIE fit | Guardrail |
|---|---|---|---|
| مقاله عمومی | اغلب قابلبررسی | ممکن است | اصلاح حساس سریع Purge شود |
| Catalog عمومی | مشروط | مشروط | Price/Stock parity |
| Service status page | با احتیاط | ممکن است گمراه کند | Incident truth |
| Account/Permission | نامناسب | نامناسب | Current authorization |
| Payment/Balance | نامناسب | نامناسب | Durable truth |
پشتیبانی واقعی Browser/CDN/Proxy را آزمون کنید؛ وجود Header مساوی اجرا در هر Layer نیست.
Cache stampede: محبوبترین Key میتواند Origin را بشکند
اگر Entry داغ منقضی شود و همه Requestها MISS شوند، هرکدام به Primary میرود. کنترلها:
| کنترل | اثر | ریسک/شرط |
|---|---|---|
| Request collapse/singleflight | یک Regeneration، چند Waiter | Timeout/leader failure |
| Lock با Token | مالک مشخص بازسازی | Lock expiry و safe release |
| TTL jitter | انقضاها پخش میشوند | Freshness سخت |
| Probabilistic early refresh | Key داغ زودتر تازه میشود | پیچیدگی/Extra load |
| SWR | Waiter نسخه Stale میگیرد | بودجه Staleness |
| Prewarm | Entryهای حیاتی آمادهاند | Cache pollution/Origin load |
Full purge در ترافیک بالا Stampede را از یک Key به کل Working set گسترش میدهد. سناریوی Cache cold، شکست Layer و Recovery را همراه طرح Uptime و مانیتورینگ تمرین کنید.
API caching: Contract هر Endpoint جداست
API عمومی Read-heavy میتواند Cache شود، اما Auth scope، Query normalization، Pagination، Error، Rate limit، Version و Invalidation باید مشخص باشند. REST بودن بهتنهایی Cacheability را تضمین نمیکند.
| Endpoint | Key | Freshness | ریسک |
|---|---|---|---|
| Public catalog | Version/Query/Page/Locale | کوتاه + Event | Price/stock stale |
| Search suggestion | Normalized query/locale | متوسط | Cardinality/abuse |
| User profile | ترجیحاً Shared bypass | جاری | Cross-user leak |
| Permission check | Subject/resource/policy version | بسیار محدود | Access stale |
| Webhook/callback | Cache نشود | عملیات Idempotent | Replay/state loss |
Validation، Idempotency، Retry و Reliability Endpointها در راهنمای طراحی و امنیت API عمیقتر آمده است.
Cache security: سرعت روی مرز اعتماد
Web Cache Deception
Attacker مسیری میسازد که برای Origin Dynamic/شخصی است اما برای Cache شبیه Asset است. اگر Origin روی Path اضافه همان HTML شخصی را ۲۰۰ بدهد و CDN از پسوند تصمیم بگیرد، Response ممکن است Shared شود. مستند Cloudflare درباره Cache Deception تطابق Extension و Content-Type را یکی از کنترلها میداند؛ Route correctness و no-store/private همچنان لازماند.
Cache poisoning
Input کنترلشده مهاجم—Host، Header، Query یا Path normalization—ممکن است Response آلودهای بسازد که زیر Key عمومی ذخیره شود. Canonicalization یکسان در CDN/Proxy/App، Allowlist Host، حذف Headerهای Unkeyed اثرگذار، تست Variant و محدود کردن Debug detail لازماند.
Cross-tenant و Cross-user leakage
دو Tenant یا User را با Markerهای متفاوت همزمان تست کنید. اگر پاسخ شخصی جابهجا شد، فوراً Bypass/Purge، Evidence و Scope را حفظ و Incident را فعال کنید؛ این فقط Bug Performance نیست. پاسخ اعتماد و Remedy را با سیستم اعتماد و رسیدگی به رخداد مشتری هماهنگ کنید.
چندلایه بودن، Debug را سخت میکند
Browser ممکن است HIT دهد درحالیکه CDN MISS است؛ CDN میتواند HIT باشد و Object cache هم HIT قدیمی خودش را داشته باشد. برای هر Layer شناسه و Marker مستقل لازم است.
| Evidence | Layer | محدودیت |
|---|---|---|
| DevTools memory/disk cache | Browser | همه Clientها را نمایندگی نمیکند |
| Service Worker log/version | Client app | HTTP cache جداست |
| Age/Cache-Status/vendor header | Edge/Proxy | معنای Provider-specific |
| Build/Content version | Body/Application | باید غیرشخصی باشد |
| Key/TTL/Hit metric | Object store | Sampling/Cardinality |
| Trace/Query log | Origin/Primary | Privacy/Cost |
اندازهگیری Cache: Hit ratio کافی نیست
| Metric | تعریف | تقسیم لازم | تصمیم |
|---|---|---|---|
| Request hit ratio | HIT / Lookup | Layer، Route، Persona | Key/TTL/Eligibility |
| Byte hit ratio | Byteهای Cache / کل Byte | Asset type/Region | Bandwidth/CDN |
| Origin offload | Request/Compute حذفشده از Origin | Endpoint/Cold/Warm | Capacity/TCO |
| Latency | p50/p75/p95/p99 | HIT/MISS/BYPASS/PoP | User/SLO |
| Freshness lag | Event→نسخه تازه قابل مشاهده | Surface/Region | Invalidation/Purge |
| Correctness | Parity/Leakage/Stale incident | Entity/Persona | Guardrail/Incident |
| Store health | Memory/Eviction/Latency/Error | Instance/Namespace | Sizing/Policy |
Hit ratio را با Denominator روشن گزارش کنید: آیا BYPASS داخل Lookup است؟ درخواست Revalidated HIT یا MISS شمرده شده؟ Layerها یک تعریف دارند؟ Data contract و Reconciliation این ابهام را حل میکند؛ اصول آن در معماری تحلیل داده و GA4/Warehouse قابل تعمیم است.
Hit بالا میتواند بد باشد
اگر Error، ۴۰۴ طولانی، Permission قدیمی یا Response شخصی Cache شود، Hit بالا فقط دامنه خطا را بیشتر میکند. Correctness، Freshness و Business guardrail مقدماند.
Hit پایین همیشه بد نیست
Checkout و Account باید BYPASS باشند. Long-tail URLها ممکن است Reuse کافی نداشته باشند. بهجای هدف کلی ۹۰٪، Target را برای هر Artifact class و Cost/Benefit آن تعریف کنید.
آیا Cache مستقیماً رتبه SEO را بالا میبرد؟
Cache تضمین رتبه یا Conversion نیست. میتواند Origin latency و پایداری را بهتر و تحویل Asset را کارآمد کند؛ اما محتوای ضعیف، JavaScript سنگین، Layout shift، Server error یا UX بد را خودکار حل نمیکند. از سوی دیگر، Cache قدیمی میتواند Title، Canonical، Robots، Structured data یا Content قبلی را به Crawler بدهد. پس Release SEO نیز به Purge و بررسی HTML عمومی نیاز دارد.
عیبیابی Cache با فرضیه، نه Clear همهچیز
۱. Artifact و Expected version را ثبت کنید
URL نهایی، زمان تغییر، Expected/Actual، Persona، Region و Artifact را بنویسید: HTML، Asset، API، Entity یا DNS. یک Marker مانند Build ID یا Content version از «برای من تازه است» دقیقتر است.
۲. Redirect و Variant را مشخص کنید
Scheme، Host، Slash، Query، Language، Encoding، Device و Auth را ثبت کنید. ممکن است URL قدیمی Purge شده اما Destination، Variant زبان یا PoP دیگری قدیمی باشد.
۳. Header و Body را با Request واقعی بگیرید
curl -sS -D headers-1.txt -o body-1.html 'https://example.com/final-url/'
curl -sS -D headers-2.txt -o body-2.html 'https://example.com/final-url/'HEAD ممکن است مسیر متفاوتی از GET داشته باشد. Headerهای Cache-Control/Age/Vary/ETag/Cache-Status و Provider را کنار Hash/Marker Body بخوانید. Request Debug را روی داده حساس یا بدون مجوز اجرا نکنید.
۴. Layer را با آزمون ردکننده محدود کنید
| نشانه | فرضیه | آزمون ردکننده |
|---|---|---|
| فقط یک Browser قدیمی | HTTP cache/SW | Profile/Device دیگر |
| یک Region قدیمی | CDN PoP | Probe چند Region/Origin |
| HTML تازه، Data قدیمی | API/Object cache | Primary/API direct evidence |
| Guest قدیمی، Login تازه | Shared page cache | Authorized bypass/Origin |
| Purge بیاثر | Layer دیگر/Key دیگر | Cache chain و exact key |
| Purge→5xx | Cold capacity/Stampede | Warm vs Cold load |
۵. کوچکترین Scope اثباتشده را تغییر دهید
یک URL، Tag، Entity key یا Version را هدف بگیرید. Full purge، Flush سراسری یا Restart فقط با Scope معتبر، Window، Monitoring و Rollback انجام شود.
۶. Root cause را پس از Recovery ببندید
Purge موفق نشان میدهد یک نسخه Cached دخیل بوده؛ مشخص نمیکند Event چرا نرسید، Dependency چرا جا ماند یا Key چرا متفاوت بود. Timeline، Rule diff، Purge log و Regression test را ثبت کنید.
ماتریس تست Cache
| محور | حالتها | Assertion |
|---|---|---|
| Identity | Guest A/B، User A/B، Admin | Response شخصی جابهجا نمیشود |
| State | Cold، MISS، HIT، Stale، پس از Purge | Header/Body مطابق Contract |
| Query | Canonical، UTM، Search، Variant | Normalization و Key صحیح |
| Locale/Device | fa/en، RTL/LTR، Mobile/Desktop | Variant درست و محدود |
| Status | ۲۰۰، ۳۰۱، ۴۰۴، ۴۲۹، ۵۰۰ | TTL/Store policy درست |
| Change | Entity، List، Template، Permission | همه Dependencyها تازه |
| Failure | Origin/Cache/Network down | Stale/Fail mode درست |
| Concurrency | Hot key expiry/Purge burst | Stampede کنترل شده |
تست Layer-local و End-to-end هر دو لازماند
Unit test Key builder و Invalidation handler خطای Code را میگیرد؛ Integration test Cache store و Event را؛ End-to-end مسیر Browser→Edge→Origin را. هیچکدام جای دیگری را نمیگیرد.
سه مثال برای مخاطب ایرانی
رسانه با خبر و اصلاح فوری
Assetهای Versioned عمر بلند دارند؛ HTML خبر Edge-cache کوتاه و Event purge دارد. اصلاح حقوقی/حساس Priority purge و Probe چند Region میگیرد. Home/Category وابسته نیز با Tag خبر پاک میشوند. Stale-if-error برای مقاله عادی شاید مجاز باشد، اما برای Correction حساس بودجه محدودتری دارد.
فروشگاه با قیمت و موجودی
Catalog/PDP عمومی Cacheable مشروطاند؛ تغییر SKU به Listing/Search/API fan-out میشود. Cart، Account، Checkout و Callback Shared bypass هستند. Price/Stock در Cart دوباره از Source معتبر کنترل میشود. Cache cold پیش از کمپین با نرخ کنترلشده سنجیده میشود.
SaaS با Landing عمومی و Dashboard شخصی
Landing و Docs در Browser/CDN Cache میشوند؛ Dashboard و Permission response پیشفرض Shared bypass هستند. Reference data عمومی ممکن است Cache-aside با Version tenant-independent داشته باشد، اما Feature entitlement به Policy version و User/Tenant scope حساس است.
چه چیزهایی را بهتر است Cache نکنیم؟
- Secret، Token، Password reset و Response دارای اطلاعات حساس؛
- Payment، Balance، Permission و تصمیم امنیتی بدون طراحی تخصصی؛
- Responseهای Per-user با Reuse ناچیز در Shared layer؛
- Queryهای ارزان که Cache lookup/serialization از آنها گرانتر است؛
- دادهای که Invalidation یا Freshness آن قابل تعریف نیست؛
- Entry با Cardinality کنترلنشده و ورودی مهاجم؛
- Result غلط برای پنهانکردن Query/Architecture مسئلهدار.
Rollout سیاست Cache
- یک Artifact class پرتکرار و کمریسک انتخاب کنید.
- Baseline Cost/Latency/Correctness و Freshness budget را ثبت کنید.
- Key schema، Eligibility، TTL، Event و Failure mode را بنویسید.
- Unit/Integration/Cross-session/Status tests را بسازید.
- Canary را روی Scope کوچک فعال کنید.
- HIT/MISS/Eviction/Purge lag/Origin/Guardrail را ببینید.
- Cache cold و Layer failure را Controlled آزمایش کنید.
- پس از Acceptance، Coverage را تدریجی گسترش دهید.
- Rollback Rule و همگرایی Cache قبلی را تمرین کنید.
اشتباههای رایج طراحی Cache
| اشتباه | پیامد | اصلاح |
|---|---|---|
| TTL واحد برای همه | کهنگی یا MISS بیدلیل | Freshness budget هر Artifact |
| URL-only key برای پاسخ شخصی | Leakage | Bypass/Scope صریح |
| همه Queryها در Key | Cardinality/Hit پایین | Parameter registry |
| Ignore همه Queryها | Search/Variant غلط | Content-effect analysis |
| Full purge عادی | Cold storm | Dependency/tag/version |
| فقط TTL، بدون Event | تاخیر Freshness | Invalidate-on-change |
| Cache جای Primary | Data loss | Durable source |
| Hit ratio هدف نهایی | خطای سریعتر | Correctness/Outcome guardrail |
| فرض یک Layer | Debug/Purge ناقص | Cache inventory/chain |
| Header Debug عمومی و پرجزئیات | اطلاعات برای حمله | حداقل/Authorized diagnostics |
برنامه ۳۰/۶۰/۹۰ روزه معماری Cache
روز ۱ تا ۳۰: Inventory و مرز اعتماد
- نقشه Browser/SW/CDN/Proxy/API/App/DB/DNS و Owner؛
- طبقهبندی Public/Personal/Sensitive/Transactional؛
- Baseline Warm/Cold، Origin، Freshness و Incident؛
- تأیید Bypass پاسخهای حساس با دو Identity؛
- ثبت Parameter و Status policy.
روز ۳۱ تا ۶۰: قرارداد و کنترل تغییر
- Cache contract برای Artifact classهای اولویتدار؛
- Key schema/version، TTL و Dependency graph؛
- Event-driven invalidation و Purge log/Retry؛
- Versioned asset و Cache-Status/Marker کنترلشده؛
- Automated correctness/freshness tests.
روز ۶۱ تا ۹۰: تابآوری و اقتصاد
- Stampede control، SWR/SIE فقط در Route مناسب؛
- Memory/Eviction/Working-set tuning؛
- Failure و Cache-cold exercise؛
- SLO تازگی/Purge/Recovery و Business guardrail؛
- حذف Entryها و Layerهای کمارزش بر اساس TCO.
چکلیست کیفیت سیاست Cache
- Cache کپی قابلبازسازی است و Source of truth مشخص دارد.
- Benefit/TCO و Artifact class قبل از پیادهسازی تعریف شدهاند.
- تمام Layerها، Rule، Purge path، Evidence و Owner ثبتاند.
- Eligibility شامل Method، Status، Auth و Data class است.
- Key schema و Cardinality budget داریم.
- Query/Cookie/Language/Device فقط با اثر واقعی وارد Key میشوند.
- Private و Shared cache صریحاً جدا شدهاند.
- Cache-Control و Vary با Contract سازگارند.
- Freshness/Age/Stale/Eviction درست تفکیک شدهاند.
- Validator و Asset fingerprinting در محل مناسباند.
- TTL محافظ و Event invalidation هر دو وجود دارند.
- Dependency graph و Targeted purge داریم.
- Purge Idempotent، Observable و Retryable است.
- Stale policy برای Route حساس Fail-closed است.
- Stampede و Full-cold recovery تست شدهاند.
- Cache-Status/Provider headers بدون افشای خطرناکاند.
- Cross-user/tenant، Poisoning و Deception تست شدهاند.
- Warm/Cold/Region/Status/Failure matrix اجرا میشود.
- Correctness/Freshness/Outcome بر Hit ratio مقدماند.
- Rollout، Rollback و Incident owner مشخصاند.
پرسشهای متداول درباره Cache
کش چیست و دقیقاً چه چیزی را سریع میکند؟
Cache نسخه قابلبازسازی Response یا نتیجه پردازش را نگه میدارد تا Request همارز کار کمتری انجام دهد. بسته به Layer میتواند انتقال شبکه، Render، API call یا Query را حذف کند. اگر Reuse یا هزینه تولید کم باشد، سودی ندارد.
TTL مناسب Cache چند ثانیه است؟
عدد جهانی وجود ندارد. TTL باید از نرخ تغییر، پیامد Staleness، هزینه Regeneration، Event invalidation و Failure mode بیاید. Asset Versioned میتواند بسیار بلند و Permission/Payment باید جاری یا بدون Shared cache باشد.
تفاوت no-cache و no-store چیست؟
no-cache Storage را منع نمیکند؛ Reuse را به Validation موفق وابسته میکند. no-store ذخیره Response را منع میکند. private نیز Shared cache را منع میکند، اما Private cache ممکن است ذخیره کند.
چرا با وجود Cache، Hit ratio پایین است؟
Cardinality Query/Cookie/Device، TTL کوتاه، Eviction، Working set بزرگ، Routeهای BYPASS، چند Host یا Key normalization متفاوت ممکن است علت باشد. ابتدا Miss reason را به تفکیک Layer/Route بخوانید؛ افزایش Memory تنها یکی از فرضیههاست.
چرا پاکسازی کامل Cache خطرناک است؟
Full purge تمام Working set را Cold میکند. Requestهای بعدی همزمان به Origin/Primary میروند و میتوانند Stampede بسازند. Targeted purge، Generation/Tag، Priority warmup، Rate control و Capacity monitoring کمریسکترند.
جمعبندی: Cache یک قرارداد صحت است
پرسش خوب «کش را روشن کنیم؟» نیست؛ این است: کدام Artifact برای کدام Identity، با چه Key، تا چه Freshness، زیر کدام Event و Failure mode، در کدام Layer و با چه شاهدی قابل Reuse است؟
با یک Artifact عمومی و پرتکرار شروع کنید. Contract، Key و Guardrail را بنویسید؛ Warm/Cold و دو Identity را تست کنید؛ Dependency invalidation و Recovery را اجرا؛ و فقط وقتی Correctness و Outcome پایدارند Coverage را افزایش دهید. Cache باید دوبارهکاری را حذف کند، نه شفافیت و اعتماد را.
منابع رسمی و مرجع
- IETF/RFC Editor: RFC 9111 HTTP Caching
- MDN: HTTP Caching
- MDN: Cache-Control header
- RFC ۵۸۶۱: stale-while-revalidate و stale-if-error
- IETF: RFC 9211 Cache-Status
- MDN: Service Worker Cache API
- Cloudflare: Cache keys
- Cloudflare: Cache response status
- Cloudflare: Cache Deception Armor
- AWS: Database caching patterns
- Redis: Cache-aside pattern
- Redis: Eviction policy و Metrics






