کاهش پهنای باند و هزینه سرور با CDN: راهنمای عملی
چطور با بالا بردن نرخ hit کش، بهینهسازی تصاویر و فشردهسازی درست، کاهش پهنای باند و کاهش هزینه سرور را در عمل به دست بیاورید — بدون تغییر در کد سایت.
هزینه پهنای باند معمولاً وقتی به چشم میآید که فاکتور ماهانه بالا رفته، نه وقتی که دارد ساخته میشود. در بیشتر سایتها بخش بزرگی از ترافیک خروجی صرف بایتهایی میشود که یا اصلاً نباید از سرور اصلی میآمدند، یا نباید اینقدر بزرگ میبودند. این مقاله همان بایتها را ردیابی میکند و برای هر تاکتیک میگوید چقدر کار میبرد و چقدر جواب میدهد.
پهنای باند واقعاً کجا مصرف میشود؟
پیش از هر بهینهسازی باید بدانید هر بایت از کجا میآید. اگر با حدس جلو بروید، وقتتان را روی سهم کوچکی از فاکتور میگذارید.
تصویر و ویدئو
در تقریباً هر سایتی، تصویر و ویدئو بزرگترین سهم ترافیک خروجی را دارند و معمولاً با فاصله زیاد از بقیه. یک تصویر شاخص که مستقیم از خروجی دوربین آپلود شده و مرورگر آن را در عرض ۴۰۰ پیکسل نمایش میدهد، چندین برابر آنچه لازم است بایت میسوزاند. ویدئوی با پخش خودکار (autoplay) در هدر صفحه بدترین حالت است: برای هر بازدید، بدون اینکه کاربر خواسته باشد، مگابایتها منتقل میشود.
باندلهای JavaScript
بعد از مدیا، نوبت جاوااسکریپت است: فریمورک، کتابخانه تاریخ شمسی، اسلایدر، و سه اسکریپت تحلیلی که دیگر کسی یادش نیست چرا اضافه شدهاند. برخلاف تصویر، JS هم پهنای باند میگیرد و هم زمان CPU دستگاه کاربر را — یعنی همزمان به فاکتور شما و به Core Web Vitals ضربه میزند.
فونت و بقیه
فونتهای فارسی سنگیناند، مخصوصاً وقتی چهار وزن مختلف را همزمان در قالب WOFF2 و WOFF و گاهی TTF بارگذاری میکنید. بعد از آنها CSS و پاسخهای API میمانند: کوچک ولی پرتکرار.
مکانیزم اصلی: تخلیه بار از مبدأ
کل بحث کاهش هزینه سرور به یک جمله برمیگردد: هر درخواستی که لبه (edge) پاسخ میدهد، درخواستی است که سرور شما هرگز نمیبیند — نه پهنای باندی مصرف میکند، نه پردازشی، نه اتصال دیتابیسی.
نکته اینجاست که بار مبدأ را نرخ hit تعیین نمیکند، نرخ miss تعیین میکند. فرض کنید نرخ hit را از ۷۰ درصد به ۹۰ درصد میرسانید؛ نرخ miss از ۳۰ درصد به ۱۰ درصد میرود، یعنی ترافیک رسیده به مبدأ یکسوم میشود، نه ۲۰ درصد کمتر. به همین دلیل آخرین درصدهای نرخ hit بیشترین ارزش را دارند. سازوکار دقیق ذخیره و انقضای پاسخها را در نحوه کار کش در CDN باز کردهایم.
در nsin ترافیک در سه ردیف شمرده میشود و نرخ هر گیگابایت در هر ردیف فرق دارد:
| ردیف ترافیک | چه چیزی در آن شمرده میشود |
|---|---|
| Cached | از کش لبه سرو شده، به مبدأ نرسیده |
| Proxied | از لبه عبور کرده و به مبدأ رسیده |
| Direct | از مسیر پروکسی لبه رد نمیشود |
نرخ هر گیگابایت در هر ردیف جداگانه و به تومان اعلام میشود؛ عدد بهروز را در صفحه تعرفهها و پنل ببینید.
پس نرخ hit فقط یک عدد روی داشبورد نیست؛ تعیین میکند ترافیک شما با کدام نرخ حساب شود. به همین دلیل مدل قیمتگذاری و رفتار سرویس در زمان تمامشدن سهمیه، از معیارهای اصلی انتخاب CDN است. هر درخواستی که از ردیف proxied به cached میرود، دو بار به سود شماست: هم بار مبدأ و ترافیک خروجی سرور شما کم میشود، هم آن گیگابایت به ردیفی منتقل میشود که با تعرفه خودش حساب میشود.
تصاویر: بیشترین برد با کمترین ریسک
سه کار، به ترتیب اثر:
ابعاد درست. تصویر ۳۰۰۰ پیکسلی را برای جایگاهی که حداکثر ۸۰۰ پیکسل عرض دارد سرو نکنید. با srcset و sizes چند نسخه در اختیار مرورگر بگذارید تا خودش مناسبترین را بردارد؛ این کار میتواند حجم صفحه را به یکچندم برساند، بدون اینکه کاربر تفاوتی ببیند.
فرمت مدرن. WebP و AVIF در کیفیت بصری برابر، بایت بهمراتب کمتری از JPEG و PNG میگیرند. AVIF فشردهتر است ولی انکود کندتری دارد؛ WebP نقطه تعادل خوبی است.
lazy loading. روی هر تصویری که در نمای اولیه صفحه نیست loading="lazy" بگذارید؛ تصویری که کاربر هرگز به آن نمیرسد نباید دانلود شود. فقط تصویر LCP را lazy نکنید، چون اثر معکوس دارد.
فشردهسازی: gzip یا brotli؟
هر دو محتوای متنی را پیش از انتقال فشرده میکنند و مرورگر با هدر Accept-Encoding اعلام میکند کدام را میفهمد. brotli روی HTML، CSS و JS نسبت فشردهسازی بهتری از gzip میدهد و همه مرورگرهای امروزی روی HTTPS از آن پشتیبانی میکنند: brotli را اولویت اول بگذارید و gzip را برای fallback نگه دارید.
چه چیزهایی را نباید دوباره فشرده کرد
JPEG، PNG، WebP، AVIF، MP4، WOFF2 و آرشیوهای ZIP از قبل فشردهاند. اعمال gzip یا brotli روی آنها عملاً حجم را کم نمیکند، گاهی چند بایت هم اضافه میکند و در هر حال CPU میسوزاند. فشردهسازی را به HTML، CSS، JS، JSON، SVG و XML محدود کنید.
کش طولانی و immutable برای فایلهای هشدار
اگر نام فایل شامل هش محتواست — مثل app.9f2c1b.js — آن فایل دیگر هیچوقت تغییر نمیکند، چون هر تغییر محتوا نام تازهای میسازد. پس دلیلی ندارد TTL کوتاه بدهید:
Cache-Control: public, max-age=31536000, immutable
بخش immutable مهم است: بدون آن مرورگر هنگام رفرش باز هم درخواست revalidation میفرستد و شما به ازای هر رفرش یک رفتوبرگشت شبکه و یک پاسخ HTTP 304 میپردازید. با immutable آن درخواستها اصلاً ساخته نمیشوند.
همین هدر را روی HTML نگذارید؛ HTML باید TTL کوتاه داشته باشد تا محتوای تازه فوری دیده شود. در nsin سیاست کش را برای هر مسیر جداگانه تعریف میکنید — TTL طولانی برای /static/* و TTL کوتاه برای صفحات — و بعد از هر انتشار کش را هدفمند purge میکنید. جزئیات در مستندات آمده است.
رباتها و اسکرپرهایی که بیصدا ترافیک میسوزانند
بخشی از فاکتور شما را هیچ بازدیدکننده انسانی نساخته است: اسکرپرهایی که شبها کل سایت را دانلود میکنند، اسکنرهای آسیبپذیری که پشتسرهم مسیرهای رایج را میزنند، و رباتهایی که یک صفحه محصول را هر چند ثانیه یکبار میگیرند. این ترافیک معمولاً کشناپذیر است و مستقیم به مبدأ میخورد.
با rate limiting روی مسیرهای حساس و قواعد WAF میشود بخش بزرگی از آن را پیش از رسیدن به سرور متوقف کرد؛ نحوه چیدن این قواعد را در راهنمای WAF و محافظت در برابر DDoS آوردهایم. فقط زیادهروی نکنید: مسدود شدن ناخواسته Googlebot گرانتر از هر فاکتور پهنای باندی تمام میشود — به دسترسی Googlebot از ایران سر بزنید.
نرخ hit را اندازه بگیرید، بعد اقدام کنید
بدون اندازهگیری، بهینهسازی کش حدس زدن است. در پنل nsin هم آمار ترافیک را دارید و هم لاگ زنده درخواستها؛ با همین دو تا معلوم میشود کدام مسیرها miss میخورند. علت تقریباً همیشه یکی از اینهاست:
- مبدأ روی پاسخهای استاتیک هم
Set-Cookieمیفرستد و کش را غیرفعال میکند. - پارامترهای query غیرضروری (مثل شناسههای کمپین) کلید کش را چند تکه میکنند.
- هدر
Varyبیش از حد باز است و برای هر ترکیب هدر یک نسخه جدا میسازد. - مبدأ خودش
no-storeیاprivateمیفرستد، یا باno-cacheلبه را مجبور میکند پیش از هر بار سرو اعتبارسنجی کند — اغلب بهصورت پیشفرض قالب. - TTL آنقدر کوتاه است که محتوا پیش از استفاده مجدد منقضی میشود.
رفع هر کدام، بایت را از ردیف proxied به cached منتقل میکند.
جدول تاکتیکها: تلاش در برابر اثر
| تاکتیک | تلاش | اثر معمول بر پهنای باند |
|---|---|---|
ابعاد درست تصویر و srcset | متوسط | خیلی زیاد |
| فرمت WebP/AVIF بهجای JPEG/PNG | متوسط | خیلی زیاد |
| رفع علتهای miss و بالا بردن نرخ hit | متوسط | خیلی زیاد |
| lazy loading تصاویر پایین صفحه | کم | زیاد |
| brotli روی محتوای متنی | کم | زیاد |
| TTL طولانی + immutable برای فایلهای هشدار | کم | زیاد |
| rate limiting و مسدودسازی اسکرپرها | کم | متغیر، گاهی چشمگیر |
| انتقال ویدئو به سرویس اختصاصی ویدئو | زیاد | زیاد، اگر ویدئو دارید |
اگر فقط وقت دو کار را دارید: تصاویر و نرخ hit.
ارزانترین گیگابایت، گیگابایتی است که سرور اصلی شما هرگز آن را نمیفرستد.
پرسشهای متداول
چطور بفهمم پهنای باند سایتم کجا مصرف میشود؟
از تب Network در ابزار توسعهدهنده مرورگر شروع کنید و ستون حجم را نزولی مرتب کنید؛ در چند ثانیه معلوم میشود کدام فایلها سنگیناند. برای تصویر کلیتر، آمار ترافیک و لاگ درخواستها در پنل nsin مسیرهای پرمصرف را نشان میدهد.
آیا CDN واقعاً هزینه سرور را کم میکند؟
بله، به شرطی که نرخ hit قابل قبولی داشته باشید. CDN بار را از مبدأ برمیدارد و پاسخ را از لبه میدهد؛ اگر پیکربندی کشتان اشتباه باشد و همهچیز miss بخورد، فقط یک لایه اضافه کردهاید. اول کش را درست کنید، صرفهجویی خودش میآید.
تفاوت gzip و brotli چیست؟
brotli روی محتوای متنی معمولاً خروجی کوچکتری از gzip میدهد و پشتیبانی مرورگری فراگیری دارد. تنظیم درست این است که brotli اولویت اول باشد و gzip برای کلاینتهای قدیمی باقی بماند.
چرا نرخ hit کش من پایین است؟
شایعترین علتها: ارسال Set-Cookie روی پاسخهای استاتیک، پارامترهای query که کلید کش را تکهتکه میکنند، هدر Vary بیش از حد باز، و TTL خیلی کوتاه. لاگ زنده درخواستها معمولاً در چند دقیقه الگو را نشان میدهد.
از کجا شروع کنید
ترتیب کار روشن است: اول اندازهگیری، بعد تصاویر، بعد نرخ hit، و در آخر رباتها. هر مرحله را جدا انجام دهید و اثرش را روی ترافیک مبدأ ببینید. اگر هنوز دامنهتان روی nsin نیست، از پنل اضافهاش کنید؛ هر دامنه جدید هفت روز نسخه کامل Enterprise را رایگان دارد و همین یک هفته برای دیدن اثر واقعی کش کافی است.