تحسين محركات البحث في Next.js

كيفية جعل موقع Next.js قابلاً للزحف والفهرسة والترتيب، مع شرح نظامي التوجيه وأنماط العرض وMetadata API وsitemap.ts وrobots.ts وnext/image وnext/link والأخطاء التي تعطّل ذلك بصمت.

نُشر أول مرة: 26 يونيو 2026 · آخر تحديث: 13 أغسطس 2026 · Advanced
اللغات

يحل Next.js أصعب مشكلات SEO الخاصة بـJavaScript افتراضياً إذا استُخدم على نحو صحيح. تضع Server Components وSSG/ISR المحتوى في HTML من دون انتظار طابور العرض، وتحل Metadata API العناوين والروابط الأساسية وOpen Graph على الخادم، فيما يمثل sitemap.ts وrobots.ts اصطلاحين للملفات. يمنحك الإطار البنية الأساسية لكنه لا يكتب الوسوم نيابة عنك. الأخطاء متوقعة: غياب metadataBase، وعدم ضبط priority لصورة LCP، وتصدير metadata من Client Component، وإرجاع صفحات 404 للحالة 200.

الخلاصة — يحل Next.js أصعب مشكلات SEO في JavaScript افتراضياً عند استخدامه على نحو صحيح. يعتمد App Router على Server Components ويدعم SSG وSSR وISR، وكلها ترسل HTML معروضاً فلا يوجد تأخير لطابور العرض. اضبط البيانات الوصفية عبر Metadata API الأصلية (metadata / generateMetadata) ولا تنس metadataBase كي لا تصبح الروابط الأساسية وصور OG نسبية. استخدم app/sitemap.ts وapp/robots.ts، وابنِ المسارات الديناميكية مسبقاً بـgenerateStaticParams، واضبط priority لصورة LCP، وحافظ على الروابط كمراسي next/link حقيقية. يدعم Pages Router تحسين البحث أيضاً عبر next/head. الأخطاء متوقعة، ومعظمها ليس خطأ Next.js بل خطأ التنفيذ.

موقع Next.js في الصورة

Next.js إطار مبني على React، لذا ينطبق عليه كل ما في SEO في JavaScript. وما يستحق دليلاً مستقلاً هو أنه يوفر حلولاً أصلية لمعظم مشكلات JS وSEO: العرض على الخادم، والتوليد الثابت، ونظام البيانات الوصفية، واصطلاحي sitemap وrobots. ليست الصعوبة في قدرة Google على قراءته، فهو يعرض JavaScript منذ سنوات، بل في اختيار نمط العرض الصحيح وربط أساسيات SEO. وهذه حالة متخصصة من SEO لنظام CMS بلا واجهة؛ فقرارات العرض في الواجهة الأمامية أهم بكثير من نظام CMS نفسه.

احفظ هذا الإطار: عبارة «هذا موقع Next.js» لا تخبرك بكيفية تقديم أي عنوان URL بعينه. يُضبط نمط العرض والتخزين المؤقت وحدود Server/Client Components لكل مسار، وأحياناً لكل مقطع. قد يجمع المشروع صفحة تسويق ثابتة وصفحة منتج SSR ولوحة Client Component. لا تعمم سلوك مسار واحد على التطبيق كله؛ اختبر عنوان URL المحدد.

نظاما توجيه وآليتان مختلفتان

لدى Next.js نظاما توجيه يتعاملان مع SEO بطرق مختلفة:

  • Pages Router، وهو النموذج الأقدم: يجلب البيانات عبر getStaticProps أو getServerSideProps، ويدير البيانات الوصفية بـ<Head> من next/head أو حزمة next-seo، ولا يدعم Server Components.
  • App Router، منذ v13 وهو النهج الحالي الموصى به: يستخدم React Server Components افتراضياً، وMetadata API الأصلية (metadata أو generateMetadata)، واصطلاحي app/sitemap.ts وapp/robots.ts، وgenerateStaticParams للمسارات الديناميكية. Evidence for this claim The App Router uses Server Components and supports generateStaticParams plus metadata file conventions. Scope: Current Next.js App Router behavior; route rendering can become dynamic based on APIs used. Confidence: high · Verified: Next.js: Server and Client Components Next.js: generateStaticParams

يمكن لكليهما تحقيق ترتيب جيد. يمنحك App Router نظام بيانات وصفية أنظف ومتكاملاً، من دون التعامل المتكرر مع next/head، وServer Components جاهزة؛ لذلك أفضله للبناء الجديد. لكن مقولة «App Router أو لا SEO» خرافة؛ فمواقع Pages Router كثيرة تحقق ترتيباً جيداً.

