إعادة التوجيه (rewrites)
تسمح لك إعادة التوجيه (rewrites) بتعيين مسار طلب وارد إلى مسار وجهة مختلف.
تعمل إعادة التوجيه كوكيل URL وتخفي مسار الوجهة، مما يجعل المستخدم يبدو وكأنه لم يغير موقعه على الموقع. على النقيض من ذلك، فإن عمليات إعادة التوجيه (redirects) ستوجه إلى صفحة جديدة وتظهر تغييرات URL.
لاستخدام إعادة التوجيه، يمكنك استخدام مفتاح rewrites
في ملف 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
) وقبل المسارات الديناميكية. عندما ترجع دالة rewrites
كائنًا من المصفوفات بشكل محدد، يمكن تغيير هذا السلوك والتحكم فيه بدقة أكبر، بدءًا من إصدار v10.1
من Next.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 هو:
- يتم التحقق من الرؤوس (headers) وتطبيقها
- يتم التحقق من عمليات إعادة التوجيه (redirects) وتطبيقها
- يتم التحقق من عمليات إعادة التوجيه
beforeFiles
وتطبيقها - يتم التحقق من الملفات الثابتة من الدليل العام (public directory)، وملفات
_next/static
، والصفحات غير الديناميكية وتقديمها - يتم التحقق من عمليات إعادة التوجيه
afterFiles
وتطبيقها، إذا تمت مطابقة إحدى عمليات إعادة التوجيه هذه، نتحقق من المسارات الديناميكية/الملفات الثابتة بعد كل مطابقة - يتم التحقق من عمليات إعادة التوجيه
fallback
وتطبيقها، يتم تطبيق هذه قبل عرض صفحة 404 وبعد التحقق من جميع المسارات الديناميكية/الأصول الثابتة. إذا كنت تستخدم fallback: true/'blocking' فيgetStaticPaths
، فلن يتم تشغيل عمليات إعادة التوجيهfallback
المحددة في ملفnext.config.js
.
معلمات إعادة التوجيه
عند استخدام المعلمات في إعادة التوجيه، يتم تمرير المعلمات في الاستعلام افتراضيًا عندما لا يتم استخدام أي من المعلمات في destination
.
module.exports = {
async rewrites() {
return [
{
source: '/old-about/:path*',
destination: '/about', // معلمة :path غير مستخدمة هنا لذا سيتم تمريرها تلقائيًا في الاستعلام
},
]
},
}
إذا تم استخدام معلمة في الوجهة، فلن يتم تمرير أي من المعلمات تلقائيًا في الاستعلام.
module.exports = {
async rewrites() {
return [
{
source: '/docs/:path*',
destination: '/:path*', // معلمة :path مستخدمة هنا لذا لن يتم تمريرها تلقائيًا في الاستعلام
},
]
},
}
لا يزال بإمكانك تمرير المعلمات يدويًا في الاستعلام إذا تم استخدام أحدها بالفعل في الوجهة عن طريق تحديد الاستعلام في destination
.
module.exports = {
async rewrites() {
return [
{
source: '/:first/:second',
destination: '/:first?second=:second',
// نظرًا لأن معلمة :first مستخدمة في الوجهة، لن تتم إضافة معلمة :second
// تلقائيًا في الاستعلام على الرغم من أنه يمكننا إضافتها يدويًا
// كما هو موضح أعلاه
},
]
},
}
معلومة جيدة: سيتم تحليل معلمات الصفحات الثابتة من التحسين الثابت التلقائي (Automatic Static Optimization) أو التقديم المسبق (prerendering) من عمليات إعادة التوجيه على العميل بعد الترطيب وتوفيرها في الاستعلام.
مطابقة المسار
يُسمح بمطابقة المسارات، على سبيل المثال /blog/:slug
ستطابق /blog/hello-world
(بدون مسارات متداخلة):
module.exports = {
async rewrites() {
return [
{
source: '/blog/:slug',
destination: '/news/:slug', // يمكن استخدام المعلمات المطابقة في الوجهة
},
]
},
}
مطابقة مسار Wildcard
لمطابقة مسار wildcard، يمكنك استخدام *
بعد معلمة، على سبيل المثال /blog/:slug*
ستطابق /blog/a/b/c/d/hello-world
:
module.exports = {
async rewrites() {
return [
{
source: '/blog/:slug*',
destination: '/news/:slug*', // يمكن استخدام المعلمات المطابقة في الوجهة
},
]
},
}
مطابقة مسار Regex
لمطابقة مسار regex، يمكنك لف regex بين قوسين بعد معلمة، على سبيل المثال /blog/:slug(\\d{1,})
ستطابق /blog/123
ولكن ليس /blog/abc
:
module.exports = {
async rewrites() {
return [
{
source: '/old-blog/:post(\\d{1,})',
destination: '/blog/:post', // يمكن استخدام المعلمات المطابقة في الوجهة
},
]
},
}
الرموز التالية (
, )
, {
, }
, :
, *
, +
, ?
تُستخدم لمطابقة مسار regex، لذا عند استخدامها في source
كقيم غير خاصة، يجب تهريبها بإضافة \\
قبلها:
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
.
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',
// لن تكون قيمة الصفحة متاحة في
// الوجهة نظرًا لتوفير القيمة وعدم
// استخدام مجموعة capture مسماة مثل (?<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
من تطبيقك الرئيسي إلى موقع خارجي.
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
.
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
module.exports = {
async rewrites() {
return {
fallback: [
{
source: '/:path*',
destination: `https://custom-routes-proxying-endpoint.vercel.app/:path*`,
},
],
}
},
}
إعادة التوجيه مع دعم basePath
عند الاستفادة من دعم basePath
مع إعادة التوجيه، يتم إضافة basePath
تلقائيًا إلى كل source
و destination
ما لم تقم بإضافة basePath: false
إلى إعادة التوجيه:
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
مع إعادة التوجيه، يتم إضافة locales
المكونة تلقائيًا إلى كل source
و destination
ما لم تقم بإضافة locale: false
إلى إعادة التوجيه. إذا تم استخدام locale: false
، فيجب إضافة بادئة source
و destination
بلغة لمطابقتها بشكل صحيح.
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 | تمت إضافة الرؤوس. |