Skip to content

CDN পারফরম্যান্স অপ্টিমাইজেশন

কমপ্রেশন, ইমেজ, Early Hints, origin কানেকশন, বড় ফাইল ও ভিডিও — আর সবচেয়ে জরুরি, ইউজারের দিক থেকে আসলে কতটা ফাস্ট হলো সেটা মাপা।

গল্পে বুঝি

শ্রীমঙ্গলের একটা চা-বাগানের ফ্যাক্টরি চালান বিরুনি। ভোরে পাতা তোলা হয়, দুপুরের মধ্যে শুকিয়ে গুঁড়ো হয়, আর বিকেলে ট্রাকে উঠে ঢাকা, চট্টগ্রাম, খুলনার ডিপোতে যায়। প্রথম বছর বিরুনি খোলা বস্তায় চা পাঠাতেন। বস্তা ঢিলেঢালা, ভেতরে অর্ধেক বাতাস, একটা ট্রাকে যতটা ধরার কথা তার অর্ধেক ধরত। দ্বিতীয় বছর তিনি একটা প্রেস মেশিন কিনলেন: বস্তা চেপে শক্ত বেল বানানো হয়, একই ট্রাকে দ্বিগুণ চা যায়। ডিপোতে পৌঁছে বেল খুলে দিলে চা আগের মতোই থাকে, কিছু হারায় না। তবে বিরুনি একটা জিনিস শিখলেন: যে চা আগেই ভ্যাকুয়াম-প্যাক করা টিনে ভরা, সেটা আবার প্রেসে দিলে কিছুই ছোট হয় না, শুধু মেশিনের সময় নষ্ট।

তৃতীয় সমস্যা ছিল প্যাকেটের সাইজ। ঢাকার সুপারশপ চায় ৫০০ গ্রামের প্যাকেট, গ্রামের মুদি দোকান চায় ২৫ গ্রামের ছোট পাতা, হোটেল চায় ৫ কেজির বস্তা। আগে বিরুনি সবাইকে ৫ কেজির বস্তা পাঠাতেন, দোকানিরা নিজেরা ভেঙে নিত, অনেক নষ্ট হতো। এখন ডিপোর মরিয়ম অর্ডার দেখে বলেন “এই দোকান ছোট প্যাকেট চায়”, আর ডিপোতেই বড় বস্তা ভেঙে সঠিক সাইজে প্যাক হয়, কারণ প্রতিটা ছোট অর্ডারের জন্য বাগানে ফেরত লোক পাঠানো অসম্ভব। একবার প্যাক হলে সেই সাইজটা ডিপোর তাকে থাকে, পরের দোকান একই সাইজ চাইলে সরাসরি দেওয়া হয়।

আর সবচেয়ে বড় শিক্ষাটা এল রুশদের কাছ থেকে, যিনি ডিস্ট্রিবিউশন দেখেন। বিরুনি গর্ব করে বলতেন “ফ্যাক্টরি থেকে ট্রাক দুই ঘণ্টায় ছাড়ে।” রুশদ বললেন, “দোকানদার তো ফ্যাক্টরির ঘড়ি দেখে না। সে দেখে অর্ডার দেওয়ার কতক্ষণ পর তাকে চা পৌঁছল। খুলনার দোকানে সেটা তিন দিন।” তাই রুশদ প্রতিটা দোকানের কাছ থেকে আসল পৌঁছানোর সময় খাতায় লেখা শুরু করলেন, আর পাশাপাশি নিজে মাসে একবার নিজের লোক দিয়ে পরীক্ষামূলক অর্ডার দিতেন।

