Skip to content
كل المقالاتمقاسات صور متعددة من Next.js تمر عبر Keenpix

تحسين الصور في Next.js باستخدام CDN مخصص

ربط next/image مع Keenpix عبر custom loader، وضبط sizes واختيار AVIF أو WebP، ثم التحقق من الطلب الفعلي داخل المتصفح قبل الإطلاق.

nextjsresponsive-imagesimplementation

ظهور الصورة على الشاشة لا يثبت أن استبدال محرّك الصور في 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 عشوائية تغيّر المفتاح من دون أن تغيّر الصورة.

افحص الطلب، لا شكل الصفحة فقط

افتح أدوات المطور ونفّذ هذه المراجعة:

  1. ابحث عن srcset وتأكد من وجود أكثر من قيمة w.
  2. غيّر عرض الصفحة وراقب الصورة التي اختارها المتصفح.
  3. راجع Content-Type وVary وCache-Control في الاستجابة.
  4. جرّب مصدراً غير موجود في 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. المثال يشرح سلوك الطلب ولا يقدّم نتيجة أداء عامة.

صور محسّنة وفاتورة يمكن فهمها.

استخدم الخدمة المدارة أو شغّل المحرك مفتوح المصدر على خادمك.