توجيه الروابط ذات الأحرف الكبيرة إلى صيغتها الصغيرة
الرابط بحرف كبير يأخذ مدخلًا خاصًا به في ذاكرة LiteSpeed ولا يصيبها في أي زيارة. قاعدة توجيه واحدة في Cloudflare تجمع كل الطلبات على رابط واحد بأحرف صغيرة.
اجعل كل طلب لموقعك ينتهي إلى رابط واحد بأحرف صغيرة، فيقدّم LiteSpeed نسخة مخزَّنة واحدة لكل صفحة بدل نسخة لكل صيغة من صيغ الأحرف.
تنطبق على أي موقع في الاستضافة المشتركة أو الموزِّعة أو المميّزة يقع خلف Cloudflare، وعلى أي خطة من خطط Cloudflare بما فيها المجانية. وتحتاج وصولًا إلى حساب Cloudflare الذي يستخدمه النطاق — انظر وضع موقعك خلف Cloudflare.
لماذا تُجزِّئ الأحرف الكبيرة ذاكرة التخزين
يبني LiteSpeed Cache مفتاح كل مدخل على مسار الطلب كنصٍّ حرفي، فهذان مدخلان منفصلان:
https://example.com/Blog/My-Great-Article
https://example.com/blog/my-great-articleالصفحة نفسها والمحتوى نفسه ووسم canonical نفسه — ومدخلان اثنان، يُسخَّن كلٌّ منهما على حدة.
وتصل الطلبات ذات الأحرف الكبيرة بلا انقطاع، من:
- مسارات التصنيفات والوسوم وأنواع المحتوى المخصّصة التي أُنشئت بحرف كبير ولم تُوحَّد بعدها،
- روابط داخلية قديمة كُتبت قبل ترتيب هذه المسارات،
- روابط خارجية ونسخ منقولة حفظت الصيغة كما وردت في وسم
og:urlأو في رابط منسوخ، - فهرس Google، الذي يظلّ يطلب النصّ نفسه الذي زحف إليه. فوسم canonical يوحّد الفهرسة، لكنه لا يمنع وصول الطلب إلى الرابط المفهرَس.
والأخير هو الأغلى ثمنًا. فزيارات البحث التي تهبط على صيغة نادرة الطلب تخطئ الذاكرة، فتعيد PHP وMySQL بناء الصفحة في كل زيارة، بينما تجلس النسخة ذات الأحرف الصغيرة مخزَّنة وسريعة لا تخدم أحدًا تقريبًا. وبيانات Core Web Vitals الميدانية (CrUX) تُجمع من زيارات حقيقية، فتصير التجربة المقيسة لتلك الصفحة هي المسار البطيء غير المخزَّن.
ويسهل إغفال ذلك، لأن:
- نسبة إصابة الذاكرة الإجمالية تبقى سليمة — فمجموعة صغيرة من الروابط الباردة دومًا تذوب في المجموع،
- أنت تكتب الروابط بأحرف صغيرة حين تختبر الموقع، وكذلك يفعل كل من يعمل عليه،
- زواحف السيو توحّد الروابط قبل جلبها، فلا يعيد الفحص إنتاج المشكلة.
ولا يحوّل Apache ولا LiteSpeed الروابط إلى أحرف صغيرة من تلقاء نفسه، وذلك عن قصد: فالمسارات في لينكس حسّاسة لحالة الأحرف، والملفات الحقيقية تعتمد على ذلك.
تحقّق هل تعنيك المشكلة
نزّل سجلّ وصول خامًا واعرض الطلبات الناجحة لمسارات تحوي حرفًا كبيرًا وليست ملفات:
awk '$9 == 200 && $7 ~ /[A-Z]/ && $7 !~ /\./ {print $7}' example.com-ssl_log | sort | uniq -c | sort -rn | head -20وكل مسار له عدد يُعتدّ به هو صفحة قُدِّمت من PHP لا من الذاكرة في كل تلك الزيارات.
وفي Google Search Console تظهر المشكلة نفسها في Performance → Pages: صفحة واحدة مذكورة مرتين بصيغتي أحرف مختلفتين.
أضف قاعدة التوجيه في Cloudflare
-
في Cloudflare، اختر النطاق ثم افتح Rules → Overview.

-
اختر Create rule ثم Redirect Rules.
-
في Rule name اكتب اسمًا تعرفه لاحقًا، مثل
UnifiedURLs. -
تحت If incoming requests match… اختر Custom filter expression.
-
الصق هذا في صندوق التعبير. وإن ظهر لك البنّاء المرئي بدل صندوق النصّ، فاختر Edit expression في الزاوية للتحويل إلى النصّ.
(http.request.uri.path contains "A" or http.request.uri.path contains "B" or http.request.uri.path contains "C" or http.request.uri.path contains "D" or http.request.uri.path contains "E" or http.request.uri.path contains "F" or http.request.uri.path contains "G" or http.request.uri.path contains "H" or http.request.uri.path contains "I" or http.request.uri.path contains "J" or http.request.uri.path contains "K" or http.request.uri.path contains "L" or http.request.uri.path contains "M" or http.request.uri.path contains "N" or http.request.uri.path contains "O" or http.request.uri.path contains "P" or http.request.uri.path contains "Q" or http.request.uri.path contains "R" or http.request.uri.path contains "S" or http.request.uri.path contains "T" or http.request.uri.path contains "U" or http.request.uri.path contains "V" or http.request.uri.path contains "W" or http.request.uri.path contains "X" or http.request.uri.path contains "Y" or http.request.uri.path contains "Z") and http.request.uri.path.extension eq ""والصيغة البديهية لهذا هي
http.request.uri.path matches "[A-Z]"، غير أن المعاملmatchesيحتاج خطة Business أو Enterprise. وسلسلةcontainsتؤدّي العمل نفسه على كل الخطط. -
تحت Then… اضبط Type على
Dynamic، والصق هذا في Expression:concat("https://", http.host, lower(http.request.uri.path)) -
اضبط Status code على
301 - Permanent Redirect، وفعّل Preserve query string. -
تحت Place at اختر First. فالتوجيه ينبغي أن يقع قبل أن تشتغل أي قاعدة أخرى على رابط على وشك التغيّر.
-
اختر Save.