মিলিয়ে নিই: বস্তা চেপে বেল বানানো হলো কমপ্রেশন (gzip, Brotli, zstd), ডিপোতে বেল খোলা হলো ব্রাউজারের decompression, আর আগেই টিনে ভরা চা আবার প্রেসে দেওয়া হলো আগে থেকে কমপ্রেসড ইমেজ বা ভিডিও আবার কমপ্রেস করা, যাতে লাভ শূন্য। দোকান অনুযায়ী প্যাকেটের সাইজ হলো ইমেজ রিসাইজ ও ফরম্যাট নেগোশিয়েশন, মরিয়মের অর্ডার দেখে বোঝা হলো Accept হেডার পড়া, আর ডিপোতে প্যাক করে তাকে রাখা হলো এজে ট্রান্সফর্ম করে ক্যাশ করা। ফ্যাক্টরির ঘড়ি হলো সার্ভারের দিকের মেট্রিক, দোকানদারের খাতা হলো RUM (real user monitoring), আর রুশদের পরীক্ষামূলক অর্ডার হলো synthetic monitoring। বাস্তবে CDN পারফরম্যান্সের কাজ মানে ঠিক এটাই: কম বাইট পাঠানো, সঠিক জিনিস পাঠানো, আর ইউজারের দিক থেকে মাপা।

পারফরম্যান্স আসলে কোথায় হারায়

একটা পেজ লোড হতে যে সময় লাগে, সেটা কয়েকটা টুকরোর যোগফল। CDN কোন টুকরোয় সাহায্য করে আর কোনটায় পারে না, সেটা আগে পরিষ্কার থাকা দরকার।

টুকরোকে নিয়ন্ত্রণ করেCDN কীভাবে সাহায্য করে
DNS লুকআপresolver, DNS TTLদ্রুত authoritative DNS, anycast
TCP + TLS handshakeইউজার থেকে সার্ভারের দূরত্বকাছের এজে handshake, TLS 1.3, HTTP/3
প্রথম বাইট পর্যন্ত (TTFB)ক্যাশ HIT হলে এজ, MISS হলে originHIT বাড়ানো, origin-এ গরম কানেকশন
বডি ডাউনলোডবাইটের সংখ্যা আর ব্যান্ডউইথকমপ্রেশন, ছোট ইমেজ, সঠিক ফরম্যাট
রেন্ডারিংব্রাউজার, আপনার HTML/CSS/JSপ্রায় কিছুই না (Early Hints কিছুটা সাহায্য করে)

শেষ সারিটা মনে রাখুন। একটা ৩ MB JavaScript বান্ডেল এজ থেকে খুব দ্রুত আসবে, কিন্তু ফোনের CPU সেটা পার্স করতে সেই একই সময় নেবে। CDN নেটওয়ার্কের সমস্যা সারায়, আপনার কোডের সমস্যা না।

কমপ্রেশন

নেগোশিয়েশন কীভাবে হয়

ব্রাউজার জানায় সে কোন কোন এনকোডিং খুলতে পারে, সার্ভার তার মধ্যে একটা বেছে নিয়ে জানায় কোনটা ব্যবহার করল:

GET /app.js HTTP/2
Host: baytalhikma.org
Accept-Encoding: gzip, deflate, br, zstd

HTTP/2 200
content-type: application/javascript
content-encoding: br
vary: Accept-Encoding

vary: Accept-Encoding জরুরি। এটা CDN-কে বলে যে একই URL-এর জন্য এনকোডিং ভেদে আলাদা কপি রাখতে হবে। এটা না থাকলে Brotli-সাপোর্ট করা ব্রাউজারের জন্য রাখা কপি এমন একটা পুরনো ক্লায়েন্টের কাছেও যেতে পারে যে Brotli খুলতে পারে না, আর সে বাইনারি জঞ্জাল দেখবে। বেশিরভাগ CDN Accept-Encoding-কে আলাদাভাবে নরমালাইজ করে (যেমন শুধু “br আছে কিনা” আর “gzip আছে কিনা” দেখে), যাতে হাজারটা আলাদা হেডার স্ট্রিং থেকে হাজারটা কপি না হয়।

তিনটা অ্যালগরিদম

অ্যালগরিদমকোথায় ভালোমনে রাখার বিষয়
gzipসব জায়গায় সাপোর্টেড, ফলব্যাক হিসেবেসবচেয়ে কম রেশিও
Brotliটেক্সট অ্যাসেটে gzip-এর চেয়ে সাধারণত ছোট ফাইলসর্বোচ্চ লেভেল খুব ধীর, তাই ডায়নামিক রেসপন্সে মাঝারি লেভেল
zstdদ্রুত কমপ্রেশন, ডায়নামিক রেসপন্সে ভালো ব্যালান্সব্রাউজার সাপোর্ট নতুন, তাই ফলব্যাক রাখতেই হবে