أنماط العرض وما يعنيه كل منها لـSEO

يعالج Google JavaScript في ثلاث مراحل: الزحف، ثم موجة عرض مؤجلة، ثم الفهرسة. “All pages with a 200 HTTP status code are sent to the rendering queue.” (الترجمة العربية) «تُرسل كل الصفحات التي ترجع رمز HTTP 200 إلى طابور العرض». الفكرة الأساسية في Next.js هي اختيار نمط يضع المحتوى في HTML قبل موجة العرض، فلا يبقى ما تنتظره. Evidence for this claim Google crawls, renders, and indexes JavaScript pages, and successful pages can enter the rendering queue. Scope: Google Search processing, not a promise that a URL will be indexed. Confidence: high · Verified: Google: JavaScript SEO basics

  • Static Site Generation (SSG) — تُعرض الصفحات مسبقاً وقت البناء، فيتوفر HTML فوراً بلا مخاطرة طابور العرض. يلائم المحتوى الذي لا يتغير كل دقيقة. يحدد generateStaticParams() في App Router أو getStaticPaths() في Pages Router المسارات الديناميكية التي تُبنى مسبقاً.
  • Incremental Static Regeneration (ISR) — صفحات ثابتة يعاد التحقق منها بعد مدة (export const revalidate = 3600). يحصل الزاحف على HTML ثابت وTTFB منخفض مع بقاء المحتوى حديثاً. لكنه يحمل فخاً: بعد انتهاء النافذة يحصل الطلب التالي، وربما Googlebot، على الصفحة القديمة، وتُقدّم النسخة الجديدة في الطلب الذي يليه. للبيانات شديدة التقلب، كالأسعار والمخزون، يكون SSR أكثر أماناً.
  • Server-Side Rendering (SSR) — يُعرض HTML لكل طلب. يحصل الزاحف على HTML كاملاً فوراً، مقابل زمن خادم أعلى؛ لذا راقب TTFB وLCP. يؤدي export const dynamic = 'force-dynamic' أو استخدام واجهات وقت الطلب، مثل ملفات تعريف الارتباط والرؤوس، إلى اختيار SSR للمسار.
  • React Server Components، وهي افتراضية في App Router: تُعرض على الخادم ويُرسل HTML من دون JavaScript للمكوّن نفسه. يوجد المحتوى في الاستجابة الأولى بلا فجوة hydration، وهذا أفضل افتراض لـSEO. تعيش التفاعلية في Client Components الموسومة بـ'use client'.
  • Client-Side Rendering (CSR) — يحدث العرض كله في المتصفح. يستطيع Googlebot فهرسته بعد موجة العرض، بوسيط يقارب 10 ثوانٍ بينما يمتد المئين التسعون إلى ساعات، وقد تحصل زواحف أخرى، مثل Bingbot وروبوتات الذكاء الاصطناعي ومعاينات الشبكات الاجتماعية، على صفحة فارغة. في App Router يكون CSR اختيارياً بـ'use client'، وفي Pages Router تجنب جلب المحتوى الرئيسي داخل useEffect. لا تستخدمه للمحتوى الذي تريد ترتيبه.

تذكّر دائماً أن «Googlebot يستطيع عرضه» لا تعني «ينبغي أن تجعله يعرضه». العرض مكلف ومؤجل وليس متاحاً لكل الزواحف.

Metadata API في App Router

تعمل Metadata API في Server Components فقط؛ تُحل البيانات الوصفية على الخادم قبل عرض الصفحة فتصل إلى HTML الأولي. صدّر metadata من layout.js أو page.js:

export const metadata: Metadata = {
  title: 'My Page',
  description: 'Page description',
}

وعندما تعتمد الوسوم على بيانات مجلوبة، استخدم generateMetadata():

export async function generateMetadata({ params }) {
  const post = await getPost(params.slug)
  return { title: post.title, description: post.description }
}

