إعادة التوجيه (rewrites)

تسمح لك إعادة التوجيه (rewrites) بتعيين مسار طلب وارد إلى مسار وجهة مختلف.

تعمل إعادة التوجيه كوكيل URL وتخفي مسار الوجهة، مما يجعل المستخدم يبدو وكأنه لم يغير موقعه على الموقع. على العكس من ذلك، فإن إعادة التوجيه (redirects) ستعيد توجيه إلى صفحة جديدة وتظهر تغييرات URL.

لاستخدام إعادة التوجيه، يمكنك استخدام مفتاح rewrites في ملف next.config.js:

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/about',
        destination: '/',
      },
    ]
  },
}

يتم تطبيق إعادة التوجيه على التوجيه من جانب العميل، حيث سيتم تطبيق إعادة التوجيه على <Link href="/about"> في المثال أعلاه.

rewrites هي دالة غير متزامنة تتوقع إرجاع مصفوفة أو كائن من المصفوفات (انظر أدناه) يحتوي على كائنات بخصائص source و destination:

  • source: String - هو نمط مسار الطلب الوارد.
  • destination: String - المسار الذي تريد التوجيه إليه.
  • basePath: false أو undefined - إذا كانت القيمة false فلن يتم تضمين basePath عند المطابقة، يمكن استخدامها لإعادة التوجيه الخارجية فقط.
  • locale: false أو undefined - ما إذا كان يجب عدم تضمين اللغة المحلية عند المطابقة.
  • has هي مصفوفة من كائنات has بخصائص type و key و value.
  • missing هي مصفوفة من كائنات missing بخصائص type و key و value.

عندما ترجع دالة rewrites مصفوفة، يتم تطبيق إعادة التوجيه بعد التحقق من نظام الملفات (الصفحات وملفات /public) وقبل المسارات الديناميكية. عندما ترجع الدالة كائنًا من المصفوفات بشكل محدد، يمكن تغيير هذا السلوك والتحكم فيه بدقة أكبر، اعتبارًا من إصدار v10.1 من Next.js:

next.config.js
module.exports = {
  async rewrites() {
    return {
      beforeFiles: [
        // يتم التحقق من إعادة التوجيه هذه بعد الرؤوس/إعادة التوجيه
        // وقبل جميع الملفات بما في ذلك ملفات _next/public مما
        // يسمح بتجاوز ملفات الصفحات
        {
          source: '/some-page',
          destination: '/somewhere-else',
          has: [{ type: 'query', key: 'overrideMe' }],
        },
      ],
      afterFiles: [
        // يتم التحقق من إعادة التوجيه هذه بعد التحقق من ملفات الصفحات/العامة
        // ولكن قبل المسارات الديناميكية
        {
          source: '/non-existent',
          destination: '/somewhere-else',
        },
      ],
      fallback: [
        // يتم التحقق من إعادة التوجيه هذه بعد التحقق من ملفات الصفحات/العامة
        // والمسارات الديناميكية
        {
          source: '/:path*',
          destination: `https://my-old-site.com/:path*`,
        },
      ],
    }
  },
}

معلومة مفيدة: إعادة التوجيه في beforeFiles لا تتحقق من نظام الملفات/المسارات الديناميكية مباشرة بعد مطابقة المصدر، بل تستمر حتى يتم التحقق من جميع beforeFiles.

ترتيب التحقق من المسارات في Next.js هو:

  1. يتم التحقق من الرؤوس وتطبيقها
  2. يتم التحقق من إعادة التوجيه وتطبيقها
  3. يتم التحقق من إعادة التوجيه beforeFiles وتطبيقها
  4. يتم التحقق من الملفات الثابتة من الدليل العام، وملفات _next/static، والصفحات غير الديناميكية وتقديمها
  5. يتم التحقق من إعادة التوجيه afterFiles وتطبيقها، إذا تمت مطابقة إحدى إعادة التوجيه هذه، نتحقق من المسارات الديناميكية/الملفات الثابتة بعد كل مطابقة
  6. يتم التحقق من إعادة التوجيه fallback وتطبيقها، يتم تطبيق هذه قبل عرض صفحة 404 وبعد التحقق من جميع المسارات الديناميكية/الأصول الثابتة. إذا كنت تستخدم fallback: true/'blocking' في getStaticPaths، فلن يتم تشغيل إعادة التوجيه fallback المحددة في ملف next.config.js.

معلمات إعادة التوجيه

عند استخدام المعلمات في إعادة التوجيه، يتم تمرير المعلمات في الاستعلام افتراضيًا عندما لا يتم استخدام أي من المعلمات في destination.

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/old-about/:path*',
        destination: '/about', // لا يتم استخدام معلمة :path هنا لذا سيتم تمريرها تلقائيًا في الاستعلام
      },
    ]
  },
}