একটা ব্যবহারিক নিয়ম: স্ট্যাটিক অ্যাসেট বিল্ডের সময় সর্বোচ্চ লেভেলে আগেই কমপ্রেস করুন, ডায়নামিক রেসপন্স রিয়েল-টাইমে মাঝারি লেভেলে। বিল্ডের সময় সময় নিয়ে কমপ্রেস করলে প্রতিটা রিকোয়েস্টে খরচ হয় না।

# Pre-compress at build time; nginx/CDN serves the .br/.gz sibling directly
find build -type f \( -name '*.js' -o -name '*.css' -o -name '*.html' -o -name '*.svg' \) \
  -exec brotli -q 11 -k {} \; \
  -exec gzip -9 -k {} \;
# nginx origin: serve precompressed siblings if present
gzip_static on;
brotli_static on;   # requires the ngx_brotli module
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;

Origin-এ নাকি এজে কমপ্রেস করবেন

দুইটা পথ আছে, আর দুটোরই ফাঁদ আছে।

  • Origin কমপ্রেস করে, CDN সেটাই ক্যাশ করে। আপনার নিয়ন্ত্রণ বেশি। কিন্তু যদি origin শুধু gzip দেয়, CDN ব্রাউজারকে Brotli দিতে পারবে না (যদি না সে নিজে খুলে আবার কমপ্রেস করে)।
  • Origin কমপ্রেস ছাড়া পাঠায়, CDN এজে কমপ্রেস করে। সহজ, আর CDN প্রতিটা ক্লায়েন্টের জন্য সেরা এনকোডিং বেছে নেয়। কিন্তু origin থেকে এজে বেশি বাইট যায়, যেটা origin egress খরচ বাড়ায়।

বেশিরভাগ সেটআপে মাঝামাঝিটা ভালো চলে: origin gzip বা Brotli দেয়, আর CDN কনফিগে দেখে নিন সে কি origin-এর এনকোডিং রাখে, নাকি নিজে বদলায়। আপনার CDN-এর ডকে “compression” পেজটা পড়ে নিশ্চিত হন।

আগে থেকে কমপ্রেসড ফাইল আবার কমপ্রেস করবেন না। JPEG, PNG, WebP, AVIF, MP4, WOFF2, ZIP, এগুলো নিজেরাই কমপ্রেসড। এগুলোকে gzip করলে সাইজ প্রায় একই থাকে (কখনো কখনো কয়েক বাইট বাড়ে), কিন্তু সার্ভার আর ব্রাউজার দুজনেরই CPU খরচ হয়। gzip_types-এ শুধু টেক্সট টাইপ রাখুন।

Compression dictionary transport

একটা নতুন কৌশল: ব্রাউজারের কাছে আগে থেকে থাকা একটা ফাইলকে “অভিধান” হিসেবে ব্যবহার করে পরের ফাইলটা কমপ্রেস করা। আপনি যখন app.v41.js থেকে app.v42.js-এ যান, দুটোর ৯০ শতাংশ কোড একই। সার্ভার তখন শুধু পার্থক্যটা পাঠায়, পুরো ফাইল না। এটা RFC 9842 (Compression Dictionary Transport) হিসেবে সেপ্টেম্বর ২০২৫-এ প্রকাশিত হয়েছে, আর Brotli ও zstd-এর জন্য dcb ও dcz content-encoding রেজিস্টার করে।

# First visit: server marks the response as usable as a dictionary
HTTP/2 200
content-type: application/javascript
use-as-dictionary: match="/assets/app.*.js"

# Later visit: browser offers the dictionary it holds
GET /assets/app.v42.js HTTP/2
Accept-Encoding: gzip, br, zstd, dcb, dcz
Available-Dictionary: :pQ3Y...base64-sha256...:

