موقعنا يضم 402 صفحة منشورة. خريطة الموقع تُبنى تلقائيًا وتحمل كل رابط جديد خلال ثوانٍ من نشره. ومع ذلك كنا نفتح Search Console كل أسبوع لنطلب فهرسة الصفحات يدويًا، واحدة واحدة.
ظننا المشكلة في الأتمتة. اتضح أن السبب رقم واحد مكرر 293 مرة.

ما الذي كنا نراه
الأعراض كانت واضحة ومربكة في الوقت نفسه. ننشر صفحة خدمية جديدة، فتظهر في sitemap.xml فورًا. نفتح الملف ونتأكد بأنفسنا: الرابط موجود، والتاريخ موجود، وعدد الروابط زاد واحدًا. كل شيء يبدو سليمًا.
ثم لا يحدث شيء. تمر أيام وGoogle لا يزور الصفحة. نذهب إلى Search Console ونضغط «طلب الفهرسة»، فتُفهرس خلال ساعات. نكرر هذا مع كل صفحة.
مع الوقت صار لدينا اعتقاد خاطئ: أن خريطة الموقع لا تُحدَّث. فحصنا كود توليدها ثلاث مرات، وأضفنا سجلات تتبع، وتأكدنا أنها تُبنى عند كل طلب لا عند البناء. كانت سليمة تمامًا.
الرقم الذي كشف كل شيء
بدل فحص الكود مرة رابعة، فحصنا مخرجاته. استخرجنا كل قيم lastmod من الملف وعددنا المتكرر منها:
عدد الروابط: 353
عدد قيم lastmod المختلفة: 87
الأكثر تكرارًا:
2026-07-21T13:22:57.000Z ← 261 رابطًا
2026-08-03T21:12:10.790Z ← 4 روابط
2026-07-17T21:44:59.000Z ← رابطان
261 رابطًا يحمل الطابع الزمني نفسه. ليس اليوم نفسه ولا الساعة نفسها، بل الثانية نفسها: 13:22:57.
رجعنا إلى قاعدة البيانات لنتأكد أن المشكلة ليست في طبقة العرض:
SELECT post_modified, COUNT(*) c
FROM wp_posts
WHERE post_status = 'publish'
GROUP BY post_modified
ORDER BY c DESC LIMIT 3;
2026-07-21 13:22:57 => 293
2026-07-12 01:25:15 => 2
2026-07-14 11:59:50 => 2
293 عنصرًا من 402 منشور. أي 73% من الموقع يدّعي أنه تغيّر في اللحظة ذاتها.
من أين جاء هذا التاريخ
21 يوليو كان يوم حملة تحسين شاملة على الموقع. راجعنا العناوين والأوصاف على 392 صفحة، وأصلحنا ما يحتاج إصلاحًا. وفي نهاية العملية شغّلنا سكربتًا يحدّث post_modified لكل صفحة مسّها التعديل، بنية سليمة: أردنا إخبار Google أن المحتوى تجدّد.
السكربت اشتغل في ثانية واحدة على مئات الصفحات. فحملت كلها الطابع نفسه.
ما لم نحسب حسابه أن Google يقرأ هذه الإشارة قراءة معاكسة تمامًا.
كيف يتعامل Google مع lastmod
وثائق Google عن خرائط المواقع تذكر شرطًا صريحًا لقيمة lastmod: أن تكون دقيقة ومتّسقة. والجملة المهمة فيها أن Google قد يتجاهل هذه القيم إذا وجدها غير موثوقة.
وهذا منطقي من موقعه. تخيّل أنك زاحف تفحص ملايين المواقع يوميًا، وميزانية زحفك محدودة. أنت تعتمد على lastmod لتقرر: أي الصفحات أعيد زيارتها اليوم، وأيها أؤجل شهرًا. فإذا وجدت موقعًا يخبرك أن 73% من صفحاته تغيّرت في الثانية نفسها، فأمامك احتمالان: إما حدث شيء استثنائي، أو أن هذا الموقع لا يعرف ماذا يكتب في هذا الحقل.
الاحتمال الثاني أرجح إحصائيًا. فيتوقف الزاحف عن الاعتماد على الحقل، ويعود إلى تقدير وتيرة التغيير بنفسه من تاريخ زياراته السابقة. وهذا التقدير بطيء ومحافظ.
النتيجة عندنا: صفحات جديدة تدخل خريطة الموقع بتاريخ صحيح تمامًا، وGoogle يتجاهل التاريخ لأنه توقف عن تصديق الحقل كله. ولا يبقى أمامك إلا الطلب اليدوي.
العيب الثاني الذي وجدناه في الطريق
أثناء مراجعة كود خريطة الموقع لاحظنا شيئًا آخر. الروابط الأربعة الرئيسية (الصفحة الأولى، المدونة، الأعمال، المنتجات) كانت مكتوبة هكذا:
{
url: SITE,
lastModified: new Date(), // ← لحظة الطلب
changeFrequency: "daily",
}
new Date() تعني «الآن». وبما أن المسار يُبنى عند كل طلب، فإن هذه الروابط تخبر Google في كل زحفة أنها تغيّرت قبل ثانية.
الصفحة الرئيسية لا تتغير كل دقيقة. وحين يزور الزاحف الرابط ثلاث مرات ويجد المحتوى نفسه رغم أن التاريخ يتغير في كل مرة، فهو يجمع دليلًا إضافيًا على أن هذا الموقع لا يكتب lastmod بدقة.
لماذا لا يكفي «توقفوا عن التعديل الجماعي»
الحل الواضح: لا تلمس post_modified بالجملة مرة أخرى. لكنه حل هش.
في ووردبريس، post_modified يتحرك مع أي حفظ. ويتحرك مع wp search-replace. ويتحرك حين تحدّث حقلًا مخصصًا على مئة صفحة. ويتحرك حين يفتح محرر صفحة ويحفظها دون أن يغيّر حرفًا. كل هذه العمليات مشروعة وستتكرر، ومع كل واحدة منها تعود المشكلة.
أردنا حلًا لا يعتمد على أن يتذكر أحد قاعدة.
الحل: تاريخ مشتق من المحتوى لا من لمسه
الفكرة بسيطة. بدل أن نسأل «متى حُفظت هذه الصفحة؟» نسأل «هل تغيّر ما يراه الزائر فيها؟».
كتبنا إضافة صغيرة تحسب بصمة (hash) لمحتوى كل صفحة، وتقارنها بالبصمة المحفوظة عند كل حفظ. إن تطابقتا فلا شيء يتحرك. وإن اختلفتا يُحدَّث التاريخ.
function roeia_lastmod_content_hash( WP_Post $post ): string {
$parts = [
$post->post_title,
$post->post_content,
$post->post_excerpt,
$post->post_name,
(string) get_post_thumbnail_id( $post->ID ),
];
foreach ( [ 'rank_math_title', 'rank_math_description' ] as $k ) {
$parts[] = (string) get_post_meta( $post->ID, $k, true );
}
foreach ( get_object_taxonomies( $post->post_type ) as $tax ) {
$terms = wp_get_post_terms( $post->ID, $tax, [ 'fields' => 'ids' ] );
if ( ! is_wp_error( $terms ) && $terms ) {
sort( $terms );
$parts[] = $tax . ':' . implode( ',', $terms );
}
}
return md5( implode( "\x1f", $parts ) );
}
البصمة تشمل ما يظهر للزائر: العنوان والمحتوى والصورة البارزة والتصنيفات وعناوين محركات البحث. ولا تشمل عدّادات المشاهدة ولا سجلات الإضافات ولا أي بيانات داخلية لا تغيّر الصفحة المعروضة.
ثم عند الحفظ:
function roeia_lastmod_sync( int $post_id, ?WP_Post $post = null ): bool {
$post = $post ?: get_post( $post_id );
$hash = roeia_lastmod_content_hash( $post );
$stored = (string) get_post_meta( $post_id, '_roeia_content_hash', true );
if ( $stored === $hash && get_post_meta( $post_id, '_roeia_lastmod', true ) ) {
return false; // لا تغيير حقيقي، لا نلمس التاريخ
}
update_post_meta( $post_id, '_roeia_content_hash', $hash );
update_post_meta( $post_id, '_roeia_lastmod', gmdate( 'c' ) );
return true;
}
add_action( 'save_post', 'roeia_lastmod_sync', 30, 2 );
وأخيرًا نعرض القيمة في REST لتقرأها الواجهة الأمامية:
add_action( 'rest_api_init', function () {
foreach ( [ 'page', 'post', 'projects', 'products' ] as $type ) {
register_rest_field( $type, 'sitemap_lastmod', [
'get_callback' => fn( $obj ) => get_post_meta( $obj['id'], '_roeia_lastmod', true ),
] );
}
} );
الفخ الذي كاد يعيد المشكلة من أولها
بعد كتابة الإضافة بقيت خطوة: بذر التواريخ للصفحات الـ402 الموجودة.
الحل السهل أن نضع «الآن» للجميع. وهو بالضبط ما أوقعنا في المشكلة أول مرة. كنا سننتج 402 صفحة بالطابع نفسه بدل 293.
فبحثنا عن أصدق تاريخ متاح لكل صفحة: آخر مراجعة (revision) محفوظة قبل يوم الحملة، وإن لم توجد فتاريخ النشر.
function roeia_lastmod_honest_seed( WP_Post $post ): string {
global $wpdb;
$rev = $wpdb->get_var( $wpdb->prepare(
"SELECT MAX(post_modified_gmt) FROM {$wpdb->posts}
WHERE post_parent = %d AND post_type = 'revision'",
$post->ID
) );
return gmdate( 'c', strtotime( ( $rev ?: $post->post_date_gmt ) . ' UTC' ) );
}
تاريخ النشر ليس تخمينًا. هو أصدق ما نملك: هذه الصفحة لم نتحقق من تعديل حقيقي فيها منذ نُشرت. والأهم أنه موزّع طبيعيًا على سنوات، فيختفي التجمّع الذي أثار الشك.
اختبار السلوك قبل التطبيق
قبل تشغيل البذر على 402 صفحة، جرّبنا الاتجاهين على صفحة واحدة.
الاتجاه الأول، حفظ دون تغيير:
post_modified الحالي: 2026-07-21 13:22:57
البذرة الصادقة: 2026-07-15T10:58:59+00:00
حفظ بلا تغيير حرّك التاريخ؟ لا (صحيح)
الاتجاه الثاني، تغيير حقيقي في وصف الصفحة:
قبل: 2026-07-15T10:58:59+00:00
بعد: 2026-08-03T21:23:14+00:00
تحرّك؟ نعم (صحيح)
اختبار الاتجاه الأول وحده لا يكفي. حقل لا يتحرك أبدًا يجتاز ذلك الاختبار بامتياز ولا ينفع في شيء.
النتيجة
| القياس | قبل | بعد |
|---|---|---|
| تواريخ مختلفة | 87 | 337 من 353 رابطًا |
| أكبر تكرار لتاريخ واحد | 261 | 8 |
| التوزيع | كتلة واحدة في 21 يوليو | موزّع من 2021 إلى 2026 |
التوزيع الشهري بعد الإصلاح: 74 صفحة في 2021، و16 في 2023، و203 في 2026. منحنى طبيعي لموقع بدأ قبل خمس سنوات وتسارع نشاطه هذا العام.
خطأ ارتكبناه أثناء الإصلاح
عند تعديل مولّد خريطة الموقع أضفنا دالة lastModOf() تفضّل الحقل الجديد، واستخدمناها في الروابط الأربعة الثابتة. ونشرنا.
الملف لم يتغير.
ظننا الكاش هو السبب، فأفرغنا ثلاث طبقات منه دون فائدة. ثم لاحظنا تفصيلًا في صيغة التواريخ: القيم القادمة من الحقل الجديد تنتهي بـ+00:00، والقيم القديمة تنتهي بـ.000Z. الملف كان مليئًا بالصيغة الثانية.
رجعنا إلى الكود:
$ grep -n "new Date(p.modified)" src/app/sitemap.ts
141: lastModified: new Date(p.modified),
152: lastModified: new Date(p.modified),
161: lastModified: new Date(p.modified),
171: lastModified: new Date(p.modified),
عرّفنا الدالة واستعملناها في مكان واحد، وتركنا المواضع الأربعة التي تولّد كل روابط الصفحات والمقالات والأعمال والمنتجات. أي أن 349 رابطًا من 353 ظل يقرأ الحقل القديم.
الدرس: بعد أي استبدال في الكود، ابحث عن النمط القديم بـgrep قبل النشر. ولا تعتمد على «الصفحة الرئيسية تغيّرت» كدليل، فكلا المسارين ينتج تاريخًا اليوم.
كيف تفحص موقعك أنت
الفحص لا يحتاج أدوات. نزّل خريطة موقعك وعُدّ القيم المتكررة:
curl -s https://example.com/sitemap.xml \
| grep -oP '(?<=<lastmod>)[^<]+' \
| sort | uniq -c | sort -rn | head
وإن كان الموقع على ووردبريس، اسأل قاعدة البيانات مباشرة:
SELECT post_modified, COUNT(*) c FROM wp_posts
WHERE post_status = 'publish'
GROUP BY post_modified ORDER BY c DESC LIMIT 5;
القراءة: إن كان أكبر تكرار رقمًا صغيرًا نسبة إلى حجم موقعك فالحقل سليم. وإن رأيت رقمًا يقارب ثلث صفحاتك أو أكثر بطابع زمني واحد، فأنت أمام الحالة نفسها.
البدائل التي جرّبناها ورفضناها
قبل الوصول إلى بصمة المحتوى مررنا بثلاثة طرق مسدودة، وكل واحد منها يُقترح كثيرًا في المنتديات.
1. إخطار Google بتحديث الخريطة
لسنوات كان بإمكانك استدعاء رابط google.com/ping?sitemap=... بعد كل نشر فيزحف Google إلى الملف. أوقفت Google هذه الخدمة في يونيو 2023، وأعلنت أن السبب أن أغلب الإخطارات لم تكن تحمل تغييرًا فعليًا.
لاحظ المفارقة: الخدمة أُلغيت للسبب نفسه الذي أفقدنا الثقة في lastmod. الإشارة التي يُساء استعمالها تُهمل.
2. الاعتماد على IndexNow
عندنا إضافة تُخطر IndexNow عند كل نشر أو تعديل، وتعمل جيدًا. لكنها لا تحل هذه المشكلة، لأن Google ليس من المشاركين في البروتوكول. المستفيد منها Bing وYandex وSeznam وNaver.
احتفظنا بها لأنها مفيدة في مكانها. ووضّحنا في تعليق داخل الملف أن Google يلتقط التغييرات عبر lastmod لا عبرها، حتى لا يظن أحد بعدنا أن التغطية مكتملة.
3. واجهة الفهرسة البرمجية
توفّر Google واجهة Indexing API تُرسل إليها الروابط برمجيًا. لكن توثيقها يحصرها في نوعين من المحتوى: إعلانات الوظائف والبثّ المباشر. استعمالها لصفحات خدمية أو مقالات مخالف للاستخدام المعلن، وقد يعمل اليوم ويتوقف غدًا.
رفضناها لأننا لا نبني على أساس قد يُسحب في أي وقت.
متى لا تكون هذه مشكلتك
قبل أن تعدّل شيئًا، تأكد أن التشخيص ينطبق عليك. أعراض «الصفحات لا تُفهرس» لها أسباب أخرى أكثر شيوعًا:
- robots.txt يحجب الزاحف. افتح
example.com/robots.txtوابحث عنDisallow: /تحتUser-agent: *. فحصنا الشهر الماضي أثناء عمل تحسين محركات بحث موقعًا إخباريًا فيه 63,982 مقالًا ولديه صفحة واحدة مفهرسة، والسبب هذا السطر الواحد. - وسم noindex منسي. يحدث كثيرًا بعد نقل موقع من بيئة تطوير.
- خريطة الموقع غير معلنة في robots.txt. سطر
Sitemap:واحد يختصر على الزاحف وقتًا. - الموقع جديد. إن كان عمر النطاق أسابيع فالبطء طبيعي ولا علاقة له بأي حقل.
- محتوى ضعيف. إن زُحفت الصفحة ولم تُفهرس، فالمشكلة في الصفحة لا في الخريطة. هذه حالة مختلفة تمامًا وتظهر في Search Console باسم «تم الزحف إلى الصفحة ولم تتم فهرستها».
الحالة التي نتحدث عنها لها بصمة محددة: خريطة سليمة، وصفحات تُفهرس فورًا عند الطلب اليدوي، وتجمّع واضح في قيم lastmod. إن لم تجتمع الثلاثة فابحث في مكان آخر.
لمن يهمّه هذا أكثر
كلما كبر موقعك زاد أثر هذه المشكلة. موقع من عشرين صفحة يزوره الزاحف كاملًا في كل مرة، فلا يهم ما تكتبه في lastmod.
أما موقع بمئات أو آلاف الصفحات فميزانية زحفه موزّعة. والزاحف يحتاج إلى إشارة تخبره أين يبدأ. المواقع الإخبارية والمتاجر الإلكترونية ومواقع الخدمات متعددة المدن هي الأكثر تضررًا، لأن نموذجها يقوم على نشر متواصل يحتاج اكتشافًا سريعًا.
وهذه المواقع نفسها هي الأكثر عرضة للعمليات الجماعية: تحديث أسعار على كل المنتجات، تغيير رقم هاتف في كل الصفحات، ترحيل بيانات. كل واحدة منها تكتب الطابع الزمني نفسه على آلاف الصفحات دفعة واحدة.
أسئلة شائعة
هل حذف الحقل أفضل من كتابته بشكل خاطئ؟
Google يقول إن غياب lastmod مقبول، وإنه يعتمد وقتها على تقديره الخاص. لكن الحقل الصحيح أفضل من غيابه، لأنه يوجّه الزاحف إلى ما تغيّر فعلًا. والترتيب: حقل دقيق، ثم لا حقل، ثم حقل مضلل.
كم يستغرق استعادة الثقة؟
لا رقم معلنًا. Google بنى تقديره عبر أسابيع من مقارنة ما تقوله بما يجده، وسيعيد بناءه بالطريقة نفسها. القياس العملي: راقب في Search Console عمود «تاريخ آخر زحف» للصفحات التي عدّلتها، وقارنه بما قبل الإصلاح.
هل تنفع الطريقة مع منصات غير ووردبريس؟
المبدأ لا يخص منصة. اسأل نفسك: هل التاريخ الذي أرسله يعكس تغيّر ما يراه الزائر، أم لحظة لمس السجل في قاعدة البيانات؟ إن كان الثاني فأنت معرّض للمشكلة نفسها. التطبيق يختلف، والفكرة واحدة: خزّن بصمة للمحتوى، وقارنها قبل تحريك التاريخ.
ماذا عن الصفحات التي تعرض محتوى ديناميكيًا؟
صفحة تعرض قائمة تتغير (مثل صفحة مدونة أو أرشيف) لا تملك محتوى ثابتًا تبصمه. عالجنا هذه بقاعدة مختلفة: تاريخ صفحة الفهرس هو أحدث تاريخ بين أبنائها. فصفحة المدونة تتغير حين ينشر مقال جديد، لا مع كل طلب.
وإن أردت أدوات تساعدك في قرارات تقنية مشابهة، جمعنا بعضها في صفحة الأدوات المجانية.
ماذا نتوقع الآن
Google لا يستعيد ثقته بالحقل في يوم. بنى تقديره عبر أسابيع، وسيعيد بناءه عبر أسابيع مماثلة يقارن فيها ما نقوله بما يجده فعلًا.
ما نستطيع قوله بثقة: الملف صار يقول الصدق. صفحة عُدّلت أمس تحمل تاريخ أمس، وصفحة لم تُمس منذ 2021 تحمل تاريخ 2021. والحفظ الذي لا يغيّر شيئًا لم يعد يحرّك شيئًا.
وهذه أول مرة يكون فيها ذلك صحيحًا على موقعنا.