إذا تم استخدام معلمة في الوجهة، فلن يتم تمرير أي من المعلمات تلقائيًا في الاستعلام.

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/docs/:path*',
        destination: '/:path*', // يتم استخدام معلمة :path هنا لذا لن يتم تمريرها تلقائيًا في الاستعلام
      },
    ]
  },
}

لا يزال بإمكانك تمرير المعلمات يدويًا في الاستعلام إذا تم استخدام واحد بالفعل في الوجهة عن طريق تحديد الاستعلام في destination.

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/:first/:second',
        destination: '/:first?second=:second',
        // نظرًا لأن المعلمة :first مستخدمة في الوجهة، لن تتم إضافة المعلمة :second
        // تلقائيًا في الاستعلام على الرغم من أنه يمكننا إضافتها يدويًا
        // كما هو موضح أعلاه
      },
    ]
  },
}

معلومة مفيدة: سيتم تحليل معلمات الصفحات الثابتة من التحسين الثابت التلقائي أو التجهيز المسبق من إعادة التوجيه على العميل بعد التميؤ وتوفيرها في الاستعلام.

مطابقة المسار

يُسمح بمطابقة المسار، على سبيل المثال /blog/:slug ستطابق /blog/hello-world (بدون مسارات متداخلة):

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/blog/:slug',
        destination: '/news/:slug', // يمكن استخدام المعلمات المتطابقة في الوجهة
      },
    ]
  },
}

مطابقة مسار Wildcard

لمطابقة مسار wildcard، يمكنك استخدام * بعد معلمة، على سبيل المثال /blog/:slug* ستطابق /blog/a/b/c/d/hello-world:

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/blog/:slug*',
        destination: '/news/:slug*', // يمكن استخدام المعلمات المتطابقة في الوجهة
      },
    ]
  },
}

مطابقة مسار Regex

لمطابقة مسار regex، يمكنك لف regex بين قوسين بعد معلمة، على سبيل المثال /blog/:slug(\\d{1,}) ستطابق /blog/123 ولكن ليس /blog/abc:

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/old-blog/:post(\\d{1,})',
        destination: '/blog/:post', // يمكن استخدام المعلمات المتطابقة في الوجهة
      },
    ]
  },
}

يتم استخدام الأحرف التالية (, ), {, }, [, ], |, \, ^, ., :, *, +, -, ?, $ لمطابقة مسار regex، لذا عند استخدامها في source كقيم غير خاصة، يجب تهريبها بإضافة \\ قبلها:

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        // هذا سيطابق `/english(default)/something` عند الطلب
        source: '/english\\(default\\)/:slug',
        destination: '/en-us/:slug',
      },
    ]
  },
}

مطابقة الرأس، الكوكي، والاستعلام

لمطابقة إعادة توجيه فقط عندما تتطابق قيم الرأس أو الكوكي أو الاستعلام مع حقل has أو لا تتطابق مع حقل missing، يمكن استخدام has و missing. يجب أن تتطابق كل من source وجميع عناصر has وألا تتطابق جميع عناصر missing لتطبيق إعادة التوجيه.

يمكن أن تحتوي عناصر has و missing على الحقول التالية:

  • type: String - يجب أن يكون إما header أو cookie أو host أو query.
  • key: String - المفتاح من النوع المحدد للمطابقة ضده.
  • value: String أو undefined - القيمة للتحقق منها، إذا كانت undefined فستطابق أي قيمة. يمكن استخدام سلسلة تشبه regex لالتقاط جزء معين من القيمة، على سبيل المثال إذا تم استخدام القيمة first-(?<paramName>.*) لـ first-second فسيتم استخدام second في الوجهة مع :paramName.
next.config.js
module.exports = {
  async rewrites() {
    return [
      // إذا كان الرأس `x-rewrite-me` موجودًا،
      // سيتم تطبيق إعادة التوجيه هذه
      {
        source: '/:path*',
        has: [
          {
            type: 'header',
            key: 'x-rewrite-me',
          },
        ],
        destination: '/another-page',
      },
      // إذا كان الرأس `x-rewrite-me` غير موجود،
      // سيتم تطبيق إعادة التوجيه هذه
      {
        source: '/:path*',
        missing: [
          {
            type: 'header',
            key: 'x-rewrite-me',
          },
        ],
        destination: '/another-page',
      },
      // إذا تمت مطابقة المصدر والاستعلام والكوكي،
      // سيتم تطبيق إعادة التوجيه هذه
      {
        source: '/specific/:path*',
        has: [
          {
            type: 'query',
            key: 'page',
            // لن تكون قيمة الصفحة متاحة في
            // الوجهة نظرًا لتوفير القيمة وعدم
            // استخدام مجموعة التقاط مسماة مثل (?<page>home)
            value: 'home',
          },
          {
            type: 'cookie',
            key: 'authorized',
            value: 'true',
          },
        ],
        destination: '/:path*/home',
      },
      // إذا كان الرأس `x-authorized` موجودًا و
      // يحتوي على قيمة مطابقة، سيتم تطبيق إعادة التوجيه هذه
      {
        source: '/:path*',
        has: [
          {
            type: 'header',
            key: 'x-authorized',
            value: '(?<authorized>yes|true)',
          },
        ],
        destination: '/home?authorized=:authorized',
      },
      // إذا كان المضيف هو `example.com`،
      // سيتم تطبيق إعادة التوجيه هذه
      {
        source: '/:path*',
        has: [
          {
            type: 'host',
            value: 'example.com',
          },
        ],
        destination: '/another-page',
      },
    ]
  },
}