HTTP/2 200
content-encoding: dcb
vary: Accept-Encoding, Available-Dictionary

২০২৬ সালের অক্টোবর অনুযায়ী ব্রাউজার ও CDN সাপোর্ট এখনো ছড়াচ্ছে, তাই এটা চালু করার আগে আপনার CDN আর টার্গেট ব্রাউজারে সাপোর্ট আছে কিনা যাচাই করুন। লাভ সবচেয়ে বেশি যেখানে আপনি ঘনঘন ডিপ্লয় করেন আর ইউজাররা বারবার ফেরত আসে।

ইমেজ অপ্টিমাইজেশন

বেশিরভাগ পেজে ইমেজই সবচেয়ে বেশি বাইট। এখানে দুইটা আলাদা জিনিস আছে: ফরম্যাট (AVIF, WebP, JPEG) আর সাইজ (পিক্সেল)।

ফরম্যাট নেগোশিয়েশন

ব্রাউজার Accept হেডারে জানায় সে কোন ইমেজ ফরম্যাট বোঝে:

GET /images/library.jpg HTTP/2
Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8

HTTP/2 200
content-type: image/avif
vary: Accept
content-length: 48211

একই URL, কিন্তু রেসপন্স AVIF। এখানে আবার vary: Accept জরুরি, আর এখানেই একটা বড় ফাঁদ: Accept হেডারের মান ব্রাউজার ভেদে, ভার্সন ভেদে আলাদা। CDN যদি পুরো হেডার স্ট্রিং দিয়ে ক্যাশ ভাগ করে, তাহলে কয়েক ডজন আলাদা কপি তৈরি হয়, আর hit ratio পড়ে যায়। ভালো CDN ইমেজ ফিচারগুলো Accept-কে নরমালাইজ করে মাত্র কয়েকটা বাকেটে (avif / webp / বাকি সব)। নিজে বানালে সেটা আপনাকেই করতে হবে (চ্যাপ্টার ৪-এ ক্যাশ কী নরমালাইজেশন দেখুন)।

বিকল্প হলো ফরম্যাট সিদ্ধান্ত HTML-এ নিয়ে যাওয়া, যাতে URL-ই ফরম্যাট বলে দেয় আর Vary লাগে না:

<picture>
	<source type="image/avif" srcset="/img/library-800.avif 800w, /img/library-1600.avif 1600w" />
	<source type="image/webp" srcset="/img/library-800.webp 800w, /img/library-1600.webp 1600w" />
	<img src="/img/library-800.jpg" width="800" height="533" alt="Bayt al-Hikma reading room" loading="lazy" />
</picture>

এটা ক্যাশের জন্য সবচেয়ে পরিষ্কার: প্রতিটা URL-এর একটাই রেসপন্স।

এজে রিসাইজ

একটা ৪০০০ পিক্সেলের ছবি ৪০০ পিক্সেলের থাম্বনেইলে দেখালে ব্রাউজার ১০০ গুণ বেশি পিক্সেল ডাউনলোড করে। বেশিরভাগ CDN-এর ইমেজ সার্ভিস URL বা query parameter দিয়ে রিসাইজ করতে দেয়, এজে একবার ট্রান্সফর্ম করে ফলাফল ক্যাশ করে।

https://baytalhikma.org/cdn-img/width=400,quality=75,format=auto/images/library.jpg

(সিনট্যাক্সটা উদাহরণমাত্র; প্রতিটা ভেন্ডরের নিজের ফরম্যাট আছে।) দুইটা জিনিস খেয়াল রাখুন:

  • যেকোনো width allow করবেন না। কেউ যদি width=1, width=2, … width=4000 দিয়ে রিকোয়েস্ট পাঠায়, প্রতিটা নতুন ট্রান্সফর্ম, নতুন ক্যাশ এন্ট্রি, নতুন origin ফেচ। কয়েকটা নির্দিষ্ট সাইজ (৪০০, ৮০০, ১২০০, ১৬০০) ঠিক করে দিন, বাকিগুলো রিজেক্ট বা নিকটতম সাইজে রাউন্ড করুন।
  • ট্রান্সফর্ম মানে CPU। একটা MISS-এ ইমেজ রিসাইজ করতে কয়েকশো মিলিসেকেন্ড লাগতে পারে। প্রথম ভিজিটরের জন্য সেটা ধীর, তাই জনপ্রিয় সাইজগুলো ক্যাশ ওয়ার্ম করা যায়।