http.request.uri.path.extension eq "" هو شطر التعبير الذي يُبقي الملفات
الحقيقية خارج التوجيه، وهو مهم. فمجلد الرفع مليء بأسماء مثل IMG_2024.jpg
وBanner-Photo.png، ونظام الملفات حسّاس لحالة الأحرف — وتصغير تلك المسارات
يحوّل صورًا عاملة إلى صفحات 404. فأبقِه. ويُستثنى موقع تنتهي روابطه بـ
.html: فتلك الصفحات تُستبعد مع الملفات.
لماذا 301 لا 302
التوجيه الدائم يخبر محرّكات البحث أن تجمع إشارات الترتيب على الرابط ذي الأحرف الصغيرة بدل فهرسة الاثنين. كما تحفظ المتصفحات والطبقات الوسيطة استجابة 301، فتُوجَّه الزيارة التالية للرابط ذي الأحرف الكبيرة دون رحلة جديدة إلى Cloudflare.
تأكّد أنه نجح
ينبغي أن يردّ الرابط ذو الأحرف الكبيرة الآن بتوجيه:
curl -sI https://example.com/Blog/My-Great-Article | head -n 3ابحث عن HTTP/2 301 وعن location: https://example.com/blog/my-great-article.
ثم اطلب الرابط ذا الأحرف الصغيرة مرتين وتحقّق أنه مخزَّن:
curl -sI https://example.com/blog/my-great-article | grep -i litespeedوينبغي أن يعيد الطلب الثاني x-litespeed-cache: hit — انظر
التأكد من عمل LiteSpeed Cache.
ثم تأكّد أن ملفًا في اسمه أحرف كبيرة ما زال يُفتح. وينبغي أن يعيد هذا 200:
curl -so /dev/null -w '%{http_code}\n' https://example.com/wp-content/uploads/2026/01/IMG_2024.jpgإن لم ينجح
- الرابط ذو الأحرف الكبيرة ما زال يعيد 200. فالمضيف لا يمرّ عبر Cloudflare — إذ يحتاج سجلّ DNS إلى السحابة البرتقالية، وإلا لم تر القواعد هذه الزيارات. وتأكّد كذلك أن القاعدة تظهر Active.
- صارت الصور 404. فاختبار الامتداد ناقص، أو سقطت الأقواس حول سلسلة
orعند اللصق. فـandأقوى ارتباطًا منor، ومن دون الأقواس ينطبق الاستثناء علىcontains "Z"الأخيرة وحدها. صحّح التعبير ثم امسح ذاكرة Cloudflare: فالتوجيه الخاطئ قد خُزِّن. - الموقع في حلقة توجيه. فثمّة ما يعيد الرابط ذا الأحرف الصغيرة إلى آخر بحرف
كبير — إضافة، أو قاعدة في
.htaccess، أو Site Address محفوظ في ووردبريس بحرف كبير. عالجه هناك لا بإزالة هذه القاعدة. - رابط ما زال يتصرّف بالطريقة القديمة بعد تعديل القاعدة. فالمتصفحات تحفظ 301
بشدّة. اختبر بـ
curlأو في نافذة خاصة، لا في التبويب الذي كنت تستعمله.
لماذا لا يصلح هذا في .htaccess
المقطع المتداوَل لهذه المشكلة يستعمل خريطة إعادة كتابة:
RewriteMap lc int:tolowerوRewriteMap لا يصحّ إلا في سياق إعدادات الخادم أو المضيف الافتراضي، في Apache
وLiteSpeed على السواء. وفي ملف .htaccess يعيد Apache خطأ 500، وفي الاستضافة
المشتركة لا وصول لك إلى المستوى الذي يُسمح فيه بالتوجيه. ولا بديل يعمل من
.htaccess وحده — فـ mod_rewrite لا يستطيع تغيير حالة أحرف نصٍّ بغير خريطة.
وإن لم يكن الموقع خلف Cloudflare، فافعلها في PHP. أنشئ wp-content/mu-plugins/
إن لم يكن موجودًا، واحفظ هذا في
wp-content/mu-plugins/lowercase-urls.php. فالإضافات الواجبة تُحمَّل تلقائيًا
ولا تحتاج تفعيلًا:
<?php
/**
* Plugin Name: Lowercase URL redirect
*/
add_action( 'init', function () {
if ( is_admin() || wp_doing_ajax() || 'GET' !== ( $_SERVER['REQUEST_METHOD'] ?? '' ) ) {
return;
}
$parts = explode( '?', $_SERVER['REQUEST_URI'] ?? '', 2 );
$path = $parts[0];
if ( $path === strtolower( $path ) || strpos( $path, '/wp-json/' ) === 0 ) {
return;
}
wp_safe_redirect( strtolower( $path ) . ( isset( $parts[1] ) ? '?' . $parts[1] : '' ), 301 );
exit;
} );وهذا لا يعمل إلا مع الطلبات التي يتولّاها ووردبريس، فلا يمسّ الملفات في
wp-content/uploads. وهو بديل احتياطي لا عوض: إذ يظلّ الطلب يصل إلى PHP، وهو
العمل الذي تتجنّبه قاعدة Cloudflare من أصله.