قواعد إعادة التوجيه في htaccess لـ WordPress: كيف تكتب 301 صحيحًا
انقل رابطًا دون أن تفقد ترتيبه: أي توجيه htaccess تستخدم في WordPress، وأين يجب أن توضع القواعد، وكيف تختبرها دون أن تُقفل خارج موقعك.
نُشر
تنقل رابطًا وتريد للقديم أن يحتفظ بترتيبه؟ تحتاج إلى 301 في .htaccess، موضوعًا حيث لا يمحوه WordPress، ومكتوبًا بالتوجيه الصحيح، ومشيرًا إلى الرابط الذي يقدّمه WordPress فعلًا. أخطئ في أي من هذه الثلاثة وتحصل على سلسلة إعادة توجيه، أو حلقة، أو خطأ 500 يقفلك خارج wp-admin.
القرار، ثم قاعدة الموضع، ثم الاختبار، بهذا الترتيب.
أي توجيه: Redirect أم RedirectMatch أم RewriteRule
هناك وحدتان في Apache تعملان هنا، وهما ليستا بديلتين إحداهما عن الأخرى.
Redirect (mod_alias) — مسار واحد معروف إلى وجهة واحدة. أبسط ما يؤدي الغرض:
Redirect 301 /old-page/ https://example.com/new-page/
سلوكان يجب معرفتهما. إنه يطابق على بادئة مسار لا على نص مطابق تمامًا، فـ /old-page/ يلتقط أيضًا /old-page/anything/ ويلحق /anything/ بالوجهة. كما أنه ينقل سلسلة الاستعلام تلقائيًا.
RedirectMatch (mod_alias) — تعبير نمطي واحد يغطي روابط كثيرة. هكذا تنقل قسمًا بأكمله:
RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1
مجموعة الالتقاط (.*) تصبح $1 في الوجهة، فيصل /blog/hello-world/ إلى /articles/hello-world/. لاحظ الشرطة المائلة في البداية: أنماط mod_alias تطابق مسار الرابط كاملًا.
RewriteRule (mod_rewrite) — مطلوب حين تعتمد إعادة التوجيه على شرط: اسم المضيف، أو البروتوكول، أو سلسلة استعلام، أو وكيل المستخدم. لا شيء في mod_alias يستطيع اختبار ذلك.
RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]
النمط داخل .htaccess على مستوى المجلد تُحذف منه الشرطة المائلة الأولى — ولهذا هو ^(.*)$ هنا و ^/blog/ في مثال mod_alias. الخلط بين الاثنين هو السبب الأول لكون قاعدة منسوخة لا تفعل شيئًا في صمت.
اجعل mod_alias خيارك الافتراضي. ولا تلجأ إلى RewriteRule إلا حين تحتاج إلى شرط. تعبيرات نمطية أقل، وفرص خطأ أقل.
أين يجب أن توضع القواعد
إعادات التوجيه المخصصة موضعها فوق سطر # BEGIN WordPress. لا داخله.
# --- custom redirects ---
Redirect 301 /old-page/ https://example.com/new-page/
RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1
# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
سببان مستقلان:
- WordPress يعيد كتابة كتلته الخاصة. حفظ الإعدادات ← الروابط الدائمة يستدعي
flush_rewrite_rules()، الذي يتخلص من كل ما بين العلامتين ويعيد توليده. فالقاعدة التي تضعها بالداخل تختفي في المرة التالية التي يلمس فيها أحدهم تلك الشاشة — ربما بعد شهور، دون أن يربط أحد بين الحدثين. - الترتيب يحدد من يفوز. كتلة WordPress تنتهي بقاعدة شاملة تُرسل كل طلب ليس ملفًا ولا مجلدًا إلى
index.php. وRewriteRuleموضوعة بعدها لن تعمل أبدًا.
تعقيد واحد يجدر قوله بصراحة: mod_alias و mod_rewrite لا تعملان في الحقيقة بترتيب الملف. فـ Apache يعالج mod_alias أثناء ترجمة الرابط، ويعالج mod_rewrite على مستوى المجلد لاحقًا في مرحلة التصحيح — فقد يفوز سطر Redirect على RewriteRule يظهر فوقه في الملف. وإن وجدت نفسك محتاجًا إلى الاثنين على مسارات متداخلة، فاختر وحدة واحدة لذلك المسار والتزم بها. تتبّع خلاف بين mod_alias و mod_rewrite لا يستحق ساعة من وقتك.
الشرطات المائلة في النهاية
إعادة التوجيه القانونية في WordPress نفسها تضيف شرطة مائلة إلى نهاية معظم الروابط الدائمة. فهذا:
Redirect 301 /old-page/ https://example.com/new-page
ينتج قفزتين — توجيه 301 الخاص بك إلى الرابط بلا شرطة، ثم توجيه 301 الخاص بـ WordPress يضيف الشرطة. السلاسل ما زالت تمرّر إشارات الترتيب، لكنها تهدر ميزانية الزحف وتضيف رحلة ذهاب وإياب لكل زائر.
حمّل الوجهة في متصفح أولًا، وانسخ الرابط تمامًا كما يستقر في شريط العنوان، واستخدمه. وإن كانت بنية روابطك الدائمة تنتهي بـ .html أو بلا شرطة مائلة، فطابق ذلك بدلًا منه. لا توجد إجابة صحيحة عالميًا هنا، بل فقط «طابق ما يقدّمه WordPress».
الاختبار دون أن تقفل نفسك خارجًا
يُقرأ .htaccess مع كل طلب. وخطأ الصياغة يُرجع 500 للموقع كله بما فيه wp-admin، فيجب أن يكون طريق التعافي موجودًا قبل أن تحتاج إليه.
انسخ الملف احتياطيًا أولًا. عبر SSH:
cp /path/to/wordpress/.htaccess /path/to/wordpress/.htaccess.bak
فإن أعطى الموقع 500، أعد تسمية الملف السليم فوق المعطوب فيعود الموقع فورًا. واترك جلسة SFTP مفتوحة في نافذة أخرى — فتسجيل الدخول من الصفر والموقع معطّل هو حيث يبدأ الذعر. ولاحظ أن apachectl configtest لا يحلل .htaccess، فسيبلغ عن إعداد سليم بينما موقعك ميت.
اختبر بـ curl لا بمتصفح. المتصفحات تخزّن استجابات 301 بشدة وستعرض عليك بكل رضا نتيجة الأمس:
curl -sI https://example.com/old-page/ | head -n 5
اقرأ أمرين: سطر الحالة يجب أن يقول 301، وترويسة Location: يجب أن تكون الرابط النهائي بالضبط. ولرؤية السلسلة كاملة، تتبّعها:
curl -sIL https://example.com/old-page/ | grep -E '^(HTTP|Location)'
وجود أكثر من سطر HTTP/1.1 301 يعني أنك بنيت سلسلة. وأكثر من خمسة تقريبًا يعني أنك بنيت حلقة، وسيتوقف curl ويخبرك بذلك.
استخدم 302 ما دمت غير واثق. فـ 302 لا يُخزَّن بالطريقة نفسها، فالخطأ يصبح قابلًا للتراجع في ثوانٍ. انتقل إلى 301 حين يُظهر curl الوجهة التي قصدتها. هذا لا يكلّف شيئًا وقد أنقذ من المواقع أكثر مما أنقذت أي عادة أخرى في هذه المقالة.
وإن بدت قاعدة كأنها لا تفعل شيئًا البتة، فتأكّد أن Apache يقرأ الملف أصلًا. فـ AllowOverride None على المجلد يجعل Apache يتجاهل .htaccess كليًا، و nginx يتجاهله تمامًا — افحص ترويسة Server: بـ curl -I قبل أن تصحّح التعبيرات النمطية. ولتجميع الكتلة بالصياغة الصحيحة لإصدار Apache لديك ومسار التثبيت، استخدم مولّد htaccess لـ WordPress.
متى لا تستخدم htaccess إطلاقًا
لعدد قليل من إعادات التوجيه التي يحتاج المحررون إلى إدارتها بأنفسهم، تكون إضافة إعادة توجيه تخزّن القواعد في قاعدة البيانات هي الأداة الأفضل — فهي تنجو من ترحيل الاستضافة ولا تتطلب SSH. والمقايضة حقيقية: يجب أن يُقلَع php لتقديم إعادة التوجيه، وهذا أبطأ من إجابة Apache مباشرة.
استخدم .htaccess للنقلات البنيوية الدائمة — تغيير نطاق، أو إعادة تسمية قسم، أو فرض https أو www. واستخدم إضافة لإعادات التوجيه التحريرية المتفرقة. والجمع بينهما لا بأس به ما دمت تعرف أي طبقة تملك أي رابط، لأن إعادة توجيه معرّفة في مكانين هي خطأ ينتظر عصرًا سيئًا.
FAQ
أسئلة
أين توضع إعادات التوجيه المخصصة في ملف htaccess الخاص بـ WordPress؟
فوق سطر # BEGIN WordPress، ولا توضع أبدًا بين العلامتين. فـ WordPress يعيد توليد كل ما بين هاتين العلامتين كلما حفظ أحدهم شاشة الروابط الدائمة، فتُحذف القاعدة الموضوعة بالداخل دون أي تنبيه. والموضع مهم أيضًا للترتيب: كتلة WordPress تنتهي بقاعدة شاملة تُرسل كل طلب غير مطابق إلى index.php.
هل أستخدم Redirect أم RedirectMatch أم RewriteRule من أجل 301؟
استخدم Redirect لمسار واحد معروف، و RedirectMatch حين يغطي نمط واحد روابط كثيرة، و RewriteRule حين تعتمد إعادة التوجيه على شرط مثل اسم المضيف أو البروتوكول أو سلسلة الاستعلام. فـ Redirect و RedirectMatch يأتيان من mod_alias وهما أبسط. أما RewriteRule فيأتي من mod_rewrite وهو الوحيد القادر على اختبار الشروط.
لماذا تسبب إعادة التوجيه في htaccess حلقة لا تنتهي؟
عادةً لأن الوجهة نفسها ما زالت تطابق القاعدة التي أرسلت الزائر إليها، فتشتغل القاعدة من جديد إلى ما لا نهاية. والسبب الشائع الآخر هو فرض https خلف موزّع أحمال أو شبكة توزيع محتوى، حيث يرى الخادم طلبًا عبر http عاديًا في كل مرة رغم أن الزائر يتصفح عبر https أصلًا. اختبر ترويسة X-Forwarded-Proto بدلًا من ذلك.
هل تهمّ الشرطة المائلة في نهاية الرابط داخل إعادة توجيه WordPress؟
نعم. إعادة التوجيه القانونية في WordPress تضيف شرطة مائلة في نهاية معظم الروابط الدائمة، فتوجيه 301 نحو رابط بلا شرطة ينتج قفزتين بدل واحدة. سلاسل إعادة التوجيه ما زالت تمرّر إشارات الترتيب لكنها تهدر ميزانية الزحف وتبطئ الزائر. طابق الرابط النهائي الذي يقدّمه WordPress تمامًا، بما في ذلك الشرطة.
كيف أختبر إعادة توجيه في htaccess دون أن أعطّل موقعي؟
شغّل curl بخيار الترويسات فقط على الرابط القديم واقرأ رمز الحالة وترويسة Location قبل أن تثق بالمتصفح. فالمتصفحات تخزّن استجابات 301 بشراسة وستعرض لك نتيجة قديمة. واحتفظ بنسخة من الملف السليم أولًا، لأن خطأ صياغة واحدًا في htaccess يُرجع 500 في كل صفحة بما فيها wp-admin.
لماذا أرجع موقعي كله خطأ 500 بعد إضافة إعادة توجيه؟
توجيه واحد مكتوب بصيغة خاطئة في htaccess يُسقط كل رابط تحت ذلك المجلد، بما في ذلك لوحة الإدارة. الأسباب الشائعة هي غلاف IfModule غير مغلق، أو سطر RewriteEngine On مفقود، أو خلط صياغة الوصول بين Apache 2.2 و 2.4 في ملف واحد. احذف آخر كتلة أضفتها وأعد التحميل للتأكد.