الحقول المهمة لـSEO:

  • title — يدعم نصاً وقالباً ('%s | Brand') وقيمة افتراضية وتجاوزاً مطلقاً. اضبط القالب مرة في root layout لترثه عناوين الصفحات.
  • description و**alternates.canonical، وهي الطريقة الصحيحة لضبط الرابط الأساسي في App Router، وopenGraph** التي يجب أن تُحل صورها إلى عناوين مطلقة، و**twitter** المستخدم أيضاً في معاينات LinkedIn وSlack، و**robots** لتوجيهات index/follow وتوجيهات googleBot مثل max-snippet وmax-image-preview.
  • metadataBaseمطلوب لحل الروابط الأساسية وصور OG بصورة صحيحة. نسيانه أكثر أخطاء بيانات Next.js الوصفية شيوعاً؛ إذ تتسرب عناوين نسبية إلى canonical وOpen Graph، فتعطّل المعاينات وتشوّش إشارات الرابط الأساسي.

مثال على قالب العنوان:

// app/layout.tsx
export const metadata: Metadata = {
  metadataBase: new URL('https://example.com'),
  title: { template: '%s | Brand Name', default: 'Brand Name' },
}
// app/blog/page.tsx
export const metadata: Metadata = { title: 'My Blog Post' }
// Output: <title>My Blog Post | Brand Name</title>

فخّان. أولاً، تُدمج البيانات الوصفية سطحياً من layout إلى page؛ فكائن متداخل مثل openGraph في مقطع ابن يستبدل كائن الأب كاملاً. لذلك يحذف openGraph: { title: 'Home' } على مستوى الصفحة أي openGraph.images في layout بصمت. ثانياً، تعمل metadata في Server Components فقط؛ وتصديرها من ملف 'use client' لا يفعل شيئاً من دون تنبيه.

البيانات الوصفية المتدفقة. في الصفحات المعروضة ديناميكياً يستطيع generateMetadata بث البيانات الوصفية بعد HTML الأولي. ينفذ Googlebot JavaScript ويفحص DOM كاملاً، فيعمل التدفق معه. لكن Next.js يكتشف الروبوتات محدودة HTML، مثل Bingbot وTwitterbot وSlackbot وfacebookexternalhit، ويرسل إليها البيانات الوصفية دفعة واحدة داخل <head>. تقول وثائق Next.js: “streaming metadata is disabled for bots and crawlers that expect metadata to be in the <head> tag.” (الترجمة العربية) «يُعطّل بث البيانات الوصفية للروبوتات والزواحف التي تتوقع وجودها في وسم <head>». يحدث ذلك تلقائياً بلا إعداد. أما الصفحات المعروضة مسبقاً فتُستكمل بياناتها الوصفية وقت البناء ولا يوجد بث أصلاً. السلوك مرتبط بالإصدار، وهو حالي حتى Next.js 16.2.10؛ فأعد فحص وثائق generateMetadata عند الترقية. وبسبب اختلاف مسارات التسليم، تحقق بطريقتين: طلب إنتاج مباشر (curl -I أو عرض المصدر) وانتقال من جهة العميل إلى المسار نفسه، فقد يتغير head بينهما.

Evidence for this claim Prerendered Next.js pages do not use streaming metadata because metadata is resolved at build time in the documented path. Scope: route output, metadata and deployment Confidence: high · Verified: Metadata and OG images

البيانات الوصفية عبر next/head في Pages Router

في Pages Router تعيش البيانات الوصفية داخل <Head> من next/head:

import Head from 'next/head'

export default function Page() {
  return (
    <>
      <Head>
        <title>My Page | Brand</title>
        <meta name="description" content="Description" />
        <link rel="canonical" href="https://example.com/my-page" />
      </Head>
      {/* page content */}
    </>
  )
}

اضبط العنوان والوصف لكل صفحة، لا في _app.js وحده، وضع canonical لكل صفحة بما فيها نسخ التقسيم إلى صفحات. توحّد حزمة next-seo ذلك عبر مكوّن <NextSeo> ومساعدات البيانات المنظمة. ويعني الانتقال إلى App Router غالباً استبدال next/head وnext-seo بتصدير metadata الأصلي.

هل تحتاج إلى حزمة next-seo؟ إنها إضافة خارجية وليست جزءاً من Next.js، وما زالت تُصان بنشاط عند v7.2.0 وقت الكتابة وليست مؤرشفة. تحدد وثائقها موضعها بوضوح: للوسوم الوصفية القياسية في App Router يوصي README باستخدام generateMetadata/metadata المدمجين بدلاً من <NextSeo>، أما في Pages Router فيظل <NextSeo> طبقة مساعدة مقبولة فوق next/head. وتبقى مكوّنات JSON-LD المساعدة، مثل ArticleJsonLd وFAQPageJsonLd مع useAppDir، حالة استخدام في App Router تفضلها بعض الفرق على كتابة <script type="application/ld+json"> يدوياً. الخلاصة: في بناء App Router جديد ابدأ بـMetadata API الأصلية؛ الحزمة اختيارية وليست شرطاً، وهذا ما يقوله القائمون عليها.