Early Hints (103)

একটা সাধারণ পেজ লোডে সার্ভার HTML বানাতে সময় নেয় (ডেটাবেস কুয়েরি, টেমপ্লেট রেন্ডার)। এই সময়টায় ব্রাউজার বসে থাকে, অথচ সে জানতে পারলে আগেই CSS আর ফন্ট আনা শুরু করতে পারত। 103 Early Hints ঠিক এটা করে: আসল রেসপন্সের আগে একটা অস্থায়ী রেসপন্স পাঠায়, যেটায় শুধু Link হেডার থাকে।

HTTP/2 103
link: </assets/app.css>; rel=preload; as=style
link: <https://fonts.cordoba.example>; rel=preconnect

HTTP/2 200
content-type: text/html
link: </assets/app.css>; rel=preload; as=style
...

CDN-এর জন্য এটা বিশেষ কাজের: origin যখন ধীরে HTML বানাচ্ছে, এজ আগের রেসপন্স থেকে শেখা Link হেডারগুলো দিয়ে সাথে সাথে 103 পাঠাতে পারে। কয়েকটা CDN এটা স্বয়ংক্রিয়ভাবে করে।

Early Hints-এর ব্রাউজার সাপোর্ট সমান না। Chromium-ভিত্তিক ব্রাউজারে preload আর preconnect কাজ করে; অন্য ব্রাউজারের অবস্থা বদলাচ্ছে, তাই caniuse.com-এ দেখে নিন। যেসব ব্রাউজার বোঝে না, তারা 103 উপেক্ষা করে, তাই এটা পাঠানো নিরাপদ। শুধু সেইসব রিসোর্স হিন্ট করুন যেগুলো প্রায় প্রতিটা পেজে লাগে; অপ্রয়োজনীয় preload আসল জরুরি রিসোর্সের ব্যান্ডউইথ খেয়ে ফেলে।

Origin-এর দিকে কানেকশন

ইউজার থেকে এজ পর্যন্ত পথটা ছোট, কিন্তু MISS হলে এজ থেকে origin পর্যন্ত পথটা লম্বা হতে পারে (ঢাকার এজ থেকে ফ্রাঙ্কফুর্টের origin)। প্রতিটা MISS-এ যদি নতুন TCP আর TLS handshake হয়, তাহলে শুধু handshake-এই কয়েকটা round trip চলে যায়।

এখানে যা দেখবেন:

  • Keep-alive ও কানেকশন পুলিং। এজ থেকে origin-এ খোলা কানেকশন পুনর্ব্যবহার হচ্ছে কিনা। origin-এর keepalive_timeout খুব ছোট হলে CDN প্রতিবার নতুন কানেকশন খুলবে।
  • Origin shield / tiered cache (চ্যাপ্টার ৭)। সব এজ সরাসরি origin-এ না গিয়ে একটা কেন্দ্রীয় লেয়ারের মধ্য দিয়ে যায়, যার origin-এর সাথে গরম কানেকশন থাকে।
  • Origin-এর অবস্থান। আপনার বেশিরভাগ ইউজার যদি দক্ষিণ এশিয়ায় থাকে, origin সিঙ্গাপুর বা মুম্বাইয়ে রাখলে প্রতিটা MISS অনেক সস্তা।
# nginx as origin: keep CDN connections alive long enough to reuse
keepalive_timeout 75s;
keepalive_requests 10000;
http2 on;

রেঞ্জ রিকোয়েস্ট আর বড় ফাইল

একটা ২ GB-র ISO বা একটা লম্বা ভিডিও পুরোটা একবারে ক্যাশ করা কঠিন। ব্রাউজার আর ভিডিও প্লেয়ার প্রায়ই পুরো ফাইল চায় না, চায় একটা অংশ:

