اگر یک مشتری سبد یا نام کاربر دیگری را ببیند، مسئله دیگر «کش قدیمی» نیست؛ یک رخداد امنیتی است. اگر فقط مدیر سایت نسخه تازه را ببیند اما مهمان هنوز HTML قبلی را بگیرد، احتمالاً دو نسخه با Cache key یا لایه متفاوت دارید. و اگر بعد از پاکسازی کامل، سرور زیر بار کند شود، Purge خودِ درمان نبوده؛ فقط منشأ را ناگهان با همه درخواستها روبهرو کرده است.
کش وردپرس یک دکمه یا افزونه نیست. زنجیرهای از Browser، CDN/Edge، Reverse proxy، Page cache، Object cache، Transient، PHP OPcache و Cache در APIهای وابسته است. راهحل پایدار با پنج سؤال ساخته میشود: چه چیزی قابلذخیره است، چه درخواستهایی باید یک پاسخ مشترک بگیرند، تازگی مجاز چقدر است، چه رویدادی نسخه را باطل میکند و از کجا میفهمیم پاسخ درست تحویل شده است؟
این راهنما برای مدیر WordPress، توسعهدهنده، تیم محتوا و فروشگاه ایرانی است. برای مبانی عمومی TTL، Fresh/Stale، Cache-Control و ETag ابتدا راهنمای «کش چیست و معماری Cache چگونه طراحی میشود» را ببینید؛ این مقاله مالک تنظیم، ایمنی و عیبیابی Cache در WordPress است.
پاسخ کوتاه: کش وردپرس چیست؟
WordPress برای تولید یک صفحه میتواند PHP اجرا کند، از Database بخواند، Hookهای قالب و افزونه را فراخوانی کند و HTML بسازد. Cache یک کپی قابلبازسازی از پاسخ یا نتیجه میانی را نگه میدارد تا درخواست بعدی کار کمتری انجام دهد. Cache نباید تنها نسخه Order، Payment، User profile یا Inventory باشد؛ Source of truth جای دیگری است.
| پرسش | پاسخ درست | برداشت پرریسک |
|---|---|---|
| هدف چیست؟ | کاهش Latency/Origin load در محدوده تازگی و صحت | بالاتر بردن Hit ratio به هر قیمت |
| Cache چه نگه میدارد؟ | نسخه قابل حذف و بازتولید | تنها کپی داده تجاری |
| چه کسی پاسخ را میگیرد؟ | فقط درخواستهای همارز طبق Cache key | هرکس همان URL را باز کرد |
| Purge چه میکند؟ | نسخه مشخص را Invalid یا حذف میکند | علت ریشهای را خودکار رفع میکند |
| موفقیت چیست؟ | پاسخ درست و تازه با هزینه کمتر | صرفاً نمره Performance یا HIT بیشتر |
مدل ذهنی کش: Request تا Regeneration
هر لایه Cache را با این جریان بخوانید:
- Eligibility: آیا Method، Status، مسیر و ماهیت پاسخ اجازه ذخیره میدهند؟
- Identity: Cache key از کدام بخشهای Scheme، Host، Path، Query، Header، Cookie، Language یا Device ساخته میشود؟
- Lookup: نسخهای برای این Key هست و هنوز Fresh است؟
- Serve/Revalidate: پاسخ مستقیم استفاده میشود، با Origin اعتبارسنجی میشود یا دوباره تولید میشود؟
- Invalidate: TTL، انتشار محتوا، تغییر وابستگی، Purge دستی یا Eviction چه زمانی آن را کنار میزند؟
- Observe: Hit/Miss/Age/Version و زمان Origin چگونه ثبت و بررسی میشوند؟
وقتی نسخه قدیمی میبینید، پاسخ یکی از این شش مرحله غلط است. این مدل از آزمون تصادفی تنظیمها مفیدتر است.
نقشه لایههای Cache در WordPress
| لایه | دارایی یا نتیجه | Key رایج | نشانه خرابی | مالک محتمل |
|---|---|---|---|---|
| Browser | CSS، JS، Font، Image و گاهی HTML | URL + Header | فقط یک Browser قدیمی است | Frontend/HTTP |
| Service Worker | Asset یا Response طبق کد برنامه | منطق Cache API | Incognito یا دستگاه دیگر متفاوت است | PWA/Frontend |
| CDN/Edge | Asset و در Cache-everything گاهی HTML | Host/Path/Query + Rule | کشور، اپراتور یا PoP متفاوت است | Infrastructure |
| Reverse proxy/Host | HTML یا پاسخ PHP در Nginx/Varnish/LiteSpeed | URL/Cookie/Header | Purge افزونه اثری ندارد | Host/DevOps |
| Page Cache plugin | HTML آماده برای Visitor | URL + Mobile/User/Cookie rule | مهمان قدیمی، مدیر تازه | WordPress admin |
| Object Cache | Object، Option و Query result | Key + Group + Blog | HTML تازه اما داده درونی قدیمی | Backend/Host |
| Transient | نتیجه موقت API/محاسبه/Markup | Transient name | Widget یا نرخ/Feed دیر تازه میشود | Plugin/Theme owner |
| PHP OPcache | Bytecode کامپایلشده PHP | File path/version | پس از Deploy رفتار کد قبلی میماند | Host/Release |
| Upstream API | پاسخ سرویس قیمت، ارسال یا Search | قرارداد Provider | همه لایههای WordPress تازهاند، داده نه | Integration owner |
دکمه «پاککردن کش» یک افزونه معمولاً همه این لایهها را کنترل نمیکند. همچنین فعال بودن چند Page cache مستقل لزوماً Performance را دو برابر نمیکند؛ ممکن است Ruleها و Purgeهای ناسازگار بسازد.
Cache، Session، Queue و Source of truth را قاطی نکنید
| جزء | اگر حذف شود چه باید شود؟ | نمونه WordPress/فروشگاه | ریسک اشتباه |
|---|---|---|---|
| Cache | با هزینه بیشتر بازسازی شود | HTML مقاله یا Query result | کندی موقت |
| Session | رفتار کاربر مختل میشود | شناسه سبد/ورود | اختلاط یا از دست رفتن Journey |
| Queue | کارهای در انتظار گم نشوند | ایمیل سفارش یا Preload job | کار انجامنشده/تکراری |
| Source of truth | نباید به Cache وابسته باشد | Order، Transaction، Stock | از دست رفتن یا تناقض تجاری |
Redis میتواند برای Cache، Session یا Queue به کار رود، اما این کاربردها سیاست Persistence، Eviction، Isolation و Recovery یکسانی ندارند. فرمان سراسری مانند FLUSHALL روی Instance مشترک ممکن است چیزی بیش از Cache وردپرس را حذف کند؛ Scope و مالکیت را پیش از هر Flush ثابت کنید.
قبل از نصب یا تنظیم افزونه، Baseline بگیرید
بدون Baseline نمیدانید Cache چه مشکلی را حل کرده یا چه خطایی ساخته است. «سایت حس میشود سریعتر است» شاهد کافی نیست. اثر را در کنار چارچوب سرعت سایت، UX و Outcome بسنجید.
| حوزه | شاهد پایه | تقسیم لازم | Guardrail |
|---|---|---|---|
| Latency | TTFB و زمان پاسخ Origin | URL type، HIT/MISS، Mobile/Desktop | p95/p99 بدتر نشود |
| Load | PHP worker، CPU، DB query/time | Warm/Cold و ساعت ترافیک | Queue و Error بالا نرود |
| Freshness | Publish→visible latency | HTML/Asset/Data/Region | از بودجه تازگی عبور نکند |
| Correctness | تست Persona/Cart/Price/Role | Guest/User/Admin و دو Session | هیچ پاسخ شخصی Share نشود |
| Resilience | Origin unavailable/cold test | Normal/Peak/Failure | 5xx و Recovery کنترل شود |
| Business | View→Cart→Payment→Kept | قبل/بعد و Release cohort | خطای پرداخت/موجودی رشد نکند |
Inventory: دقیقاً چه Cacheهایی فعالاند؟
یک جدول مالکیت بسازید؛ نام Plugin بهتنهایی کافی نیست.
| لایه/محصول | Scope | Rule source | Purge path | Evidence | Owner |
|---|---|---|---|---|---|
| Browser/CDN | Asset یا HTML | Header/Edge rule | URL/Tag/Prefix | Age و Provider status | … |
| Host cache | Public HTML | Panel/Nginx/Varnish | Panel/API | Response header/Log | … |
| WP Rocket | Page/optimization | Plugin + compatibility | Post/Domain | File/Plugin header | … |
| Object Cache | Key/group | object-cache.php | Key/Group/All | Hit/Miss/Eviction | … |
| Transient/API | Feature-specific | Plugin code | Event/TTL | Version/last refresh | … |
برای هر افزونه، مسئول نگهداری، Changelog، Compatibility، Rollback و هزینه عملیاتی را هم ثبت کنید. «رایگان» یا «محبوب» بودن جای این ارزیابی را نمیگیرد؛ راهنمای TCO و ریسک افزونه/قالب WordPress چارچوب انتخاب را تکمیل میکند.
قرارداد Cache برای هر نوع پاسخ
پیش از انتخاب TTL، برای هر Page/API type یک Cache contract بنویسید:
| فیلد قرارداد | پرسش | مثال مقاله | مثال Checkout |
|---|---|---|---|
| Eligibility | کدام Method/Status قابل ذخیره است؟ | GET/HEAD عمومی ۲۰۰ | پیشفرض Bypass |
| Cache key | چه Requestهایی همارزند؟ | Host + Canonical path | User/session-dependent |
| Freshness budget | حداکثر نسخه قدیمی قابلقبول؟ | بر پایه سیاست تحریریه | قیمت/موجودی بسیار محدود |
| Invalidation | چه Eventهایی کدام Keyها را پاک کنند؟ | Update post→post/home/archive | Cart/stock/payment event |
| Failure mode | در خطای Origin نسخه stale مجاز است؟ | شاید، طبق Rule | برای پاسخ شخصی/تراکنش خیر |
| Evidence | درستی چگونه دیده میشود؟ | Version marker + header | Session isolation + backend |
| Owner | چه کسی تغییر/Incident را پاسخ میدهد؟ | Content/Platform | Commerce/Payment |
Cache key: مرز میان سرعت و نشت داده
Cache key تعیین میکند کدام Requestها «یکسان» فرض شوند. Key بیشازحد جزئی، Entryهای فراوان و Hit کم میسازد؛ Key بیشازحد کلی، پاسخ اشتباه یا شخصی را میان کاربران Share میکند.
| بُعد | چه زمانی در Key باشد؟ | خطر حذف | خطر افزودن بیدلیل |
|---|---|---|---|
| Scheme/Host | اگر پاسخ واقعاً فرق دارد | Host contamination/Redirect اشتباه | Fragmentation |
| Path | تقریباً همیشه | صفحه اشتباه | — |
| Query | Parameter محتوایی لازم | Variant/Search اشتباه | UTM و Noise، Hit کم |
| Language | پاسخ Server-side متفاوت | زبان/جهت اشتباه | Variation زیاد Header |
| Device | HTML سرور واقعاً متفاوت | Markup ناسازگار | تقسیم Mobile/Desktop بیدلیل |
| Cookie/User | ترجیحاً Bypass پاسخ شخصی؛ در طراحی خاص Key جدا | نشت Cart/Profile/Nonce | Cache per-user کمارزش |
مستند Cache key در Cloudflare نشان میدهد Query، Header، Cookie، Host و ویژگی کاربر میتوانند رفتار Key را عوض کنند و دسترسی گزینهها به Plan وابسته است. این تنظیم محصول خاص است؛ Rule واقعی CDN خودتان را بخوانید و از روی نام گزینه حدس نزنید.
پارامترهای رهگیری را با پارامترهای محتوایی یکی نگیرید
utm_source معمولاً نباید نسخه HTML جدا بسازد؛ اما Parameter انتخاب Currency، Language یا Variant ممکن است محتوا را تغییر دهد. حذف همه Queryها از Key بدون Contract میتواند پاسخ یک Search، Filter یا Preview را به Request دیگر بدهد. ابتدا Parameter registry با Purpose، Output effect، Cache policy و Owner بسازید.
Responsive بودن معمولاً Cache موبایل جدا نمیخواهد
اگر Browser با CSS همان HTML را Responsive میکند، Device key فقط Hit ratio را تقسیم میکند. Cache موبایل جدا زمانی توجیه دارد که Origin واقعاً Markup متفاوت تولید کند؛ آنگاه Tablet، Bot، Header variation و QA دو نسخه نیز به هزینه سیستم اضافه میشوند.
پاسخ شخصی را به Shared cache نسپارید
وجود Cookie بهتنهایی پاسخ را Private نمیکند. راهنمای HTTP caching در MDN میان Private browser cache و Shared cache فرق میگذارد و برای محتوای شخصی private را توضیح میدهد. در WordPress علاوه بر Header، Rule واقعی CDN/Host/Plugin و آزمون دو Session مستقل لازم است.
هدرهای HTTP را بهعنوان قرارداد بخوانید
| Directive/Header | معنای عملی | خطای رایج |
|---|---|---|
Cache-Control: no-store | پاسخ ذخیره نشود | استفاده سراسری و از دستدادن Cache مفید |
private | Shared cache استفاده نکند؛ Private cache ممکن است | فرض اینکه Cookie خودکار همین کار را میکند |
no-cache | ذخیره ممکن است؛ پیش از reuse باید Validate شود | ترجمه به «اصلاً ذخیره نکن» |
max-age | Freshness برای Cache هدف طبق قرارداد HTTP | یک عدد برای HTML و Asset |
s-maxage | Freshness ویژه Shared cache | نادیدهگرفتن رفتار CDN خاص |
ETag/Last-Modified | Validator برای Conditional request | تصور اینکه بهتنهایی Purge میکنند |
Age | سن تقریبی پاسخ در Shared cache | تفسیر بدون شناخت لایه/Provider |
Vary | انتخاب پاسخ بر اساس Header مشخص | Vary: User-Agent و Fragmentation شدید |
RFC 9111 مرجع هنجاری Cache در HTTP است. Headerهای «آشپزخانهای» و متناقض را کورکورانه روی همه Routeها نگذارید؛ Contract باید با نوع Response و رفتار Managed cache هماهنگ باشد.
نسخهگذاری Asset از Purge مرورگر قابلاعتمادتر است
برای CSS/JS/Image تغییرپذیر، Filename یا Content hash جدید منتشر کنید و Assetهای Fingerprinted را با عمر بلند نگه دارید. URL جدید باعث میشود Browser و CDN نسخه تازه را مستقل از Cache قبلی بخواهند. Minify، Delay JS و Remove Unused CSS موضوع دیگریاند؛ آنها را با Page caching یکی نگیرید و مطابق راهنمای بهینهسازی CSS و JavaScript جدا تست کنید.
Page Cache در WordPress: HTML عمومی، نه همه Routeها
Page cache بیشترین منفعت را جایی دارد که کاربران ناشناس پاسخ یکسان میگیرند: مقاله، صفحه خدمات، Landing عمومی و Archive پایدار. پاسخهای زیر نیازمند Bypass یا Contract جدا هستند:
- Admin، Login، Preview، Draft و مسیرهای دارای Permission؛
- Account، Cart، Checkout و صفحه حاوی داده Session؛
- Callback/Webhook و عملیات تغییردهنده State؛
- REST/AJAX شخصی، Nonce و پاسخ وابسته به Role؛
- Search/Facet/Queryهایی که Key آنها تعریف نشده است؛
- Error و Redirectهایی که نباید طولانیمدت ذخیره شوند.
قانون «GET همیشه امن برای Cache است» کافی نیست؛ یک GET میتواند Profile شخصی یا Token داخل HTML داشته باشد. Eligibility را از Response semantics، Authentication و Data classification بسازید.
Object Cache و Redis: Query کمتر، مسئولیت بیشتر
طبق مستند WP_Object_Cache، Object Cache پیشفرض WordPress فقط در طول Request در حافظه میماند؛ Persistence به Drop-in/Backend جدا نیاز دارد. تعریف WP_CACHE بهتنهایی Persistent Object Cache را فعال نمیکند. افزونهها نیز باید از توابع wp_cache_* استفاده کنند، نه اینکه مستقیماً به کلاس داخلی متکی باشند.
| کنترل Redis/Object Cache | سؤال Acceptance | نشانه خطر |
|---|---|---|
| Namespace/Database | سایت و Environment جدا هستند؟ | Key collision میان Production/Staging |
| Memory limit | maxmemory و Headroom تعریف شده؟ | رشد بدون سقف یا OOM |
| Eviction policy | با الگوی Cache سازگار است؟ | Eviction زیاد/Write error |
| Observability | Hit/Miss، Evicted/Expired، Latency دیده میشوند؟ | فقط وضعیت Connected |
| Failure | قطع Redis Graceful degradation دارد؟ | کل سایت Down میشود |
| Flush scope | Key/Group/Site قابل هدفگیری است؟ | FLUSHALL بهعنوان Runbook عادی |
مستند رسمی Redis درباره Eviction تفاوت Policyها و شاخصهایی مانند keyspace_hits، keyspace_misses و evicted_keys را توضیح میدهد. Hit ratio بالا بدون Correctness ارزش ندارد؛ اما Hit پایین همراه Eviction زیاد میتواند نشانه Memory/Policy نامتناسب باشد.
Transient: زمان انقضا سقف است، نه قول ماندگاری
Transients API وردپرس تصریح میکند که Transient ممکن است پیش از زمان Expiration ناپدید شود و پس از آن نباید برگردد. با Persistent Object Cache نیز محل نگهداری میتواند از Database به Backend Cache منتقل شود. بنابراین:
- کد باید Cache miss و بازتولید را همیشه مدیریت کند؛
- برای داده حساس به تازگی، Event invalidation را کنار TTL داشته باشد؛
- Falsey value را از «وجود ندارد» درست تفکیک کند؛
- External API failure را با Timeout، Backoff و Last-known-good policy روشن مدیریت کند؛
- Transient را جای Order، Payment state یا Durable queue نگذارد.
PHP OPcache با HTML Cache فرق دارد
OPcache در PHP Bytecode کامپایلشده Script را نگه میدارد؛ Page cache نسخه HTML یک Response را. اگر پس از Deploy کد رفتار قبلی مانده اما HTML/CDN پاک شدهاند، Release process، Symlink/File timestamp، OPcache invalidation و PHP-FPM processها را بررسی کنید. Restart کور در ساعت اوج جای Runbook استقرار نیست.
WP Rocket: Cache، Purge و Preload سه وضعیت متفاوتاند
رفتار زیر مربوط به مستندات جاری WP Rocket در مرداد ۱۴۰۵ است و جای بررسی Version، Host compatibility و تنظیم واقعی سایت شما را نمیگیرد. مستند پاکسازی WP Rocket میگوید هنگام انتشار یا ویرایش محتوا، Cache همان محتوا و وابستگیهایی مانند Home و Taxonomy مرتبط میتواند پاک شود. پس هر Update لزوماً Full purge نیست.
| عملیات | چه میکند؟ | هزینه/ریسک | شاهد اتمام |
|---|---|---|---|
| Cache HIT | نسخه ساختهشده را تحویل میدهد | اگر Key/Rule غلط باشد پاسخ غلط سریعتر میرسد | Header/File + Version marker |
| Purge/Clear | Entry هدف را Invalid/حذف میکند | درخواست بعدی Cold است | MISS/فقدان File و سپس نسخه تازه |
| Preload/Warm | URL را برای بازسازی پیشاپیش Request میکند | CPU/DB/Queue مصرف میکند | Entry جدید + HIT بعدی |
| File optimization | CSS/JS را Transform یا زمان اجرا را عوض میکند | شکست ظاهر/Interaction | Asset و Task regression |
طبق مستند Preload WP Rocket، Preload با پاکسازی پیوند دارد، از Sitemap/URLهای شناختهشده Queue میسازد و در رفتار جاری تا ۴۵ URL در ۶۰ ثانیه پردازش میکند و هنگام فشار سرعت را کاهش میدهد. این سقف مشخصه محصول در زمان نگارش است، نه عدد جهانی برای همه Hostها. Cron، دسترسی عمومی Sitemap و Cache مدیریتی Host نیز بر نتیجه اثر دارند.
چرا پوشه Cache خالی است؟
ممکن است Host خودش Page caching را مدیریت کند و WP Rocket در آن محیط فایل HTML نسازد؛ ممکن است Request با Login، Query، Cookie یا Route مستثنا باشد؛ یا Preload/Cron هنوز کار نکرده باشد. ابتدا Contract Hosting و Header را بخوانید، سپس سراغ File system بروید.
استثنا را فقط در یک لایه ثبت نکنید
راهنمای Exclusion در WP Rocket نشان میدهد Hostهای مدیریتشدهای وجود دارند که استثنای Page cache باید در لایه Host اعمال شود. اگر Checkout را در Plugin مستثنا اما CDN آن را Cache کنید، سیاست انتهایی هنوز ناامن است. Ruleها را Edge→Host→Plugin→Application همراستا کنید.
TTL را از Freshness budget بسازید، نه از عدد پیشنهادی Blogها
| نوع محتوا | رویداد تغییر | بودجه تازگی نمونه | مکانیزم اصلی | Fallback |
|---|---|---|---|---|
| مقاله پایدار | Publish/Update | طبق SLA تحریریه | Event purge + TTL محافظ | Revalidate |
| صفحه اصلی/Archive | تغییر عضو وابسته | کوتاهتر از مقاله | Dependency purge | TTL |
| قیمت/موجودی | ERP/Order/Manual update | بر پایه ریسک Oversell | Event + API contract | کوتاه/Bypass |
| CSS/JS fingerprinted | URL جدید در Deploy | عمر بلند | Content hash | Rollback manifest |
| Account/Checkout | هر Request/Session | شخصی و جاری | Shared-cache bypass | private/no-store طبق پاسخ |
TTL بلند با Invalidation دقیق میتواند هم سریع و هم تازه باشد؛ TTL کوتاه بدون Dependency map فقط Miss بیشتری میسازد. برعکس، Event purge ممکن است Fail شود، بنابراین TTL محافظ و مانیتور Publish→visible هنوز ارزش دارند.
Invalidation را بهصورت Graph وابستگی طراحی کنید
ویرایش یک نوشته فقط URL نوشته را تغییر نمیدهد. کارت همان نوشته ممکن است در Home، Category، Tag، Author archive، Related content، Sitemap یا Feed دیده شود. «Purge همه» Dependency را پنهان میکند؛ «Purge فقط نوشته» نسخههای قدیمی پراکنده میسازد.
| Event | Targetهای محتمل | روش کمریسک | Acceptance |
|---|---|---|---|
| Update post | Post + Home + Archiveهای عضو + Related | Tag/URL dependency purge | Version تازه در همه Surfaceها |
| تغییر Menu | تمام Templateهای دارای Header | Template/tag purge یا Controlled full purge | Nav تازه Guest/Mobile |
| تغییر Price/Stock | PDP + Category + Search + API | Product/SKU event fan-out | Page/API/Cart parity |
| Deploy CSS/JS | HTML manifest/reference | Versioned asset + HTML purge | هیچ ۴۰۴/Mixed version نیست |
| Plugin/config change | Scope نامعلوم/Template-wide | Staging، سپس Scope مستند | Smoke + rollback |
Version marker، بحث «برای من تازه است» را پایان میدهد
برای Release یا محتوای حساس یک Marker غیرشخصی در Header یا HTML بگذارید: Build ID، Content version یا Updated timestamp. سپس همان Marker را از چند Persona و Region بخوانید. Timestamp روی Screen که با JavaScript محلی ساخته شده، شاهد نسخه HTML Origin نیست.
Purge باید Idempotent، قابل تکرار و قابل مشاهده باشد
Event انتشار ممکن است Retry شود. Handler پاکسازی نباید با اجرای دوباره داده خراب کند. Event ID، Target count، زمان Queue، نتیجه Provider و خطا را ثبت کنید؛ اگر Purge API شکست خورد، Retry با Backoff و Alarm داشته باشید.
از Cache stampede پس از Purge جلوگیری کنید
وقتی Entry پرتقاضا همزمان برای هزار Request منقضی شود، همه میتوانند برای بازسازی به PHP/Database حمله کنند. Full purge پیش از کمپین نیز همین اثر را در مقیاس سایت میسازد.
| کنترل | اثر | محدودیت |
|---|---|---|
| Request collapsing/Lock | یک Worker بازسازی؛ بقیه منتظر | پشتیبانی لایه و Timeout درست لازم است |
| TTL jitter | انقضای Entryها پخش میشود | برای Freshness سخت مناسب هر داده نیست |
| Stale-while-revalidate | نسخه stale هنگام بازسازی سرو میشود | فقط در بودجه تازگی و محصول پشتیبان |
| Stale-if-error | در خطای Origin محتوای قبلی میماند | برای Account/Transaction نامناسب |
| Priority preload | Routeهای حیاتی زودتر Warm میشوند | Queue و Origin capacity لازم است |
| Rate-limited rollout | موج Cold کنترل میشود | Runbook و Monitoring میخواهد |
قابلیت دقیق هر کنترل به CDN، Reverse proxy و Plugin بستگی دارد. نام Directive را مساوی اجرای واقعی ندانید؛ Failover را با Load کنترلشده و طرح مانیتورینگ Uptime و Performance آزمایش کنید.
کش فروشگاه WooCommerce: سرعت زیر Guardrail صحت
مستند جاری WooCommerce برای Cache، Cart، My Account و Checkout را Dynamic میداند و Cookieهایی مانند woocommerce_cart_hash، woocommerce_items_in_cart و wp_woocommerce_session_ را در سیاست Cache مطرح میکند. بسیاری از Pluginها استثناهای پیشفرض دارند؛ پیشفرض را با مسیر، زبان، افزونه درگاه و دو Session واقعی تأیید کنید.
| Surface | پیشفرض تصمیم | وابستگی | تست حیاتی |
|---|---|---|---|
| Category عمومی | قابل Cache با Purge دقیق | Price/Stock/Sort/Facet | لیست با PDP و Cart برابر است |
| PDP عمومی | قابل Cache مشروط | Variant/Price/Stock/Promotion | دو Browser و آخرین موجودی |
| Cart | Shared bypass | Session/Coupon/Shipping | سبد A هرگز در B نیست |
| Checkout | Shared bypass | User/Address/Nonce/Payment | Guest/User و Retry |
| My Account | Shared bypass | Identity/Order/Profile | Logout/Login و Back button |
| Callback/Webhook | Bypass؛ عملیات Idempotent | Transaction/order state | Success/Fail/Cancel/Duplicate |
برای فروشگاه ایرانی، تست فقط «صفحه پرداخت باز شد» نیست. Journey افزودن کالا، Coupon، انتخاب شهر و روش ارسال، ورود/خروج، انتقال به درگاه، بازگشت موفق/ناموفق/لغوشده، Callback تکراری، تأخیر Webhook، تغییر موجودی و Refund را پوشش دهید. راهنمای UX فروشگاه Taskهای خرید را تکمیل میکند.
بازگشت Browser از درگاه، حقیقت نهایی پرداخت نیست
Route بازگشت را Cache نکنید و State پرداخت را صرفاً از Query کاربر نپذیرید. Verify server-to-server، Idempotency، Correlation با Order و Reconciliation لازماند. طراحی قرارداد Callback/Webhook در راهنمای API، امنیت و قابلیت اطمینان عمیقتر آمده است.
نمایش پاسخ شخصی به کاربر دیگر Incident است
فوراً لایه Shared cache را برای Surface متاثر Bypass/Purge، Evidence شامل Header/Request ID/زمان/Persona را حفظ، دامنه کاربران و داده افشاشده را بررسی و Incident process را فعال کنید. فقط Clear cache و بستن Ticket کافی نیست. اعتماد، اطلاعرسانی و جبران باید با سیستم شواهد و Remedy فروشگاه هماهنگ باشد.
Web Cache Deception و Cache poisoning را در Threat model بگذارید
Ruleهای «Cache everything» یا اعتماد به پسوند URL میتوانند Response پویا را قابلذخیره کنند. مستند Cache Deception Armor نمونهای را توضیح میدهد که Route شخصی با پسوند ظاهراً استاتیک مانند .jpg پاسخ HTML میدهد و ممکن است اشتباه Cache شود. کنترل محصول خاص مفید است اما جای این موارد را نمیگیرد:
- Route شخصی با پسوند یا Path اضافه نباید همان Response موفق را برگرداند؛
Content-Type، Status و Extension باید سازگار باشند؛- Host، Query و Header کنترلنشده نباید Key یا Response را مسموم کنند؛
- Admin/Login/API/Preview و پاسخهای دارای Token از Shared cache خارج باشند؛
- آزمون Cache باید با دو Identity و Request دستکاریشده انجام شود.
فرآیند عیبیابی نسخه قدیمی در وردپرس
۱. Symptom را به Artifact تبدیل کنید
URL نهایی، زمان تغییر، Expected value، Actual value، Persona، Login state، Browser، Device، Network/Region و Screenshot را ثبت کنید. مشخص کنید قدیمی چیست: HTML، CSS، JS، Image، Price، Menu، API JSON یا داده Dashboard.
۲. URL نهایی و Redirect chain را ثابت کنید
گاهی تیم URL قدیمی را Purge میکند اما کاربر پس از ۳۰۱ به Canonical دیگری میرسد. HTTP/HTTPS، www/non-www، Slash، Query و زبان را ثبت کنید. خطا را روی Destination واقعی بازتولید کنید، نه فقط آدرس تایپشده.
۳. Browser را از Shared cache جدا کنید
Incognito فقط Cookie/Storage معمول آن Profile را تا حدی جدا میکند؛ اثبات نمیکند CDN یا Host تازه است. یک Device/Network دیگر و یک Request بدون Session بگیرید. اگر فقط یک Browser مشکل دارد، Asset cache، Service Worker و Local storage را بررسی کنید.
۴. Source، DOM و Data را جدا مقایسه کنید
| مشاهده | لایه محتمل | بررسی بعدی |
|---|---|---|
| Source قدیمی است | HTML در Edge/Host/Plugin | Age/HIT، Version marker، Origin bypass |
| Source تازه، DOM قدیمی | JS/Service Worker/Hydration | Network/Console/Runtime state |
| HTML تازه، CSS قدیمی | Browser/CDN asset | Asset URL/hash/header |
| Shell تازه، Data قدیمی | Object/Transient/API | API response، Key و last refresh |
| Guest قدیمی، Admin تازه | Public Page cache | Cookie bypass و دو Request |
| فقط یک Region قدیمی | CDN PoP/Regional origin | Provider status/purge propagation |
۵. Header و Body را با هم بگیرید
یک HEAD لزوماً دقیقاً همان مسیر Cache یا تولید GET را طی نمیکند. برای Headerهای پاسخ GET بدون ذخیره Body میتوانید از الگوی زیر استفاده کنید:
curl -sS -D - -o /dev/null 'https://example.com/final-url/'
curl -sS -D - -o /dev/null 'https://example.com/final-url/'دو درخواست پیاپی را برای Cache-Control، Age، ETag، Vary، Set-Cookie و Headerهای Provider مانند HIT/MISS/BYPASS مقایسه کنید. نام Header و معنای Status به محصول بستگی دارد. Header را همراه یک Marker داخل Body و Log Origin تفسیر کنید.
۶. کوچکترین لایه اثباتشده را Purge کنید
اگر یک Asset قدیمی است، URL همان Asset؛ اگر HTML یک Post قدیمی است، Post و وابستگیهای تعریفشده؛ اگر Object مشخص است، Key/Group پشتیبانیشده. Full purge، Redis flush و Restart آخرین Escalationهای کنترلشدهاند، نه قدم اول.
۷. بعد از Purge، علت و پیشگیری را ثابت کنید
حل شدن پس از Clear فقط میگوید نسخهای Cached بوده؛ نمیگوید چرا Invalidation شکست خورد. Event log، Purge target، Queue، Provider response، Dependency map و زمان Visible شدن را ثبت کنید. سپس همان سناریو را یک بار دیگر بدون مداخله دستی تکرار کنید.
ماتریس نشانه، فرضیه و آزمون ردکننده
| نشانه | فرضیه اول | آزمون ردکننده | اقدام پس از اثبات |
|---|---|---|---|
| فقط یک کاربر قدیمی | Browser/Service Worker | Device و Profile دیگر | Asset version/update SW |
| Guest قدیمی، Login تازه | Page cache عمومی | Origin/Cache bypass مجاز | Target purge و Rule fix |
| Purge افزونه بیاثر | Host/CDN بالادست | Provider header و Origin comparison | Purge همان لایه/هماهنگی Hook |
| قیمت قدیمی، متن تازه | Object/Transient/API | Data source مستقیم | Event invalidation/TTL fix |
| بعد Purge موج 5xx | Stampede/Origin capacity | Cold vs Warm load | Lock/Warm/rate limit/capacity |
| سبد فرد دیگر | Shared personalized cache | دو Session با Marker مستقل | Incident، Bypass، Scope review |
| Deploy دیده نمیشود | OPcache/Old pod/Asset manifest | Build ID در Nodeها | Atomic deploy/invalidation |
تست ایمنی Cache: دو Session از یک Browser مهمترند
تست «صفحه برای من باز شد» فقط Happy path یک Identity است. ماتریس باید پاسخ مشترک و پاسخ شخصی را در حالت Warm و Cold تفکیک کند.
| محور | حالتها | Assertion |
|---|---|---|
| Identity | Guest A، Guest B، User A، User B، Admin | هیچ Marker شخصی بین Identityها جابهجا نمیشود |
| Session | دو Browser/Profile مستقل | Cart، Nonce و Account جدا میمانند |
| Cache state | Cold، MISS، HIT، پس از Purge | Body و Header با Contract سازگارند |
| Device | Mobile/Desktop/Tablet | نسخه اشتباه یا Fragmentation بیدلیل نیست |
| Locale | فارسی/زبان دوم، RTL/LTR | Language/Direction درست است |
| Query | بدون Query، UTM، Search، Facet، Variant | Parameter policy اجرا میشود |
| Region/Network | PoP/اپراتورهای منتخب | Purge propagation در SLA است |
| Failure | Origin slow/down، Redis down، Queue delayed | Failure mode و Alarm مطابق قرارداد است |
تست منفی از تست مثبت مهمتر است
- پاسخ Account با Cookie کاربر A را بگیرید؛ همان URL را با B و بدون Cookie Request کنید و عدم وجود Marker A را Assert کنید.
- URL پویا را با پسوند ساختگی، Slash اضافه، Host و Query دستکاریشده آزمون کنید؛ Response شخصی نباید Cacheable ۲۰۰ شود.
- پس از Logout، Back/Reload و URL مستقیم را تست کنید؛ داده حساس نباید از Shared cache برگردد.
- برای Errorها، ۴۰۴/۴۲۹/۵۰۰ و Redirect موقت را بررسی کنید؛ TTL ناخواسته Incident را طولانی نکند.
- در دو Request پیاپی، Header و Version marker را با هم Assert کنید؛ فقط Status ۲۰۰ کافی نیست.
آزمون Warm و Cold را جدا گزارش کنید
| سناریو | چه چیزی میسنجد؟ | خطای تفسیر |
|---|---|---|
| Warm HIT | هزینه Delivery از Cache | تعمیم به کاربر اول/پس از Purge |
| Cold MISS | ظرفیت PHP/DB و زمان Regeneration | مقایسه مستقیم با HIT |
| Mixed traffic | نسبت واقعی URLهای Cacheable/Dynamic | Load test فقط Homepage |
| Purge burst | Stampede، Queue و Recovery | تست بدون Rate واقعی |
| Dependency update | تازگی همه Surfaceهای وابسته | بررسی فقط URL ویرایششده |
| Origin failure | Stale/fail-closed و Alarm | فرض خودکار CDN |
Load test را روی Production بدون هماهنگی و سقف اجرا نکنید. Dataset، نرخ، مدت، IP، زمان و Abort threshold باید از پیش معلوم باشند. هدف شکستن سایت نیست؛ پیدا کردن نقطهای است که Latency/Error/Queue از Guardrail میگذرند.
انتشار تغییرات Cache بدون شوک
- Change brief: Route/Layer/Expected effect/Risk/Owner/Rollback را ثبت کنید.
- Staging parity: CDN/Host/Object cache و Cookie rule تا حد ممکن شبیه Production باشند.
- Baseline: Warm/Cold، Header، Task و Business guardrail را ذخیره کنید.
- Canary: Rule یا Release را روی Scope کوچک/URL نمونه اجرا کنید.
- Targeted invalidation: Dependencyهای متاثر را Purge کنید.
- Controlled warmup: URLهای حیاتی را با Priority و Rate متناسب Warm کنید.
- Verification: Guest/User/Admin، دو Session، Mobile و Region را تست کنید.
- Observe: Origin load، Error، Purge latency، Freshness و Transaction را تا تثبیت ببینید.
- Rollback: Rule/Plugin/Build را برگردانید و دوباره Cache state را همگرا کنید.
Deploy اتمیک، Mixed asset را کم میکند
ابتدا Assetهای Versioned و Backward-compatible را منتشر کنید، سپس HTML را به URLهای جدید اشاره دهید. پاککردن Asset قبلی پیش از پایان عمر HTML قبلی میتواند ۴۰۴ بسازد. Rollback نیز باید Manifest نسخه قبلی را در دسترس داشته باشد.
پیش از کمپین فروش Full purge نکنید
Cache cold، Preload queue و بازدید کمپین میتوانند همزمان به Origin برسند. تغییر بزرگ را پیشتر Rollout، مسیرهای حیاتی را Warm، و Capacity را با Headroom تأیید کنید. Freeze window برای تنظیم Cache/Plugin در ساعات حساس تعریف کنید.
Observability: از HIT تا Outcome
Hit ratio فقط یک Diagnostic است. یک سیستم میتواند HIT بالا و قیمت غلط داشته باشد یا HIT پایین اما Journey سالم. داشبورد تصمیم باید چهار لایه را وصل کند:
| لایه | شاخصها | تقسیم | تصمیم |
|---|---|---|---|
| Delivery | HIT/MISS/BYPASS، Age، bytes | URL type/PoP/Device | Key/Rule/TTL |
| Origin | RPS، PHP workers، DB time، TTFB p95 | Cold/Warm/Endpoint | Capacity/Query/Preload |
| Cache store | Memory، Hit/Miss، Eviction، Latency | Group/Instance/Site | Policy/Namespace/Limit |
| Correctness | Publish→visible، parity، leakage test | Surface/Persona/Region | Invalidation/Incident |
| Journey | Search→PDP→Cart→Pay→Kept | Release/Cohort/Device | Rollback/UX fix |
برای هر Purge یا Deploy یک Correlation ID میان Event، Queue، CDN response و Test evidence نگه دارید. Alert بدون Runbook فقط Noise است؛ هر Alert باید Owner، Severity، اولین بررسی و Escalation داشته باشد.
SLO پیشنهادی: تازگی و صحت را کنار سرعت بگذارید
| SLO/Guardrail | تعریف نمونه | منبع شاهد |
|---|---|---|
| Freshness | درصد Updateهای قابل مشاهده زیر بودجه هر Page type | CMS event + Probe |
| Correctness | صفر Leakage در Synthetic cross-session tests | Test suite/Incident |
| Availability | Successful response برای Journey حیاتی | Probe + Backend |
| Latency | p75/p95 Warm و Cold جدا | RUM/APM/Edge |
| Purge | Propagation کامل Targetها در SLA | Provider log + Marker |
| Recovery | زمان همگرایی پس از Rollback/Cold event | Incident timeline |
عددها باید از ریسک کسبوکار و Baseline خود سایت تعیین شوند. مقاله پزشکی، موجودی لحظهای و Landing ثابت Freshness یکسان ندارند. از Benchmark عمومی بهعنوان Context استفاده کنید، نه SLA آماده.
سه مثال برای سایتهای ایرانی
مجله محتوایی با WP Rocket و CDN
مقالهها عمومی و Cacheable هستند؛ Update هر مقاله Post، Home و Categoryهای عضو را Purge میکند. Assetها Fingerprinted و طولانیعمرند. Preload ابتدا Home و مقالههای پرترافیک را Warm میکند. Probe از دو شبکه Marker نسخه را میخواند. Full purge فقط در تغییر Template سراسری و با Warmup کنترلشده انجام میشود.
فروشگاه WooCommerce در کمپین
Category/PDP عمومی Cache میشوند، اما Cart/Checkout/Account/Callback Shared bypass هستند. تغییر Stock بر اساس SKU، PDP و Listing وابسته را Invalid میکند؛ Cart و Backend دوباره قیمت/موجودی را تأیید میکنند. پیش از کمپین، دو Session مستقل، Coupon، ارسال شهرهای منتخب، درگاه موفق/ناموفق، Callback تکراری و Cache cold تست میشوند.
سامانه عضویت یا رزرو
Landing عمومی Cacheable است؛ Dashboard، ظرفیت شخصی، Profile و Routeهای دارای Nonce خارج از Shared cache هستند. API ظرفیت میتواند Contract کوتاهعمر داشته باشد، اما تأیید رزرو با Transaction/Lock در Source of truth انجام میشود. نمایش ظرفیت Cached هرگز جای کنترل نهایی هنگام ثبت را نمیگیرد.
اشتباههای رایج در تنظیم کش وردپرس
| اشتباه | چرا شکست میخورد؟ | جایگزین |
|---|---|---|
| نصب دو Page cache بدون مالک | Rule و Purge ناسازگار | یک مسئول Delivery و Inventory لایهها |
| Full purge پس از هر ویرایش | Cold load/Stampede | Dependency-based purge |
| TTL یکسان برای همه | ریسک و تغییر متفاوتاند | Freshness budget هر نوع پاسخ |
| Cache کاربران واردشده برای HIT | Fragmentation یا Leakage | Bypass؛ فقط Design مستند و تستشده |
| نادیده گرفتن CDN/Host | Purge Plugin کافی نیست | End-to-end rule alignment |
| Redis برای حل Query بد | علت را پنهان و عملیات را پیچیده میکند | Profile Query + Cache بر اساس شاهد |
FLUSHALL در Runbook عادی | Scope بزرگ/سرویس مشترک | Key/Group/Site flush پشتیبانیشده |
| Hard refresh بهعنوان Verification | فقط بخشی از Browser را Bypass میکند | Header+Marker+Origin/Edge evidence |
| بهینهسازی JS همزمان با Cache | علت شکست مبهم میشود | یک متغیر، Acceptance و Rollback |
| تعقیب Hit ratio بدون صحت | پاسخ غلط را سریعتر میکند | Correctness/Freshness guardrails |
برنامه ۳۰/۶۰/۹۰ روزه Cache وردپرس
روز ۱ تا ۳۰: کشف و مهار ریسک
- Inventory کامل Browser/CDN/Host/Plugin/Object/Transient/OPcache و Owner؛
- فهرست Routeهای عمومی، شخصی، تراکنشی و API؛
- تأیید فوری Bypass برای Login/Account/Cart/Checkout/Callback؛
- Baseline Warm/Cold، Header، Origin load و Publish→visible؛
- Cross-session test و Incident path برای Leakage.
روز ۳۱ تا ۶۰: قرارداد و اتوماسیون
- Cache contract برای Page/API type و Parameter registry؛
- Dependency map و Targeted purge با Event log؛
- Versioned asset و Atomic deployment؛
- Redis namespace/memory/eviction/alert و Failure test؛
- Automated assertions برای Header، Marker، Persona و Transaction.
روز ۶۱ تا ۹۰: تابآوری و یادگیری
- Controlled cache-cold و Origin-failure exercise؛
- Priority warmup، Rate limit و Stampede controls؛
- SLO تازگی/صحت/Latency/Purge/Recovery؛
- Canary و Rollback rehearsal پیش از کمپین؛
- بازبینی Incident، حذف Cache بیارزش و اصلاح Runbook.
چکلیست کیفیت تنظیم Cache وردپرس
- Cache را کپی قابلبازسازی میدانیم، نه Source of truth.
- همه لایهها، Rule source، Purge path، Evidence و Owner ثبت شدهاند.
- برای هر Page/API type، Eligibility و Cache key روشن است.
- Query/Cookie/Language/Device فقط در صورت اثر واقعی وارد Key میشوند.
- Account، Cart، Checkout، Preview، Callback و پاسخ شخصی Shared bypass هستند.
no-store،privateوno-cacheدرست تفکیک شدهاند.- TTL از Freshness budget میآید و Event invalidation دارد.
- Dependency map Post/Product/Template ساخته شده است.
- Purge هدفمند، Idempotent، Logged و Retryable است.
- Preload Priority/Rate و ظرفیت Origin هماهنگاند.
- Assetهای تغییرپذیر Versioned/Fingerprinted هستند.
- Redis Memory/Eviction/Latency/Failure/Flush scope مانیتور میشود.
- Transient miss و External API failure قابل مدیریتاند.
- Deploy و OPcache invalidation Runbook و Rollback دارند.
- دو Session و چند Persona/Device/Region تست میشوند.
- Warm، Cold، Purge burst و Origin failure جدا سنجیده میشوند.
- Version marker، Header و Log به یک Correlation ID متصلاند.
- Correctness/Freshness/Business guardrail بر Hit ratio مقدماند.
پرسشهای متداول کش وردپرس
چرا بعد از ویرایش هنوز نسخه قدیمی WordPress را میبینم؟
ابتدا Artifact را مشخص کنید: HTML، CSS/JS، تصویر یا داده. سپس URL نهایی، Persona و Region را ثبت، Source/DOM/API را جدا و Headerهایی مانند Age/HIT را همراه Version marker بررسی کنید. فقط لایه اثباتشده و Dependencyهایش را Purge کنید؛ حل شدن با Full clear علت شکست Invalidation را مشخص نمیکند.
آیا بعد از هر تغییر باید کل کش را پاک کنیم؟
خیر. Update نوشته معمولاً همان URL و Surfaceهای وابسته مانند Home/Category را نیاز دارد. Full purge همه Entryها را Cold میکند و میتواند موج PHP/DB بسازد. تغییر Template سراسری شاید Scope بزرگ بخواهد، اما باید با Warmup، Monitoring و Rollback انجام شود.
آیا Redis برای هر سایت WordPress لازم است؟
نه. Persistent Object Cache وقتی ارزش دارد که Profiling نشان دهد Query/Option تکراری گلوگاه است و تیم بتواند Memory، Eviction، Isolation و Failure را اداره کند. سایت کوچک با Page cache سالم ممکن است سود محدودی بگیرد. Redis افزونه یا Query معیوب را اصلاح نمیکند.
آیا WP Rocket خودکار Cart و Checkout را مستثنا میکند؟
مستند جاری WP Rocket برای Ecommerce استثناهای خودکار برای Pluginهای پشتیبانیشده را توضیح میدهد؛ اما مسیرهای ترجمهشده، افزونه درگاه، Host cache و CDN لایههای جدا هستند. Cart، Checkout، Account، Cookie و Callback را با دو Session و خرید آزمایشی تأیید کنید.
Clear Cache و Preload چه تفاوتی دارند؟
Clear/Purge نسخه ذخیرهشده را Invalid یا حذف میکند؛ Preload سپس URL را پیشاپیش Request میکند تا Entry دوباره ساخته شود. Preload مصرف CPU/DB/Queue دارد و باید با Priority، Rate و ظرفیت Origin هماهنگ شود. پاکسازی موفق بهتنهایی به معنی Warm شدن یا رفع علت نیست.
جمعبندی: پاسخ درست، تازه و قابلاثبات
کش وردپرس خوب صرفاً TTFB را پایین نمیآورد؛ درخواستهای واقعاً همارز را به پاسخ درست، در بودجه تازگی و با هزینه کمتر وصل میکند. Cache key غلط میتواند داده شخصی را Share کند، Invalidation ناقص نسخههای پراکنده بسازد و Full purge بیبرنامه Origin را زیر بار ببرد.
از یک Route مهم شروع کنید: Contract آن را بنویسید، Header و Marker بگیرید، دو Session را تست کنید، Dependency purge را اجرا و Warm/Cold را جدا بسنجید. سپس همین الگو را به Page typeهای دیگر گسترش دهید. وقتی Owner، Evidence، Guardrail و Rollback دارید، Clear cache از واکنش حدسی به یک عملیات کنترلشده تبدیل میشود.
منابع رسمی و مرجع
- WordPress Developer Resources: WP_Object_Cache و Persistence
- WordPress Developer Resources: Transients API و Expiration
- PHP Manual: OPcache
- WP Rocket: زمان و Scope پاکسازی Cache
- WP Rocket: Preload Cache
- WP Rocket: Exclude pages از Cache و Optimization
- WooCommerce Developer Docs: تنظیم Cache و Cookieهای فروشگاه
- MDN: HTTP Caching، Private/Shared، Vary و Validation
- HTTP Working Group: RFC 9111 HTTP Caching
- Redis: Key Eviction، Memory و Hit/Miss
- Cloudflare: Cache Key settings و Query/Header/Cookie
- Cloudflare: Web Cache Deception و Deception Armor