خرائط الموقع

في App Router يمثل app/sitemap.ts اصطلاح ملف ينتج /sitemap.xml:

import type { MetadataRoute } from 'next'

export default function sitemap(): MetadataRoute.Sitemap {
  return [
    { url: 'https://acme.com', lastModified: new Date(), priority: 1 },
    { url: 'https://acme.com/blog', lastModified: new Date(), priority: 0.8 },
  ]
}

في المواقع الكبيرة يقسم generateSitemaps() الخريطة إلى ملفات عدة، إذ يحد Google كل خريطة بـ50 000 عنوان URL، ويُقدّم كل ملف عند /.../sitemap/[id].xml. يدعم الناتج أيضاً خرائط الصور والفيديو وalternates.languages الموطنة. وفي Pages Router استخدم next-sitemap أو أنشئ pages/sitemap.xml.js عبر getServerSideProps.

robots.txt

ينشئ app/robots.ts ملف robots برمجياً:

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [{ userAgent: '*', allow: '/', disallow: '/private/' }],
    sitemap: 'https://acme.com/sitemap.xml',
  }
}

يدعم قواعد لكل user-agent وخرائط موقع متعددة. يستخدم Pages Router ملفاً ثابتاً هو public/robots.txt. والقاعدة التي لا تحتمل الخطأ في النظامين: لا تحظر JavaScript أو CSS أبداً؛ فلن يعرض Google الملفات المحظورة، وقد يؤدي ذلك في إطار JS إلى صفحة فارغة.

next/image ومؤشرات Core Web Vitals

يُعد next/image من أقوى أسباب استخدام الإطار لـSEO. فهو يحمّل صور ما تحت الجزء المرئي كسولاً، ويتطلب width/height أو fill لحجز المساحة ومنع انزياح التخطيط / CLS، ويقدم WebP/AVIF تلقائياً، وينشئ srcset صحيحاً من الخاصية sizes. أهم تحسين منفرد لـCWV هو priority على صورة البطل أو الصورة أعلى الجزء المرئي، إذ يحمّلها مسبقاً لتحسين LCP:

<Image src="/hero.jpg" width={1200} height={630} priority alt="Hero" />

نسيان priority لصورة LCP أكثر أخطاء CWV شيوعاً في Next.js، وهذه المشكلات واسعة الانتشار في المواقع الفعلية؛ راجع بيانات Salt Agency في تبويب Stats. حقل alt مطلوب: فارغ للصور الزخرفية ووصفي لصور المحتوى.

يعرض next/link مراسي <a href> قياسية في HTML، فيتبعها Google طبيعياً، ويضيف تنقلاً من جهة العميل وجلباً مسبقاً في الخلفية للروابط الظاهرة في الإنتاج. القاعدة بسيطة: استخدم next/link للروابط الداخلية، ولا تستبدله بمعالج onClick أو تنقل JavaScript لا ينتج مرساة حقيقية؛ فتلك الروابط غير قابلة للزحف. استخدم prefetch={false} للروابط منخفضة القيمة لتوفير النطاق عند الحاجة.

المسارات الديناميكية وgenerateStaticParams

يخبر generateStaticParams() إطار Next.js بالمسارات الديناميكية التي يعرضها مسبقاً وقت البناء:

// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getPosts()
  return posts.map((post) => ({ slug: post.slug }))
}

الصفحات المبنية هكذا HTML ثابت بالكامل، وهو الأفضل لـSEO. من دونه تُعرض المسارات الديناميكية عند الطلب عبر SSR افتراضياً، وهذا صالح لكنه يعيد زمن الخادم. ادمجه مع revalidate وISR للمحتوى المتجدد، وتأكد من إدراج كل العناوين الديناميكية المهمة كي لا ينتظر شيء طابور العرض.

البيانات المنظمة (JSON-LD)

لا تحتوي Metadata API حقلاً للبيانات المنظمة؛ بل تحقن JSON-LD كوسم <script> في Server Component ليبقى في HTML المعروض على الخادم بلا كلفة على حزمة العميل:

const jsonLd = { '@context': 'https://schema.org', '@type': 'Article', /* … */ }
return <script type="application/ld+json"
  dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />

الأنواع المعتادة هي Article/BlogPosting وBreadcrumbList وProduct وFAQPage. تحقق منها باختبار النتائج المنسقة بعد أي تغيير في العرض.