GET /downloads/kitab-al-shifa.pdf HTTP/2
Range: bytes=1048576-2097151

HTTP/2 206
content-range: bytes 1048576-2097151/52428800
content-length: 1048576
accept-ranges: bytes

CDN এটা কয়েকভাবে সামলায়:

  • পুরো ফাইল আনা, তারপর অংশ দেওয়া। প্রথম রেঞ্জ রিকোয়েস্টে এজ পুরো ফাইল origin থেকে টানে। ছোট ফাইলে ঠিক আছে, বড় ফাইলে প্রথম ইউজারের জন্য খুব ধীর।
  • Slice caching। এজ ফাইলটাকে নির্দিষ্ট সাইজের টুকরোয় (ধরুন ১ MB বা ১০ MB) ভাগ করে, প্রতিটা টুকরো আলাদা ক্যাশ এন্ট্রি। কেউ মাঝের একটা অংশ চাইলে শুধু সেই টুকরোগুলো origin থেকে আসে।
# nginx edge: cache large files in 1 MB slices
location /downloads/ {
    slice 1m;
    proxy_cache edge;
    proxy_cache_key $uri$slice_range;
    proxy_set_header Range $slice_range;
    proxy_cache_valid 200 206 7d;
    proxy_pass https://origin;
}

ভিডিওর ক্ষেত্রে আধুনিক স্ট্রিমিং (HLS, DASH) ফাইলকে আগে থেকেই ছোট সেগমেন্টে ভাগ করে রাখে, তাই প্রতিটা সেগমেন্ট একটা সাধারণ ছোট ফাইল হিসেবে ক্যাশ হয়। ভিডিও ডেলিভারির পুরো বিস্তারিত ভিডিও ট্র্যাকের CDN চ্যাপ্টারে আছে।

Hit ratio: দুইটা আলাদা সংখ্যা

“আমাদের cache hit ratio ৯৫%” শুনতে ভালো, কিন্তু কোন hit ratio?

  • Request hit ratio: কত শতাংশ রিকোয়েস্ট এজ থেকে সার্ভ হলো।
  • Byte hit ratio: কত শতাংশ বাইট এজ থেকে সার্ভ হলো।

এই দুটো অনেক আলাদা হতে পারে। একটা সাইটের কথা ধরুন যেখানে ছোট আইকন আর CSS ৯৯% HIT, কিন্তু বড় ভিডিও ফাইলগুলো বেশিরভাগ MISS। Request hit ratio ৯৫%, কিন্তু byte hit ratio ৪০%। আপনার origin egress বিল byte hit ratio দেখে আসে, আর origin-এর লোড request hit ratio দেখে।

# Illustrative: compute both from a CDN log export (one JSON line per request)
$ jq -r '[.cache_status, .bytes] | @tsv' edge-logs.jsonl | awk '
    { total_req++; total_bytes += $2 }
    $1 == "HIT" { hit_req++; hit_bytes += $2 }
    END {
      printf "request hit ratio: %.1f%%\n", 100 * hit_req / total_req
      printf "byte hit ratio:    %.1f%%\n", 100 * hit_bytes / total_bytes
    }'
request hit ratio: 94.8%
byte hit ratio:    41.2%

(ফিল্ডের নাম আপনার CDN-এর লগ ফরম্যাট অনুযায়ী বদলাবে।)

মাপা: RUM বনাম synthetic

পারফরম্যান্সের কাজের সবচেয়ে বড় ভুল হলো নিজের ল্যাপটপ থেকে, নিজের ফাইবার কানেকশনে, নিজের শহরে মাপা।

Two ways to measure
RUM (real user monitoring)Synthetic
কে মাপেআসল ইউজারের ব্রাউজারআপনার নির্ধারিত প্রোব
কী দেখায়আসল ডিভাইস, আসল নেটওয়ার্ক, আসল অভিজ্ঞতাএকই অবস্থায় বারবার মাপলে কী হয়
কোথায় ভালো“ইউজাররা আসলে কী পাচ্ছে”রিগ্রেশন ধরা, ডিপ্লয়ের আগে-পরে তুলনা
দুর্বলতাগোলমেলে, ট্রাফিক না থাকলে ডেটা নেইআসল ইউজারের প্রতিনিধি না

