Last-Modified هو رأس استجابة HTTP يشير إلى تاريخ ووقت آخر تعديل للمورد على الخادم، مما يسمح للعميل بتنفيذ طلبات شرطية عبر If-Modified-Since. إذا لم يتغير المورد منذ التاريخ المحدد، يعيد الخادم 304 Not Modified دون إرسال نص الاستجابة، مما يوفر عرض النطاق بشكل كبير. وفقًا لـ RFC 7232 (IETF, 2014)، تقلل الطلبات الشرطية مع Last-Modified وقت تحميل الصفحات بنسبة 30-60% في الزيارات المتكررة. الرأس مدعوم تلقائيًا من معظم خوادم HTTP والبروكسي.
النقاط الرئيسية
Last-Modified هو رأس HTTP ينتمي إلى مجموعة رؤوس الطلبات الشرطية. يضيفه الخادم إلى استجابة GET أو HEAD، مشيرًا إلى تاريخ ووقت آخر تعديل للمورد المطلوب بتنسيق HTTP-date: Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT. يحفظ العميل (المتصفح، تطبيق الجوال، البروكسي) هذا التاريخ مع المورد المخزن مؤقتًا. عند طلب لاحق، يرسل العميل رأس If-Modified-Since بنفس التاريخ، ويقارنه الخادم بوقت التعديل الحالي للمورد.
بروتوكول الطلبات الشرطية مع Last-Modified مُعرَّف في RFC 7232 ومدعوم من جميع خوادم HTTP الحديثة. تنسيق التاريخ منظم بدقة — فقط GMT (Greenwich Mean Time) دون تحديد منطقة زمنية. يجب على الخادم إرجاع التاريخ بثلاثة تنسيقات ممكنة: RFC 1123 (قياسي)، RFC 850 (قديم) أو ANSI C asctime. عمليًا، تستخدم جميع الخوادم تقريبًا تنسيق RFC 1123 بطول ثابت 29 حرفًا.
ينتمي Last-Modified إلى فئة آليات التحقق من التخزين المؤقت: لا يخبر العميل ما إذا كان يمكن تخزين الاستجابة مؤقتًا، ولكنه يوفر أداة للتحقق من صلاحية مورد مخزن مؤقتًا بالفعل. يتم تحديد سياسة التخزين المؤقت بشكل منفصل عبر رأس Cache-Control. وفقًا لدراسة Akamai (2025)، يؤدي التهيئة الصحيحة لـ Last-Modified مع Cache-Control إلى تقليل الحمل على خوادم المصدر بنسبة تصل إلى 70% للمحتوى الثابت.
تم تعريف رأس Last-Modified في HTTP/1.0 (RFC 1945, 1996) وأصبح من أوائل آليات إدارة التخزين المؤقت على الويب. قبل ظهور ETag في HTTP/1.1، كان الطريقة الوحيدة لتنفيذ الطلبات الشرطية. على الرغم من عمره، لا يزال الرأس ذا صلة بفضل بساطته — لا يحتاج الخادم إلى حساب تجزئة للمحتوى، يكفي قراءة الطابع الزمني للملف من نظام الملفات أو حقل updated_at من قاعدة البيانات.
تتكون الدورة الكاملة من ثلاث مراحل. في الطلب الأول، يعيد الخادم المورد مع رأس Last-Modified وحالة HTTP 200 OK. يخزن العميل الاستجابة مؤقتًا مع التاريخ. في طلب متكرر، يرسل العميل رأس If-Modified-Since مع التاريخ المحفوظ. يقارن الخادم هذا التاريخ مع وقت التعديل الحالي للمورد. إذا لم يتغير المورد — يُرجع 304 Not Modified بجسم فارغ. إذا تغير — 200 OK مع بيانات جديدة و Last-Modified جديد.
// الطلب الأول — يعيد الخادم المورد مع تاريخ
HTTP/1.1 200 OK
Content-Type: application/json
Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT
[{"id": 1, "name": "Alice"}]
// الطلب المتكرر — يرسل العميل التاريخ المحفوظ
GET /api/users HTTP/1.1
Host: example.com
If-Modified-Since: Wed, 02 Jul 2025 14:30:00 GMT
// الاستجابة — البيانات لم تتغير
HTTP/1.1 304 Not Modified
لتطبيقات الجوال، Last-Modified مفيد بشكل خاص لمزامنة البيانات. يحفظ التطبيق تاريخ آخر تحديث ناجح ويرسله إلى الخادم في If-Modified-Since. إذا كانت هناك بيانات جديدة أو تغيرت — يعيد الخادم المجموعة الكاملة. إذا لم تكن — 304، ويستخدم التطبيق النسخة المحلية. OkHttp و URLSession يدعمان هذه الآلية تلقائيًا عبر أنظمة التخزين المؤقت المدمجة.
للملفات الثابتة، يأخذ Nginx و Apache التاريخ من سمات نظام الملفات — mtime (وقت التعديل). للمحتوى الديناميكي، يجب أن يضبط كود الخادم Last-Modified بشكل صريح بناءً على منطق الأعمال: حقل updated_at من قاعدة البيانات، تاريخ آخر commit في Git، الطابع الزمني لقطعة البناء. إذا لم يتم ضبط Last-Modified بشكل صريح، قد لا يرسل الخادم الرأس على الإطلاق، ولن يتمكن العميل من تنفيذ طلبات شرطية حسب التاريخ.
يؤدي Last-Modified و ETag مهمة مماثلة — السماح للعميل بالتحقق من صلاحية التخزين المؤقت — لكن لديهما اختلافات جوهرية. يستخدم Last-Modified طابعًا زمنيًا، بينما يستخدم ETag معرف إصدار فريد. لكل نهج سيناريوهاته الخاصة حيث يكون أكثر فعالية، وتوصي مواصفات HTTP باستخدام كلا الرأسين معًا.
| المعيار | Last-Modified | ETag |
|---|---|---|
| الجوهر | تاريخ آخر تعديل | معرف إصدار فريد |
| الدقة | حتى الثانية | حتى البت (تجزئة) |
| تعقيد التطبيق | منخفض — تلقائي من نظام الملفات | متوسط — يتطلب حساب تجزئة |
| الخوادم المجمعة | مشكلة: mtime قد يختلف بين العقد | مستقر مع بيانات متطابقة بين العقد |
| دعم النطاقات | لا يؤثر على طلبات Range | يتطلب ETag قوي للنطاقات |
| التوصية | للملفات الثابتة و APIs البسيطة | لـ APIs حيث الدقة مهمة |
الميزة الرئيسية لـ Last-Modified هي البساطة. لا يحتاج الخادم إلى حساب تجزئة للمحتوى، مما يوفر موارد المعالج في كل طلب. للمشاريع عالية الحركة التي تقدم ملفات ثابتة أو بيانات بطوابع زمنية واضحة، يظل Last-Modified الخيار الأمثل. ETag من ناحية أخرى يوفر دقة مطلقة — تغيير حرف واحد في استجابة JSON سيغير ETag، لكنه قد لا يغير التاريخ (إذا تمت إعادة كتابة الملف بنفس الإصدار).
توصي المواصفات بإرجاع كلا الرأسين في وقت واحد. يضمّن الخادم كلا من Last-Modified و ETag في استجابة 200 OK. يرسل العميل كلا الرأسين الشرطيين — If-Modified-Since و If-None-Match. يتحقق الخادم أولاً من ETag (له الأولوية)، ثم Last-Modified. إذا أشار أحدهما على الأقل إلى تغيير — يتم إرجاع الاستجابة الكاملة. وهذا يوفر أقصى مرونة: ETag يضمن الدقة، Last-Modified يوفر فحصًا احتياطيًا للعملاء الذين لا يدعمون ETag.
تعتمد تهيئة Last-Modified على نوع الخادم. لـ Nginx و Apache، يتم ضبط Last-Modified تلقائيًا للملفات الثابتة بناءً على mtime. للتطبيقات الديناميكية، يجب ضبط الرأس في كود الخادم. دعنا ننظر إلى التهيئة على المنصات الشائعة.
// Express.js — ضبط Last-Modified
app.get("/api/users", async (req, res) => {
const updatedAt = await getLastUpdate()
const ifModifiedSince = req.get("If-Modified-Since")
// التحقق من If-Modified-Since
if (ifModifiedSince && new Date(ifModifiedSince)
>= updatedAt) {
return res.status(304).end()
}
const users = await getUsers()
res.set("Last-Modified", updatedAt.toUTCString())
res.json(users)
})
في مثال Express.js، يحصل الخادم على تاريخ آخر تحديث للبيانات من قاعدة البيانات، ويتحقق من If-Modified-Since من العميل، وإذا كان التخزين المؤقت لا يزال صالحًا — يُرجع 304. إذا تغيرت البيانات — يضبط Last-Modified جديدًا ويعيد الاستجابة الكاملة. toUTCString() تحول التاريخ إلى تنسيق HTTP المطلوب. في الإنتاج، من الجيد تخزين updatedAt مؤقتًا في Redis لتجنب الاستعلام عن قاعدة البيانات في كل طلب.
يضبط Nginx تلقائيًا Last-Modified للملفات الثابتة بناءً على وقت آخر تعديل للملف. يمكن تعطيل أو تغيير هذا السلوك باستخدام توجيه etag (تعطيل ETag) أو عبر وحدة ngx_http_headers_module. للطلبات البروكسية إلى الواجهة الخلفية، يتم تمرير Last-Modified من استجابة upstream دون تغيير. مهم: إذا كانت الواجهة الخلفية لا تُرجع Last-Modified، لن يضيفه Nginx تلقائيًا للاستجابات الديناميكية.
لدى Last-Modified عدة قيود معروفة. الرئيسي هو دقة الثانية. إذا تغير المورد مرتين خلال ثانية واحدة، قد يفوت العميل الإصدار الجديد. عمليًا هذا سيناريو نادر، لكن للتحديثات عالية التردد (تدفقات الأسعار، الدردشات) يُوصى بـ ETag. القيد الثاني هو مشكلة التجميع: على خوادم مختلفة قد يكون للملف mtime مختلف بسبب النسخ أو النشر، مما يجعل Last-Modified غير متسق.
القيد الثالث — معالجة If-Modified-Since بدقة الثانية قد تؤدي إلى طلبات غير ضرورية عند الاستقصاء المتكرر للخادم. إذا كان العميل يرسل If-Modified-Since كل 500 مللي ثانية، يعيد الخادم 200 OK في كل مرة لأن التاريخ لم يتغير، لكن المورد قد تم تحديثه بالفعل. الحل هو استخدام مزيج مع ETag: سيلتقط ETag التغيير خلال ثانية، بينما يبقى Last-Modified كخيار احتياطي.
المشكلة الرابعة — Last-Modified لا يفرق بين إصدارات مختلفة من نفس المورد بنفس التاريخ. إذا تمت استعادة ملف من نسخة احتياطية وتطابق mtime الخاص به مع الأصل، لن يلاحظ العميل أن المحتوى تغير. يحل ETag هذه المشكلة: تجزئة المحتوى ستتغير بالتأكيد مع أي تعديل للبيانات، بغض النظر عن الطابع الزمني. للبيانات الحرجة، استخدم دائمًا كلا الرأسين.
الأسئلة الشائعة
فقط GMT (Greenwich Mean Time) بتنسيق RFC 1123: اليوم من الأسبوع، اليوم، الشهر، السنة، الساعات:الدقائق:الثواني. مثال: Wed, 02 Jul 2025 14:30:00 GMT. المنطقة الزمنية دائمًا GMT، ولا يُسمح بالتنسيقات الأخرى.
من الناحية الفنية نعم، لكنه ينتهك RFC 7232. إذا أعاد الخادم تاريخًا مستقبليًا، لن يقوم العملاء بتحديث المورد حتى arrive هذا التاريخ. يعتبر مثل هذا التهيئة خطأ — يجب أن يكون التاريخ في الماضي أو الحاضر.
لا، الطلبات الشرطية If-Modified-Since تعمل فقط مع GET و HEAD. لا يتم تخزين طلبات POST مؤقتًا ولا تستخدم التحقق حسب التاريخ. لفحوصات الصلاحية في POST، استخدم ETag أو آليات مخصصة.
يحدد Cache-Control سياسة التخزين المؤقت (أقصى وقت تخزين، من يمكنه التخزين)، بينما Last-Modified هو آلية تحقق للتخزين المؤقت منتهي الصلاحية. بعد انتهاء max-age، يرسل العميل If-Modified-Since للتحقق من الصلاحية.
تحقق من أن الخادم يضبط الرأس بالفعل من المصدر الصحيح — قاعدة البيانات، نظام الملفات أو API. للاستجابات الديناميكية، تأكد من استدعاء res.setHeader(“Last-Modified”, ...) صراحةً في كود المعالج.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.