أخطاء SEO الشائعة في Next.js

مستخلصة من عمليات تدقيق فعلية والأنماط السابقة:

  1. روابط canonical مفقودة أو نسبية، غالباً بسبب نسيان metadataBase، ما يعطل أيضاً عناوين صور OG.
  2. غياب priority عن صورة LCP، وهو أكبر إخفاق في CWV.
  3. صفحات 404 ترجع 200؛ استخدم notFound() المدمج لإرجاع الحالة الصحيحة، إذ تنتشر أخطاء 404 المرنة في مواقع Next.js.
  4. تصدير metadata من Client Component؛ لا يفعل شيئاً بصمت لأنه خاص بـServer Components.
  5. CSR للمحتوى الرئيسي؛ فجلب المحتوى الحرج في useEffect يمنح الزواحف غير Google صفحات فارغة.
  6. توجيه التجزئة (#) بدلاً من History API؛ فلا يمكن زحف تلك العروض منفصلة.
  7. عدم تصدير generateStaticParams؛ فتُعرض المسارات عند الطلب بدلاً من بنائها مسبقاً.
  8. استبدال openGraph عبر وراثة layout؛ تستبدل المقاطع الأبناء الكائن ولا تدمجه.
  9. حظر JS/CSS في robots.txt أو بسياسة Content Security Policy تمنع Chrome عديم الرأس لدى Googlebot من تحميل النصوص؛ اختبر ذلك عبر URL Inspection.

ملاحظات النشر

تطوّر Vercel إطار Next.js، والاستضافة لديها تمنح تكاملاً وثيقاً، مثل CDN طرفي للصفحات الثابتة وISR وTTFB جيد، لكنها ليست مطلوبة. عرّف التحويلات الدائمة في redirects() داخل next.config.js، فتُرجع 308 أو 301 مع permanent: true، واضبط رؤوس الأمان والتخزين عبر headers()، واستخدم رأس X-Robots-Tag لقواعد noindex حسب المسار عندما يصعب استخدام بيانات robots لكل صفحة.

ما الذي تفحصه وأين. لا تظهر حالة التخزين والرموز والتحويلات والبيانات المتدفقة كلها في الاختبار نفسه؛ فقد يبدو المسار سليماً في فحص ومعطلاً في آخر:

الفحصموضع الفحصسبب اختلافه عما تراه معروضاً
عمر التخزين/إعادة التحققرؤوس استجابة طلب مباشر (curl -I)قد يقدم ISR صفحة قديمة للطلب التالي مباشرة بعد انتهاء النافذة
حالة HTTP المباشرةcurl -I على عنوان الإنتاج لا الواجهة المعروضةعرض «غير موجود» بلا notFound() يظل يرجع 200
سلوك التحويلالسياق الفعلي الذي يشغله: redirect() في Server Action أو Route Handler مقابل onClick للعميلتختلف الحالة ومسار الاستجابة بحسب سياق الاستدعاء لا الوجهة وحدها
البيانات الوصفية المتدفقةطلب مباشر وانتقال عميل إلى المسار نفسهقد يتلقى العميل العادي تدفقاً فيما تتلقى روبوتات HTML البيانات كاملة داخل رأس الصفحة
انتقالات جهة العميلتنقل داخل التطبيق ثم افحص <head> ثانيةقد ينحرف مسار صحيح عند التحميل الأول بعد انتقال العميل

لا يضمن الإطار أياً من ذلك؛ يمنحك Next.js آليات مثل redirects() وnotFound() وإعادة التحقق والتدفق، لكن مفاتيح التخزين والإبطال وحالة المعاينة وإعداد النشر تظل مسؤوليتك، ويجب اختبارها في الإنتاج لا محلياً فقط.

ملاحظة أخيرة بشأن العرض الديناميكي، أي تقديم HTML معروض مسبقاً للروبوتات وJavaScript للمستخدمين. أوقف Google التوصية به: “dynamic rendering was a workaround and not a long-term solution.” (الترجمة العربية) «كان العرض الديناميكي حلاً التفافياً لا حلاً طويل الأمد». لا تحتاجه في Next.js؛ إذ تضع SSR وSSG وISR وServer Components المحتوى أصلاً في HTML. اذكره لتتعرف إليه في التدقيق، ولا تبنِ عليه.

المسار السليم هو App Router مع Server Components وISR وMetadata API مع metadataBase، وnext/image مع priority. اضبطها جيداً فتكون قد عالجت معظم SEO في Next.js.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.