إعادة التوجيه إلى URL خارجي

أمثلة

تسمح لك إعادة التوجيه بإعادة التوجيه إلى URL خارجي. هذا مفيد بشكل خاص للتبني التدريجي لـ Next.js. فيما يلي مثال لإعادة توجيه مسار /blog من تطبيقك الرئيسي إلى موقع خارجي.

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/blog',
        destination: 'https://example.com/blog',
      },
      {
        source: '/blog/:slug',
        destination: 'https://example.com/blog/:slug', // يمكن استخدام المعلمات المتطابقة في الوجهة
      },
    ]
  },
}

إذا كنت تستخدم trailingSlash: true، فأنت بحاجة أيضًا إلى إضافة شرطة مائلة في نهاية المعلمة source. إذا كان خادم الوجهة يتوقع أيضًا شرطة مائلة في النهاية، فيجب تضمينها في المعلمة destination.

next.config.js
module.exports = {
  trailingSlash: true,
  async rewrites() {
    return [
      {
        source: '/blog/',
        destination: 'https://example.com/blog/',
      },
      {
        source: '/blog/:path*/',
        destination: 'https://example.com/blog/:path*/',
      },
    ]
  },
}

التبني التدريجي لـ Next.js

يمكنك أيضًا جعل Next.js يعود إلى الوكيل لموقع ويب موجود بعد التحقق من جميع مسارات Next.js.

بهذه الطريقة لا تحتاج إلى تغيير تكوين إعادة التوجيه عند نقل المزيد من الصفحات إلى Next.js

next.config.js
module.exports = {
  async rewrites() {
    return {
      fallback: [
        {
          source: '/:path*',
          destination: `https://custom-routes-proxying-endpoint.vercel.app/:path*`,
        },
      ],
    }
  },
}

إعادة التوجيه مع دعم basePath

عند استخدام دعم basePath مع إعادة التوجيه، يتم إضافة basePath تلقائيًا إلى كل source و destination ما لم تضيف basePath: false إلى إعادة التوجيه:

next.config.js
module.exports = {
  basePath: '/docs',

  async rewrites() {
    return [
      {
        source: '/with-basePath', // يصبح تلقائيًا /docs/with-basePath
        destination: '/another', // يصبح تلقائيًا /docs/another
      },
      {
        // لا يضيف /docs إلى /without-basePath نظرًا لتعيين basePath: false
        // ملاحظة: لا يمكن استخدام هذا لإعادة التوجيه الداخلية مثل `destination: '/another'`
        source: '/without-basePath',
        destination: 'https://example.com',
        basePath: false,
      },
    ]
  },
}

إعادة التوجيه مع دعم i18n

عند استخدام دعم i18n مع إعادة التوجيه، يتم إضافة البادئات تلقائيًا إلى source و destination للتعامل مع locales المكونة ما لم تضيف locale: false إلى إعادة التوجيه. إذا تم استخدام locale: false، يجب إضافة بادئة اللغة إلى source و destination لمطابقتها بشكل صحيح.

next.config.js
module.exports = {
  i18n: {
    locales: ['en', 'fr', 'de'],
    defaultLocale: 'en',
  },

  async rewrites() {
    return [
      {
        source: '/with-locale', // يتعامل تلقائيًا مع جميع اللغات
        destination: '/another', // يمرر اللغة تلقائيًا
      },
      {
        // لا يتعامل مع اللغات تلقائيًا نظرًا لتعيين locale: false
        source: '/nl/with-locale-manual',
        destination: '/nl/another',
        locale: false,
      },
      {
        // هذا يطابق '/' لأن `en` هي defaultLocale
        source: '/en',
        destination: '/en/another',
        locale: false,
      },
      {
        // من الممكن مطابقة جميع اللغات حتى عند تعيين locale: false
        source: '/:locale/api-alias/:path*',
        destination: '/api/:path*',
        locale: false,
      },
      {
        // يتم تحويل هذا إلى /(en|fr|de)/(.*) لذا لن يطابق
        // المسارات العلوية `/` أو `/fr` مثل /:path*
        source: '/(.*)',
        destination: '/another',
      },
    ]
  },
}

سجل الإصدارات

الإصدارالتغييرات
v13.3.0تم إضافة missing.
v10.2.0تم إضافة has.
v9.5.0تم إضافة العناوين.