দুটোই লাগে। RUM-এর জন্য ব্রাউজারের Performance API আর Core Web Vitals মেট্রিকগুলো সংগ্রহ করুন, আর গড় না, p75 দেখুন, দেশ বা অঞ্চল ভেদে আলাদা করে।

// Minimal RUM: send navigation timing + LCP for the current page
new PerformanceObserver((list) => {
	const lcp = list.getEntries().at(-1);
	const nav = performance.getEntriesByType('navigation')[0];
	navigator.sendBeacon(
		'/rum',
		JSON.stringify({
			page: location.pathname,
			ttfb: Math.round(nav.responseStart),
			lcp: Math.round(lcp.startTime),
			protocol: nav.nextHopProtocol
		})
	);
}).observe({ type: 'largest-contentful-paint', buffered: true });

বাস্তব প্রোডাকশনে web-vitals লাইব্রেরি এটা ঠিকভাবে করে (পেজ লুকানোর সময় পাঠানো, ব্যাক-ফরোয়ার্ড ক্যাশ ইত্যাদি)। উপরের কোডটা শুধু ধারণা দেখাতে।

Server-Timing: সার্ভারের ভেতরের সময় ব্রাউজারে দেখানো

Server-Timing হেডার দিয়ে সার্ভার আর CDN তাদের ভেতরের সময়গুলো ব্রাউজারকে জানাতে পারে। DevTools-এর Network ট্যাবে এগুলো দেখা যায়, আর RUM কোড PerformanceResourceTiming.serverTiming দিয়ে পড়তে পারে।

HTTP/2 200
server-timing: cdn-cache;desc="MISS", edge;dur=4, origin;dur=312
server-timing: db;dur=188, render;dur=96

এখন আপনি একটা ধীর রিকোয়েস্ট দেখে বলতে পারবেন: এজে ৪ ms, origin-এ ৩১২ ms, তার মধ্যে ১৮৮ ms ডেটাবেসে। “CDN স্লো” অভিযোগের প্রায় অর্ধেক এভাবেই origin-এর দিকে ফিরে যায়।

curl দিয়ে প্রতিটা ধাপ মাপা

curl -so /dev/null https://baytalhikma.org/ -w '
dns:        %{time_namelookup}s
connect:    %{time_connect}s
tls:        %{time_appconnect}s
ttfb:       %{time_starttransfer}s
total:      %{time_total}s
size:       %{size_download} bytes
http:       %{http_version}
'
# Illustrative output from Dhaka, warm edge cache
dns:        0.012s
connect:    0.021s
tls:        0.048s
ttfb:       0.063s
total:      0.071s
size:       14820 bytes
http:       2

যদি ttfb আর tls-এর পার্থক্য বড় হয় (ধরুন ৪০০ ms), সময়টা এজ বা origin-এ রেসপন্স তৈরিতে যাচ্ছে, নেটওয়ার্কে না। তখন cache-status বা ভেন্ডরের cache হেডার দেখুন (চ্যাপ্টার ৫): MISS হলে origin ধীর, HIT হলেও ধীর হলে এজের সমস্যা।

ডিবাগিং: এই চ্যাপ্টারের সমস্যাগুলো

