ظهور الصورة على الشاشة لا يثبت أن استبدال محرّك الصور في Next.js تم بشكل صحيح. قد يحمّل المتصفح مقاساً أكبر من حاجته، أو يطلب WebP بينما الكاش لا يفرّق بين الصيغ.
الحل لا يحتاج إلى تغيير مكوّن Image. نغيّر الرابط الذي يولّده فقط، ونترك لـ Next.js مهمة إنشاء srcset.
ملف loader واحد
أنشئ ملف keenpix-loader.js في جذر المشروع:
'use client'
export default function keenpixLoader({ src, width, quality }) {
const baseUrl = process.env.NEXT_PUBLIC_KEENPIX_BASE_URL
const project = process.env.NEXT_PUBLIC_KEENPIX_PROJECT
const origin = process.env.NEXT_PUBLIC_IMAGE_ORIGIN
if (!baseUrl || !origin) {
throw new Error('Keenpix image environment variables are missing')
}
const source = src.startsWith('http') ? src : new URL(src, origin).href
const params = new URLSearchParams({
fmt: 'auto',
w: String(width),
})
if (project) {
params.set('project', project)
}
if (quality) {
params.set('q', String(quality))
}
return `${baseUrl}/img/${encodeURIComponent(source)}?${params.toString()}`
}في الخدمة المدارة، ضع معرّف المشروع داخل مسار التسليم الأساسي:
NEXT_PUBLIC_KEENPIX_BASE_URL=https://cdn.keenpix.com/p/your-project-id
NEXT_PUBLIC_IMAGE_ORIGIN=https://assets.example.comهذا هو الرابط المباشر. الرابط القديم على keenpix.com مع project داخل الـ query كان يُحوَّل في v0.2، وأصبح مرفوضاً في v0.3.
أما في الاستضافة الذاتية، فيبقى معرّف المشروع داخل الـ query:
NEXT_PUBLIC_KEENPIX_BASE_URL=https://images.example.com
NEXT_PUBLIC_KEENPIX_PROJECT=your-project-id
NEXT_PUBLIC_IMAGE_ORIGIN=https://assets.example.comهذه القيم ليست أسراراً. مفتاح التوقيع، إن فعّلت الروابط الموقّعة، لا يوضع هنا أبداً. أضف assets.example.com إلى قائمة المصادر المسموح بها داخل المشروع، وإلا سيرفض Keenpix جلب الصورة.
ربطه مع next/image
في next.config.js:
module.exports = {
images: {
loader: 'custom',
loaderFile: './keenpix-loader.js',
},
}بعدها استخدم Image بالطريقة المعتادة:
import Image from 'next/image'
export function ProductHero() {
return (
<Image
alt="حقيبة قماشية باللون الزيتوني"
height={1200}
sizes="(max-width: 768px) 100vw, 50vw"
src="/products/backpack.jpg"
width={1600}
/>
)
}السطر الذي يستحق المراجعة هنا هو sizes. هذه القيمة تساعد Next.js على بناء المقاسات المرشحة، ثم يستخدمها المتصفح لاختيار الصورة المناسبة للمساحة الفعلية. إذا كانت الصورة تشغل نصف الشاشة على سطح المكتب، فلا تجعل المتصفح يتعامل معها كأنها بعرض الشاشة كلها.
ماذا يحدث عند fmt=auto؟
يرسل loader القيمة fmt=auto. يقرأ Keenpix الهيدر Accept ويختار AVIF أو WebP، ثم يعود إلى JPEG عند الحاجة. لهذا السبب يجب أن يحترم الـ CDN الموجود أمامه الهيدر Vary: Accept. تجاهل هذا التفصيل قد يجعل الكاش يعيد صيغة غير مناسبة لمتصفح آخر.
يشرح دليل AVIF وWebP في الإنتاج طريقة فحص النسخ داخل الكاش بدلاً من الاكتفاء بالهيدر.
كل قيمة عرض تنتج رابطاً مختلفاً. طلب w=640 ليس هو طلب w=1200، ومن الطبيعي أن يملك كل واحد منهما نسخة في الكاش. المشكلة تبدأ مع query parameters عشوائية تغيّر المفتاح من دون أن تغيّر الصورة.
افحص الطلب، لا شكل الصفحة فقط
افتح أدوات المطور ونفّذ هذه المراجعة:
- ابحث عن
srcsetوتأكد من وجود أكثر من قيمةw. - غيّر عرض الصفحة وراقب الصورة التي اختارها المتصفح.
- راجع
Content-TypeوVaryوCache-Controlفي الاستجابة. - جرّب مصدراً غير موجود في allowlist وتأكد من رفضه.
إذا أردت مقارنة الحجم، استخدم نفس الصورة ونفس الأبعاد والجودة. نتيجة صورة PNG كبيرة لا تصلح وعداً لكل صور المتجر.
الروابط الموقّعة لا تُنشأ في المتصفح
ملف loader يصل إلى كود الواجهة، لذلك لا تضع فيه مفتاح HMAC. عند تفعيل التوقيع، أنشئ الرابط على الخادم أو خلف endpoint تملكه. project id قيمة عامة، أما signing secret فيجب أن يبقى على الخادم.
قد لا تحتاج إلى التوقيع أصلاً. قائمة المصادر تكفي لكثير من المواقع العامة. استخدم التوقيع لمنع أطراف أخرى من إنشاء تحويلات جديدة أو إضافة معاملات تسبب cache misses. التوقيع لا يمنع نسخ رابط صالح وإعادة استخدامه، ولا يقيّد النطاق الذي يرسل الطلب.
متى أبقي على محرّك Next.js الافتراضي؟
إذا كان المحرّك المدمج مناسباً للاستضافة والتكلفة، فالإبقاء عليه أبسط. إضافة CDN خارجي تعني خدمة وسياسة كاش ونقطة فشل جديدة. يصبح التغيير منطقياً عندما تريد قواعد صور موحّدة بين أكثر من framework، أو طبقة صور مستقلة، أو خيار نقل الحمل إلى نسخة Keenpix تعمل على خادمك.
المراجع
مصادر التحقق بتاريخ 15 أغسطس 2026 هي توثيق Image في Next.js، وإعداد custom loader، ودليل Keenpix لـ Next.js. المثال يشرح سلوك الطلب ولا يقدّم نتيجة أداء عامة.
