103 Early Hints: حمّل CSS قبل ما HTML يجهز
مستوى القارئ: متوسط
هتكسب من المقال ده طريقة عملية تقلل انتظار الموارد الحرجة لما السيرفر يتأخر في تجهيز HTML. ركز: 103 Early Hints مش بديل لتحسين الكود، لكنه يستغل وقت الانتظار بدل ما يسيبه فاضي.
المشكلة باختصار
لو عندك صفحة منتج أو لوحة تقارير بتحتاج 700 إلى 1200ms عشان السيرفر يجمع بياناتها، المتصفح غالبًا مش هيعرف ملفات CSS والخطوط المهمة غير بعد وصول HTML. اللي بيحصل فعلاً إن زمن تجهيز الصفحة وزمن تحميل الموارد بيتجمعوا فوق بعض.
سيناريو واقعي: متجر عنده 50K زائر يوميًا، وصفحة المنتج بتطلع HTML بعد 900ms بسبب قراءة المخزون والأسعار. ملف CSS الحرج حجمه 80KB وخط WOFF2 حجمه 45KB. بدون أي hint، المتصفح يبدأ تحميلهم بعد 900ms. مع 103 Early Hints، يقدر يبدأ من أول 100 إلى 150ms تقريبًا.
الفكرة بمثال بسيط
اعتبر إنك طلبت تقرير من زميلك. التقرير نفسه هياخد 10 دقائق. بدل ما يستنى يخلص التقرير ثم يقول لك افتح ملف الأرقام وملف الرسوم، يبعت لك بدري: جهّز الملفين دول عشان هتحتاجهم. لما التقرير يوصل، جزء من التحضير خلص بالفعل.
ده بالظبط دور 103 Early Hints. السيرفر يرسل ردًا مؤقتًا قبل الرد النهائي، وفيه ترويسات Link تقول للمتصفح: غالبًا هتحتاج الملفات دي. المتصفح يبدأ preload أو preconnect أثناء ما السيرفر لسه بيجهز الرد النهائي.
التعريف الدقيق: 103 هو HTTP informational status code يُرسل قبل 200 OK. استخدامه الأساسي مع ترويسة Link لتحميل موارد متوقعة مبكرًا. حسب MDN، المتصفح يبدأ جلب الموارد المذكورة بمجرد استقبال الـ hints، لكن الدعم العملي مرتبط غالبًا بـ HTTP/2 أو أحدث.
الإعداد العملي مع Cloudflare وNginx
أفضل طريقة آمنة لمعظم الفرق الصغيرة: خلّي الـ origin يرسل ترويسات Link للموارد الحرجة، وفعل Early Hints من Cloudflare لو موقعك وراه Cloudflare. Cloudflare يقدر يرسل 103 من الـ edge اعتمادًا على Link headers الموجودة في صفحات HTML.
الافتراض إن عندك تطبيق ويب وراء Nginx، وملفات ثابتة بأسماء versioned زي /assets/app.7fd2.css. لا تستخدم أسماء تتغير بدون cache busting، وإلا هتسبق تحميل ملف قديم.
server {
listen 443 ssl http2;
server_name example.com;
location / {
proxy_pass http://app:3000;
# موارد حرجة فعلًا لأول viewport فقط
add_header Link "; rel=preload; as=style" always;
add_header Link "; rel=preload; as=font; type=font/woff2; crossorigin" always;
}
}لو بتستخدم Express بدل Nginx، نفس الفكرة ممكن تتكتب داخل الرد:
app.get('/products/:id', async (req, res) => {
res.setHeader('Link', [
'; rel=preload; as=style',
'; rel=preload; as=font; type=font/woff2; crossorigin'
].join(', '));
const product = await loadProduct(req.params.id);
res.render('product', { product });
});بعدها فعّل Early Hints من Cloudflare Cache settings إن كانت متاحة لحسابك. لو مش بتستخدم Cloudflare، هتحتاج دعم من السيرفر أو الـ CDN لإرسال 103 فعليًا. وجود Link وحده مفيد كـ preload في الرد النهائي، لكنه مش نفس مكسب 103 المبكر.
إزاي تقيس قبل وبعد
متقيسش بالانطباع. افتح Chrome DevTools، Network، ثم سجل waterfall للصفحة. قبل التغيير، دور على وقت بداية تحميل CSS والخط. بعد التغيير، المفروض تشوف التحميل بدأ قبل وصول HTML النهائي أو قريب جدًا منه.
قياس تقديري من السيناريو السابق: اكتشاف CSS نزل من حوالي 900ms إلى 120ms. لو LCP كان 2.9s بسبب CSS متأخر وخط متأخر، ممكن ينزل إلى 2.1s أو 2.3s حسب الشبكة وحجم المورد. الرقم مش وعدًا عامًا، لكنه اتجاه منطقي لما المورد الحرج كان متأخر الاكتشاف.
ركز على 3 أرقام: بداية تحميل المورد الحرج، وقت انتهاء تحميله، وLCP. لو المورد بدأ بدري لكن LCP لم يتحسن، غالبًا المشكلة في JavaScript execution أو server TTFB نفسه، مش في اكتشاف CSS.
الـ trade-off هنا
المكسب: المتصفح يستغل server think time بدل ما ينتظر HTML كامل. ده مفيد جدًا للصفحات الديناميكية البطيئة نسبيًا.
التكلفة: لو بالغت في preload، هتزاحم موارد أهم وتزود ضغط الشبكة. web.dev ينبه إن preload إلزامي أكثر من hints أخرى، لذلك استخدمه بحذر. اختار 1 إلى 3 موارد فقط: CSS حرج، خط حرج، أو origin خارجي مهم.
فيه تكلفة تشغيلية كمان: لازم تحدّث روابط assets لما يتغير build hash. لو فريقك ما عندوش asset manifest واضح، الإعداد هيكسر بسهولة.
متى لا تستخدم هذه الطريقة
لا تستخدم 103 Early Hints لو HTML بيرجع في أقل من 150ms. المكسب غالبًا صغير. لا تستخدمه لو المشكلة الأساسية JavaScript bundle كبير، لأنك تحتاج split أو defer قبل hints. ولا تستخدمه لصور كثيرة تحت fold؛ كده أنت بتسرق bandwidth من الصورة أو الـ CSS الأهم.
كمان لا تستخدمه مع ملفات بدون fingerprinting واضح. لو /assets/app.css يتغير كل deploy بدون cache control مضبوط، هتزود احتمالية تحميل نسخة غير مناسبة.
المصادر
الخطوة التالية
افتح صفحة ديناميكية عندك TTFB بتاعها فوق 500ms، وحدد موردًا واحدًا حرجًا يظهر متأخرًا في waterfall. ضيف له Link rel=preload، فعل Early Hints من الـ CDN لو متاح، وقارن LCP قبل وبعد في نفس الشبكة.