লক্ষণনিশ্চিত করার উপায়সমাধান
টেক্সট ফাইল কমপ্রেস হচ্ছে নাcurl -sI -H 'Accept-Encoding: br, gzip' URL দেখুন content-encoding আছে কিনাorigin-এ gzip_types-এ টাইপটা যোগ করুন, বা CDN-এ কমপ্রেশন চালু করুন
কিছু ক্লায়েন্ট বাইনারি জঞ্জাল দেখেএকই URL-এ Accept-Encoding ছাড়া রিকোয়েস্ট করে দেখুন content-encoding: br আসছে কিনাorigin-এ Vary: Accept-Encoding পাঠান, তারপর URL পার্জ করুন
ইমেজ রিকোয়েস্টে hit ratio খুব কমলগে একই URL-এর জন্য কতগুলো আলাদা Accept মান আছে গুনুনAccept নরমালাইজ করুন, বা picture দিয়ে ফরম্যাট URL-এ নিয়ে আসুন
ইমেজ রিসাইজ ফিচারে বিল হঠাৎ বেড়ে গেছেলগে width= মানগুলোর আলাদা সংখ্যা দেখুননির্দিষ্ট সাইজের allowlist রাখুন
JPEG/MP4-এ CPU বেশি, সাইজ একইcontent-encoding: gzip ইমেজে আছে কিনা দেখুনকমপ্রেশন টাইপ লিস্ট থেকে ইমেজ ও ভিডিও বাদ দিন
বড় ফাইলের প্রথম ডাউনলোড খুব ধীরcurl -r 0-1023 -sI URL দিয়ে প্রথম ছোট রেঞ্জের TTFB দেখুনslice caching চালু করুন
রেঞ্জ রিকোয়েস্টে পুরো ফাইল আসে (200, 206 না)রেসপন্সে accept-ranges আর স্ট্যাটাস দেখুনorigin-এ রেঞ্জ সাপোর্ট চালু করুন; CDN কনফিগে রেঞ্জ পাস-থ্রু দেখুন
HIT হলেও TTFB বেশিcurl -w দিয়ে tls আর ttfb-এর পার্থক্য, আর কোন PoP থেকে আসছে দেখুনইউজারের কাছে PoP আছে কিনা দেখুন; ভেন্ডরকে PoP-সহ রিপোর্ট করুন
MISS-এ TTFB অনেক বেশিServer-Timing বা origin লগে রেসপন্স টাইম দেখুনorigin শিল্ড, origin-এর অবস্থান, keepalive দেখুন
Early Hints কাজ করছে বলে মনে হচ্ছে নাcurl -sv --http2 URL চালিয়ে stderr আউটপুটে 103 লাইন খুঁজুনCDN-এ Early Hints চালু করুন; ব্রাউজার সাপোর্ট দেখুন
ল্যাপটপে ফাস্ট, ইউজাররা অভিযোগ করছেRUM-এ দেশ আর ডিভাইস অনুযায়ী p75 দেখুনযেখানে খারাপ সেই অঞ্চলের PoP, ইমেজ সাইজ আর JS সাইজ দেখুন

মূল শিক্ষা

  1. CDN নেটওয়ার্কের সময় কমায়, রেন্ডারিংয়ের না। ভারী JavaScript এজ থেকে এলেও ফোনে ভারীই থাকে।
  2. স্ট্যাটিক অ্যাসেট বিল্ডের সময় সর্বোচ্চ লেভেলে কমপ্রেস করুন, ডায়নামিক রেসপন্স মাঝারি লেভেলে, আর আগে থেকে কমপ্রেসড ফরম্যাট কখনো আবার কমপ্রেস করবেন না।
  3. Vary: Accept-Encoding আর Vary: Accept ছাড়া কনটেন্ট নেগোশিয়েশন ভুল কপি সার্ভ করে, আর নরমালাইজেশন ছাড়া Vary hit ratio মেরে ফেলে।
  4. ইমেজের সাইজ আর ফরম্যাট বেশিরভাগ পেজে সবচেয়ে বড় জয়; এজে রিসাইজ করলে সাইজের allowlist রাখুন।
  5. Early Hints ধীর HTML-এর অপেক্ষার সময়টা কাজে লাগায়; শুধু সত্যিকারের জরুরি রিসোর্স হিন্ট করুন।
  6. বড় ফাইলের জন্য slice caching, ভিডিওর জন্য সেগমেন্টেড স্ট্রিমিং।
  7. Request hit ratio আর byte hit ratio আলাদা সংখ্যা; বিল আসে দ্বিতীয়টা থেকে।
  8. ইউজারের দিক থেকে মাপুন। RUM-এ p75 দেশ ভেদে, synthetic দিয়ে রিগ্রেশন, আর Server-Timing দিয়ে সময়টা কোথায় যাচ্ছে।