source.unsplash.com اختفت: تشريح الحادثة وكل طرق استبدالها
أُهملت الخدمة في 2021 مع وعد بأن «الاستخدامات الحالية ستستمر بالعمل»، ثم أُوقفت نهائيًا في يونيو 2024، ولا تزال تُكتب في أكواد جديدة حتى اليوم. تشريح هادئ للحادثة — والبدائل الثلاثة، مع الوكيل (proxy) الذي يعيد لك العشوائية دون مفتاح.
خدمة صور مخزنة أوقفت نطاقًا فرعيًا، وبعد عامين لا يزال يُعطّل مواقع التوثيق وشاشات تسجيل الدخول وتمارين الدورات التدريبية والكود المُولَّد حديثًا. هذا تشريح ما بعد الوفاة لرابط — ما كان يفعله، وما الذي قتله، وما الذي يجب وضعه مكانه — بالإضافة إلى نظرة متأنية على الجزء الأغرب من القصة: الآلات التي تكتب كودنا لم تلاحظ اختفاءه.
استجابات HTTP، ومدخلا سجل التغييرات المقتبسان حرفيًا، وحدود واجهة API، ومتتبعات المشاكل للمشاريع التي تعطلت، والأعداد من GitHub وnpm وStack Overflow — كلها من مصادر أساسية، مع منهجية كل واحدة موضحة في الحواشي. إن كنت تريد فقط الإصلاح، انتقل إلى جدول الترحيل.
ما الذي تحصل عليه اليوم، إن طلبته
أمر واحد، بلا مفتاح، قابل للتكرار من أي جهاز:
curl -I https://source.unsplash.com/random
HTTP/2 503
cache-control: no-cache, no-store
content-type: text/html; charset=utf-8
server: Heroku
via: 2.0 heroku-router
# الجسم: إطار iframe يشير إلى herokucdn.com/error-pages/application-error.html
لا شيء هنا هو فشل DNS. source.unsplash.com لا يزال يُحلَّل — إنه CNAME إلى
مضيف herokudns.com — لذا يُجاب على الطلب، لكن ليس من قِبل تطبيق. هذا
التفصيل أهم مما يبدو: المتصفح الذي يتلقى استجابة 503 سريعة بجسم HTML
يرسم بديل صورة معطوبة، وأي كود يقرأ response.ok أو معالج
onerror لم تكتبه أنت يسلك مسار الفشل الذي لم تختبره قط.
| نمط الرابط | ما كان يُرجعه سابقًا | اليوم |
|---|---|---|
source.unsplash.com/random | صورة عشوائية، بأي حجم | 503 |
source.unsplash.com/random/1600x900 | صورة عشوائية، مقصوصة لحجم معين | 503 |
source.unsplash.com/1600x900/?apple,desk | صورة عشوائية تطابق كلمات بحث | 503 |
source.unsplash.com/featured/1600x900?nature | صورة عشوائية مميزة | 503 |
source.unsplash.com/collection/190727/800x600 | صورة عشوائية من مجموعة | 503 |
source.unsplash.com/user/scottwebb/1600x900 | صورة عشوائية من مصور واحد | 503 |
source.unsplash.com/daily | صورة اليوم | 503 |
تم التحقق منها فرديًا باستخدام curl -o /dev/null -w "%{http_code}". ميزة البحث
أُوقفت أولًا كما أُعلن؛ اليوم التطبيق بأكمله متوقف، لذا لم يعد هذا التمييز
قائمًا.
ثلاث سنوات بين "مهجور" و"متوقف"
كلا الإعلانين لا يزالان قابلين للقراءة، في مكان واحد، على unsplash.com/documentation/changelog. مقتبسان كاملين، لأن الصياغة هي القصة بأكملها:
25 نوفمبر 2021 — "إيقاف Unsplash Source"
"يتم إيقاف Unsplash Source. الاستخدامات الحالية ستستمر بالعمل، ولكن للمشاريع الجديدة استخدموا واجهة Unsplash API الكاملة."
11 يونيو 2024 — "الإيقاف النهائي لـUnsplash Source"
"أصبح Unsplash Source غير مدعوم رسميًا منذ إيقافه في 2021. كجزء من الإيقاف النهائي، سنبدأ أولًا بتعطيل ميزة البحث، وفي الأسابيع القادمة سنوقف التطبيق بالكامل. الاستخدامات الحالية لـSource — خصوصًا على مستوى الإنتاج — يجب أن تُرحَّل في أقرب وقت ممكن إلى واجهة Unsplash API الكاملة."
اقرأهما بالتسلسل ونمط الفشل يصبح واضحًا. إشعار 2021 تضمّن وعدًا (الاستخدامات الحالية ستستمر بالعمل) وبلا تاريخ محدد. المطور الذي قرأه في 2021 كان لديه كل الأسباب لترك كوده العامل دون تغيير؛ والمطور الذي انضم في 2022 لم يقرأه أصلًا. إشعار 2024 أعطى "الأسابيع القادمة"، بعد ثلاث سنوات، على صفحة لم يحفظها أحد في المفضلة.
تنشر Unsplash فعلًا سياسة إيقاف، وهي معقولة — إذ ينص التوثيق
على أنه بالنسبة للحقول ونقاط النهاية الموثّقة علنًا، تُعلَن التغييرات في سجل التغييرات مع
إشعار مدته على الأقل 3 أسابيع، وترجع نقاط النهاية رأس Warning
خلال فترة الإيقاف. تحتوي الفقرة نفسها على الجملة التي تفسر سبب عدم حماية أي من
ذلك لـSource: "بالنسبة لأي حقول أو نقاط نهاية غير موثّقة علنًا، قد نُجري تغييرات
عليها دون إشعار." لم تكن Source قط نقطة نهاية ضمن الواجهة الموثّقة. كانت خارج
السياسة التي كانت ستحميها.
- الدرس العملي ليس "كانت Unsplash مهملة". إنه أن الرابط الذي يمكنك استخدامه دون قراءة أي توثيق هو رابط لم تقرأ سياسة إيقافه أيضًا.
- سبق التعطّل الإعلانَ. مشكلة على Drupal فُتحت في 28 نوفمبر 2022 أبلغت بالفعل عن "أحصل دائمًا على خطأ تطبيق Heroku"، قبل ثمانية عشر شهرًا من مدخل الإيقاف النهائي. هذا كيف تموت مثل هذه الخدمات: ببطء، ثم في إعلان لن تراه أبدًا.
الإعلان أصعب في الإيجاد من العطل نفسه
نُشر إيقاف 2021 على changelog.unsplash.com، وهذا هو الرابط الذي
ترتبط به كل تقارير الأخطاء المعاصرة — بما في ذلك تقرير Drupal أعلاه. ثلاثة
قياسات:
- نقطة نهاية HTTPS معطلة.
openssl s_client -connect changelog.unsplash.com:443يُرجعtlsv1 alert internal error— تفشل المصافحة قبل تقديم أي شهادة. لذا فإن كل رابط من حقبة 2021، والذي كانhttps://، ميت في المتصفح. - عبر HTTP العادي يُعيد التوجيه، ولكن بلا فائدة. عند تتبع
http://changelog.unsplash.com/deprecations/2021/11/25/source-deprecation.html، ينتهي بعد قفزتين بخطأ400عندunsplash.com/@documentation/changelog/deprecations/2021/11/25/source-deprecation/html— تم ابتلاع المسار بواسطة مسار اسم المستخدم في الموقع. - الأرشيف يحتوي على فجوة تحديدًا في مكان الإيقاف النهائي. آخر لقطة ناجحة لآلة Wayback من سجل التغييرات القديم هي 24 مارس 2024؛ وأول لقطة للسجل الجديد هي 23 أغسطس 2024. أُعلن الإيقاف النهائي في 11 يونيو 2024 — داخل تلك الفجوة التي تمتد خمسة أشهر.
لا شيء من هذا مؤامرة؛ إنه ترحيل نظام إدارة محتوى عادي. لكن النتيجة حقيقية، وهي السبب في اقتباس هذا المقال لكلا المدخلين كاملين: يجب أن يبقى السجل الأساسي لأي إيقاف أطول عمرًا من الشيء الذي أوقفه، وهنا كاد ألا يفعل ذلك.
ما الذي تعطّل فعليًا
ليست مشاريع جانبية. الأعطال أدناه هي مدخلات علنية في متتبعات المشاكل؛ العناوين والتواريخ والحالات مأخوذة من واجهتي GitHub وdrupal.org البرمجيتين.
| المشروع | المشكلة | تاريخ الفتح | ما تقوله |
|---|---|---|---|
| MUI (Material UI) | #42736 | 24 يونيو 2024 | "[docs] رابط صورة Unsplash العشوائية لم يعد يعمل" — القالب الرسمي Sign-in side أصبح يحمل صورة ميتة. أُغلقت بعد ثلاثة أيام. |
| Nextcloud | #115 | 17 يناير 2023 | "الترحيل إلى Unsplash API" — بُني تطبيق الخلفية على روابط Source. بقيت مفتوحة ثمانية عشر شهرًا، وأُغلقت في 16 يوليو 2024. |
| sindresorhus/Actions | #248 | 28 مايو 2024 | "Get Unsplash Image: خطأ 503" — إجراء اختصارات على iOS/macOS، تعطّل قبل أسبوعين من إعلان الإيقاف النهائي. |
| Drupal — Gin Login | #3324054 | 28 نوفمبر 2022 | "أوقفت Unsplash خدمة source.unsplash.com — هذا يؤخر تحميل reCAPTCHA، مما يمنع المستخدمين من تسجيل الدخول." |
اقرأ الصف الأخير مجددًا، لأنه يستحق الاستيعاب. صورة زخرفية بجانب نموذج تسجيل دخول — الأصل الأوضح غير الحرج في الصفحة — تدهورت إلى انقطاع في المصادقة، لأن طلبًا بطيئًا من طرف ثالث كان يقف أمام CAPTCHA التي احتاجها نموذج تسجيل الدخول. لم يكتبه أحد بهذا الشكل. نشأ من ترتيب تحميل المتصفح للأشياء.
مساعد البرمجة لديك لم يستلم المذكرة
هنا الجزء الذي يحوّل عطلًا حدث في 2024 إلى مشكلة في 2026. source.unsplash.com
كان موثّقًا، ومنشورًا في مدونات، ومُدرَّسًا، ومنسوخًا لنحو ثماني سنوات. كل هذا النص موجود في
بيانات تدريب النماذج التي تكتب الآن كودنا الأولي — والنص لا تنتهي صلاحيته. ثلاثة
أعداد:
| القياس | القيمة في 30 أغسطس 2026 | كيف تم أخذها |
|---|---|---|
ملفات تحتوي على source.unsplash.com |
3,344 | واجهة بحث الكود في GitHub، q=source.unsplash.com (تشمل الكود العلني المفهرس فقط — حد أدنى، لا إجمالي) |
| مستودعات في عينة من 100 ملف أُنشئت بعد الإيقاف النهائي | 12 من 77 | نفس الاستعلام، 100 نتيجة، مُصفّاة إلى 77 مستودعًا فريدًا، تمت مقارنة created_at بتاريخ 11 يونيو 2024 |
| …والمستودعات في تلك العينة التي جرى الدفع (push) إليها خلال الـ12 شهرًا الماضية | 20 من 77 | مستودعات حية، لا أرشيفات — بما في ذلك elastic/kibana، الذي لا يزال ملف العرض التوضيحي فيه يقرأ imageUrl: 'https://source.unsplash.com/64x64/?dingo' |
التنزيلات الشهرية لـunsplash-source-es6 |
23 | واجهة سجل npm — غلاف لخدمة ميتة، آخر نشر لها في 2022، لا يزال يُثبَّت |
| منشورات Stack Overflow التي تذكره | 1,459 | واجهة Stack Exchange، إجمالي /search/excerpts |
الدليل الأكثر مباشرة ليس في كود التطبيقات على الإطلاق — إنه في الموجّهات (prompts). النتيجة الأولى لذلك البحث هي مكتبة موجّهات نظام لـGPT تحتوي على السطر "please use unsplash API( https://source.unsplash.com/1280x720/?<PUT YOUR QUERY HERE>". هذا التوجيه لا يزال يُنسخ في مساعدين جدد اليوم. النموذج لا يتحقق من الرابط؛ قيل له أن يستخدمه، وكل مثال رآه قط اتفق مع ذلك.
إذن فشل الكود المُولَّد له سببان مستقلان، وإصلاح أحدهما لا يُصلح الآخر: بيانات تدريب قديمة، وتعليمات قديمة كتبها بشر فوقها. في كلتا الحالتين، العرض هو نفس عائلة الصور التي لا تُحمَّل أبدًا:
source.unsplash.com/random/1200x800 # 503 منذ منتصف 2024 — لن تعود أبدًا
images.unsplash.com/photo-… # CDN حقيقي، لكن المعرفات المحفوظة قد لا توجد
via.placeholder.com/400 # مستطيل رمادي، أُرسل إلى الإنتاج
placehold.co/800x600 # مستطيل رمادي، عن قصد
picsum.photos/800/600 # صورة حقيقية، غير ذات صلة بصفحتك
/placeholder.png # ملف لم يُضف قط إلى المستودع
حاشية على السطر الثاني من تلك القائمة: أثناء كتابة هذا، لم يُكمل
via.placeholder.com مصافحة TLS من شبكة اختبارنا أيضًا، وأجاب بـ403
عبر HTTP العادي. تحقّق منه من شبكتك الخاصة قبل الوثوق به — البديل الاحتياطي الذي
تلجأ إليه هذه الأدوات قد يكون له قصة عطل خاصة به.
الأول فقط معطل. البقية أسوأ بطريقة أكثر خفاءً: تُحمَّل، ويبدو التخطيط مكتملًا، ولا يلاحظ أحد أن الصفحة مُوضَّحة بلا شيء محدد. ولا شيء من هذا خاص بالصور — إنه الشكل العام للمشكلة. صورة النموذج عن الويب هي لقطة، وتستمر نقاط النهاية وعلامات سطر الأوامر وأسماء الحزم والخطط المجانية في التغيّر بعد إغلاق الغالق.
جدول الترحيل
توجد بالضبط ثلاث وجهات، والطريقة الصادقة لعرضها هي بما تتخلى عنه. اختر العمود أولًا، ثم اقرأ صفّك.
| رابط Source القديم | أ. رابط CDN ثابتبلا مفتاح · بلا عشوائية | ب. واجهة Unsplash APIمفتاح · استدعاء من جهة الخادم | ج. وسيط (proxy) خاص بكالمفتاح مخفي · العشوائية عادت |
|---|---|---|---|
/random |
images.unsplash.com/photo-… — صورة واحدة اخترتها |
GET /photos/random |
/?w=1600 |
/random/1600x900 |
…?w=1600&h=900&fit=crop |
/photos/random + معاملات Imgix على الرابط المُرجَع |
/?w=1600&h=900&fit=crop |
/1600x900/?apple,desk |
لا يوجد مكافئ — اختر صورة يدويًا | /photos/random?query=apple,desk |
/?query=apple,desk&w=1600 |
/featured/1600x900?nature |
لا يوجد مكافئ | /photos/random?query=nature لا خليفة لميزة "featured" |
/?query=nature&w=1600 |
/collection/67920491/1600x900 |
لا يوجد مكافئ | /photos/random?collections=67920491 |
/?collections=67920491&w=1600 |
/user/scottwebb/1600x900 |
لا يوجد مكافئ | /photos/random?username=scottwebb |
/?username=scottwebb&w=1600 |
/daily |
ثبّت صورة واحدة، ودوّرها في نسختك المبنية | لا يوجد مكافئ — خزّن صورة عشوائية واحدة لمدة 24 ساعة بنفسك | نفس الشيء، مع التخزين المؤقت في الوسيط |
الخيار أ هو ما يريده معظم الناس فعليًا. إذا كانت الصورة زخرفية — صورة رئيسية، أو لوحة جانبية لتسجيل الدخول، أو خلفية بطاقة — لم تكن بحاجة أبدًا لصورة مختلفة في كل طلب. اختر واحدة، احتفظ برابط CDN، وتتوقف الصفحة عن الاعتماد على أي شيء عشوائي:
<img src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1600&h=900&fit=crop&auto=format"
width="1600" height="900" alt="…">
# المعاملات المدعومة رسميًا: w، h، crop، fit، fm، auto=format، q، dpr.
# احتفظ بأي معامل ixid أعطته لك الواجهة — فهو ما يُبلّغ عن المشاهدة.
الخيار ب هو المسار الرسمي، وينقل الاستدعاء إلى جهة الخادم، لأن
Client-ID في جافاسكريبت الواجهة الأمامية هو بيانات اعتماد منشورة. لاحظ القاعدتين اللتين
تُوقعان الناس بالخطأ: لا يمكن دمج collections/topics مع
query في نفس الطلب، ومعامل count (بحد أقصى 30) يُغيّر شكل الاستجابة
إلى مصفوفة حتى عندما تكون 1.
curl "https://api.unsplash.com/photos/random?query=nature&orientation=landscape" \
-H "Authorization: Client-ID YOUR_ACCESS_KEY" \
-H "Accept-Version: v1"
# → JSON. الصورة موجودة عند .urls.regular / .urls.raw (أضف w/h/fit بنفسك).
# → X-Ratelimit-Limit: 1000 X-Ratelimit-Remaining: 999
الخيار ج: أعِد بناء Source، في نحو أربعين سطرًا
إذا كان ما فقدته حقًا هو السلوك — رابط بلا مفتاح يُرجع صورة مختلفة كل
مرة، قابل للاستخدام مباشرة من وسم <img>، في حقل نظام إدارة محتوى، أو في موقع ثابت
حيث لا يوجد خادم — فعليك تشغيل تلك النقطة النهائية بنفسك. إنه عامل صغير واحد أمام
الواجهة، والأشياء الثلاثة التي تجعله يصمد أمام الإنتاج هي التخزين المؤقت،
وفحص المُحيل (referrer)، وتمرير بقية سلسلة الاستعلام إلى CDN.
// Cloudflare Workers. في أماكن أخرى (Deno Deploy، Val Town…) الشكل نفسه،
// لكن افتح تخزينًا مؤقتًا مُسمّى باستخدام caches.open() بدلًا من caches.default.
// UNSPLASH_KEY يبقى في جهة الخادم. المستدعون لا يرونه أبدًا.
const ALLOWED = ["example.com", "www.example.com"]; // نطاقاتك فقط
const API_PARAMS = ["query", "collections", "topics", "username", "orientation"];
const TTL = 60; // ثوانٍ — تحمي الحصة بالساعة
const host = (value) => { try { return new URL(value).hostname; } catch { return null; } };
export default {
async fetch(req, env, ctx) {
// 0. GET فقط: ترفض واجهة Cache API تخزين أي شيء آخر، ونقطة نهاية
// للصور ليس لديها فعل آخر تجيب عليه.
if (req.method !== "GET")
return new Response("Method not allowed", { status: 405 });
const url = new URL(req.url);
// 1. صفحاتك فقط هي من يمكنها تضمينه — نقطة نهاية عامة لصورة عشوائية
// على الإنترنت المفتوح هي حد معدل شخص آخر يتم استهلاكه.
const ref = req.headers.get("referer"); // غائب في الكثير من العملاء الشرعيين
if (ref && !ALLOWED.includes(host(ref)))
return new Response("Forbidden", { status: 403 });
// 2. خزّن مؤقتًا لكل تركيبة معاملات، بحيث تكلف صفحة بها 12 صورة
// استدعاء API واحد في الدقيقة بدلًا من اثني عشر لكل عرض.
const cache = caches.default;
const hit = await cache.match(req);
if (hit) return hit;
// 3. اطلب من الواجهة الرسمية صورة عشوائية.
const api = new URL("https://api.unsplash.com/photos/random");
for (const p of API_PARAMS)
if (url.searchParams.has(p)) api.searchParams.set(p, url.searchParams.get(p));
const r = await fetch(api, { headers: {
Authorization: "Client-ID " + env.UNSPLASH_KEY,
"Accept-Version": "v1",
}});
// 403 هنا يعني عادةً الحصة بالساعة، وليس مفتاحًا سيئًا — ارفع TTL، لا داعي للذعر.
if (!r.ok) return new Response("Upstream " + r.status, { status: 502 });
const photo = await r.json();
// 4. أعِد بناء رابط الصورة: احتفظ بـixid، أضف معاملات الحجم من المستدعي.
const img = new URL(photo.urls.raw); // .raw يحمل ixid بالفعل
for (const [k, v] of url.searchParams)
if (!API_PARAMS.includes(k)) img.searchParams.set(k, v); // w، h، fit، q…
const res = new Response(null, { status: 302, headers: {
Location: img.toString(),
"Cache-Control": "public, max-age=" + TTL,
// الائتمان (الفضل) ينتقل مع إعادة التوجيه؛ يجب أن تكون قيم الرأس ASCII، ومن هنا الترميز.
"X-Photo-Credit": encodeURIComponent(photo.user.name + " on Unsplash"),
"X-Photo-Link": photo.links.html,
}});
ctx.waitUntil(cache.put(req, res.clone()));
return res;
},
};
ترحيل رابط عندها يصبح مجرد بحث واستبدال، وهذا بالضبط ما يجعل هذا الخيار يستحق العشرين دقيقة:
- https://source.unsplash.com/collection/67920491/1600x900
+ https://img.example.com/?collections=67920491&w=1600&h=900&fit=crop
ملاحظتان تصميميتان، كلتاهما كلّفتا شخصًا ما بعد ظهر سيئًا قبل تدوينهما. إعادة
التوجيه (302) بدلًا من الوساطة (proxying) للبايتات تُبقيك بعيدًا عن مسؤولية عرض النطاق
الترددي و تُبقي المشاهدة محسوبة على CDN الخاص بـUnsplash، وهذا ما تطلبه الإرشادات. أما
فحص Referer فمتساهل عمدًا عندما يكون الرأس غائبًا — إذ يُزيله الكثير من
العملاء الشرعيين — مع بقائه رغم ذلك موقفًا الحالة الواضحة حيث تصبح نقطة نهايتك
واجهة API مجانية لصور شخص آخر.
القواعد التي ترثها بمجرد استخدام الواجهة
لم يكن لدى Source قواعد لأنه لم يكن لديه حساب. الواجهة لديها خمس قواعد تُغيّر كيفية تصميمك لهذا الأمر، جميعها من التوثيق الحالي:
- حدود المعدل بالساعة، وصغيرة في البداية. 50 طلبًا/ساعة
في وضع العرض التوضيحي؛ 1,000/ساعة بعد اعتماد تطبيقك للإنتاج. تُحتسب فقط
الاستدعاءات إلى
api.unsplash.com— طلبات الصور إلىimages.unsplash.comلا تُحتسب. اقرأX-Ratelimit-Remainingفي كل استجابة. - الربط المباشر (hotlinking) إلزامي، وليس مسموحًا فقط. تشترط Unsplash تضمين روابط الصور التي تُرجعها الواجهة مباشرة، حتى يمكن نسب مشاهدات الصور إلى المصور. نسخ الملف على CDN خاص بك هو التحسين الوحيد غير المسموح به.
- احتفظ بمعامل
ixid. تغيير حجم واقتصاص الرابط المُرجَع متوقّع؛ أما إزالة المعامل الذي يُعرّف تطبيقك فليس كذلك. - الإسناد وتتبع التنزيلات جزء من الاتفاقية — يُنسب الفضل للمصوّر ولـ Unsplash، ويُبلَّغ عن "التنزيل" عبر نقطة نهاية التنزيل الخاصة بالصورة عندما يأخذ المستخدم الملف، وهو حدث يجب عليك إطلاقه بنفسك.
- المنتجات الموزَّعة تحتاج إلى تسجيل عميل ديناميكي. إذا كنت تشحن ملحقًا، أو قالبًا، أو نظام إدارة محتوى مستضاف ذاتيًا، فإن مفتاحًا واحدًا مشتركًا هو مخالفة للسياسة ونقطة فشل واحدة في آنٍ معًا؛ لدى الواجهة تدفق تسجيل مخصص تحديدًا لتلك الحالة.
هذه هي اللحظة المناسبة للصدق بشأن النطاق: إذا كنت ستوصّل واجهة API ومفتاحًا على أي حال، فإن اختيار أي واجهة صور فجأة أصبح مفتوحًا، ويستحق قضاء خمس دقائق عليه قبل كتابة العميل. قارنّا المجانية منها — الحصص، والقواعد، وسلوك البحث، وأشكال الاستجابة — في مقارنة واجهات API المجانية للصور المخزنة.
إذا كنت تريد فقط عنصرًا نائبًا، قل ذلك
جزء كبير من استخدام Source لم يكن قط عن Unsplash. كان "ضع شيئًا على شكل صورة هنا بينما أبني التخطيط". لهذا، لا تزال الخدمات بلا مفتاح موجودة وهي الإجابة الصحيحة:
| الخدمة | مفتاح؟ | ما تحصل عليه | أين تتوقف |
|---|---|---|---|
| Lorem Picsum | لا | صور فوتوغرافية حقيقية: picsum.photos/800/600، صورة ثابتة عبر /id/237/… أو /seed/xxx/…، مع ?grayscale و?blur=1..10. نقطة نهايته /v2/list تنسب كل صورة إلى صفحتها على Unsplash ومؤلفها. |
لا استهداف للموضوع إطلاقًا. الصورة لن تتعلق بصفحتك. |
| placehold.co | لا | مستطيلات موسومة بأي حجم — حشو مخططات هيكلية صادق. | إنه صندوق رمادي، ويبدو كذلك في لقطة شاشة تُشارَك مع عميل. |
| Openverse | لا | كتالوج مرخّص علنًا بواجهة API عامة، تديره WordPress.org. | مطابقة بالكلمات المفتاحية، والتراخيص تختلف حسب العنصر — يجب عليك قراءتها. |
التمييز المهم هنا: العنصر النائب مؤقت بالتعريف. إذا نجت الصورة إلى الإنتاج، فلم تعد عنصرًا نائبًا — إنها توضيح لم يختره أحد، ويستطيع القارئ ملاحظة ذلك.
مفتاح واحد بدلًا من ثلاثة
هذا هو جزء الترحيل الذي لا يخطط له أحد: نادرًا ما يستقر من يغادرون Source على
واجهة API واحدة. تحتاج الصفحة إلى صورة رئيسية، وصورتين لقسمين، وشيء
لشبكة بطاقات، والإجابة الصادقة عادة هي Unsplash مع Pexels مع
Pixabay — ثلاثة تسجيلات، وثلاثة مخططات مصادقة، وثلاثة أشكال JSON، وثلاثة نماذج
ترقيم صفحات، وثلاث مجموعات من قواعد الإسناد، كل ذلك لملء نفس وسوم
<img>. عمل التكامل هذا هو الفاتورة الحقيقية لزوال رابط بلا مفتاح،
وهو يصل بعد أسابيع من العطل الذي تسبب فيه.
دمج ذلك في تكامل واحد هو ما بنينا Pexafy من أجله: مفتاح واحد عبر 9 مكتبات مرخصة مجانًا في مخطط واحد، مع بحث دلالي على مستوى الجملة — بحيث يُرجع وصف كامل مثل "شاشة هاتف متصدعة على مكتب خشبي، مصوَّرة من الأعلى" نتائج مُرتّبة بدلًا من لا شيء. حدّان، مذكوران بوضوح، لأن هذا المقال بأكمله يدور حول عدم التفاجؤ مرتين: يحتاج مفتاحًا، لذا فهو لا يستعيد ما كانت عليه Source — إنه ينتمي إلى نفس فئة واجهة Unsplash API الرسمية أعلاه؛ ويحمل تصويرًا مرخصًا مجانًا، لا صورًا تحريرية أو صور علامة تجارية.
الجزء الجديد فعليًا موجّه إلى المساعدين أعلاه: خادم MCP
على mcp.pexafy.com/mcp يعني أن نموذجًا كان سيستشهد بخلاف ذلك برابط صورة
من الذاكرة يمكنه الآن البحث في كتالوج حقيقي وإرجاع صورة موجودة فعليًا، مع سطر
الائتمان الخاص بها مُرفقًا. هذه إجابة أفضل للروابط الميتة المكتوبة آليًا من أي قاعدة lint، والمنطق
موضح في
بنية تحتية
للبحث عن الصور لوكلاء الذكاء الاصطناعي.
دقّق ممتلكاتك في عشر دقائق
مهما رحّلت إليه، افعل هذا الجزء أولًا — لا يمكنك إصلاح روابط لم تجدها. Source هو مجرد مثال اليوم؛ الخطوات الثلاث نفسها تنطبق على كل أصل خارجي تضمّنه.
# 1. كل شيء في المستودع، بما في ذلك التوثيق والاختبارات والملفات التجريبية وREADMEs.
grep -rn --binary-files=without-match \
-e "source.unsplash.com" -e "via.placeholder.com" -e "/placeholder.png" .
# 2. كل ما تحتفظ به قاعدة البيانات — أجسام أنظمة إدارة المحتوى هي حيث تختبئ هذه لأطول فترة.
psql -c "SELECT id FROM posts WHERE body LIKE '%source.unsplash.com%'"
# 3. كل ما يطلبه الموقع المبني فعليًا: اجمعه وأدرِج الأعطال.
# طابق السمة، لا امتداد الملف — نادرًا ما تنتهي روابط الصور بـ.jpg.
grep -rhoE 'src="[^"]+"' dist/ \
| cut -d'"' -f2 | grep -E '^https?://' | sort -u \
| xargs -P8 -I{} curl -s -o /dev/null -w "%{http_code} {}\n" {} \
| grep -v "^200"
# 503 https://source.unsplash.com/random/1200x800 ← ما تبحث عنه
ثم قرّر، مرة واحدة، إلى أي مدى يُسمح للصور الخارجية بأن تكلّفك. أربع قواعد تصمد أمام الإيقاف القادم، أيًا كان من يتسبب فيه:
- اجلب وقت البناء، لا وقت الطلب. صورة تُحل أثناء البناء تفشل في CI، أمام مطور، بدلًا من الساعة 3 فجرًا أمام مستخدم.
- لا تدع أصلًا زخرفيًا يعيق مسارًا حرجًا أبدًا. لا تُحمّل مسبقًا أي شيء خارجي
فوق نموذج تسجيل دخول؛ أعطِ كل
<img>من طرف ثالث بديلًا احتياطيًا عبرonerrorوwidth/heightصريحين بحيث يكلّف الفشل صندوقًا فارغًا، لا انزياحًا في التخطيط أو سكريبتًا متوقفًا. - أضف الفحص إلى CI. الخطوة 3 أعلاه، إن نُفّذت على مخرجاتك المبنية، تحوّل "لاحظ شخص ما ذلك في النهاية" إلى بناء أحمر. إنها الخطوة الوحيدة التي تمنع التكرار.
- ضع ميزانية لاعتمادياتك الخارجية مثل أي شيء آخر. دوّن أي المضيفين يُسمح لصفحاتك بالاعتماد عليهم وماذا يحدث عندما يتعطل كل واحد منهم. الرابط الذي لم تحتج للتسجيل من أجله لا يزال اعتمادية — أثبتت Source أنه ببساطة اعتمادية لا يملكها أحد.
المراجع والحواشي
1 كل رمز حالة، وعدد، وسطر مقتبس في هذا المقال
أُخذ من مصدره الأساسي في 30 أغسطس 2026. أُخذت رموز حالة HTTP
باستخدام curl مقابل كل نمط رابط؛ جميعها أعادت 503 مع
server: Heroku وجسم يُضمّن
herokucdn.com/error-pages/application-error.html. تم التأكد من تحليل DNS في
اليوم نفسه (CNAME إلى مضيف herokudns.com).
2 كلا مدخلي سجل التغييرات مقتبسان حرفيًا من
unsplash.com/documentation/changelog. صياغة سياسة الإيقاف (3 أسابيع من الإشعار، رأس Warning، والاستثناء لنقاط النهاية
غير الموثّقة علنًا) مأخوذة من unsplash.com/documentation، في اليوم نفسه.
3 أُعيد إنتاج فشل TLS باستخدام
openssl s_client -connect changelog.unsplash.com:443 (tlsv1 alert internal
error). سلسلة إعادة التوجيه تُبعت باستخدام curl -L. فجوة الأرشيف مأخوذة من
واجهة Wayback CDX: آخر لقطة 200 لـchangelog.unsplash.com في
20240324، وأولى لقطة لـunsplash.com/documentation/changelog في 20240823.
4 عناوين المشاكل، وتواريخ الإنشاء والإغلاق قُرئت من واجهة GitHub
REST API (mui/material-ui#42736، nextcloud/unsplash#115،
sindresorhus/Actions#248) ومن واجهة JSON الخاصة بـdrupal.org لمشكلة
gin_login رقم 3324054، التي يبلغ جسمها "أحصل دائمًا على خطأ تطبيق Heroku"
في نوفمبر 2022.
5 الأعداد: واجهة بحث الكود في GitHub
(3,344 ملفًا؛ عينة من 100 نتيجة مُصفّاة إلى 77 مستودعًا، منها 12 أُنشئت
بعد 11 يونيو 2024 وتم الدفع (push) إلى 20 منها خلال الـ12 شهرًا السابقة)؛
واجهة تنزيلات سجل npm (unsplash-source-es6، 23 تنزيلًا خلال
الـ30 يومًا السابقة)؛ واجهة Stack Exchange /search/excerpts (1,459
منشورًا). بحث الكود يغطي المستودعات العلنية المفهرسة فقط، لذا كل رقم هو حد أدنى.
المصادر الأساسية: سجل تغييرات واجهة Unsplash API · توثيق واجهة Unsplash API · إرشاد الإسناد في Unsplash · حالة Unsplash · MUI #42736 · Nextcloud #115 · sindresorhus/Actions #248 · Drupal Gin Login #3324054 · Lorem Picsum · Openverse · توثيق Pexafy API و MCP.
الأسئلة الشائعة
هل source.unsplash.com متوقف مؤقتًا أم أُغلق نهائيًا؟
/random، /1600x900/?query، /collection/…، /daily) يعيد الآن HTTP 503 مع صفحة Application Error العامة من Heroku. اسم النطاق لا يزال يُحلّ (resolve)، لذا يظهر الخلل كصورة معطوبة بدلاً من خطأ في الشبكة.ما هو البديل المباشر لـ source.unsplash.com/random؟
images.unsplash.com/photo-…?w=1600&h=900&fit=crop — لا يحتاج مفتاحًا لكنه يعيد دائمًا الصورة نفسها، وهو ما كانت تحتاجه معظم الاستخدامات الزخرفية أصلاً. الواجهة البرمجية الرسمية، GET https://api.unsplash.com/photos/random مع ترويسة Authorization: Client-ID، تعيد العشوائية لكن يجب استدعاؤها من جهة الخادم. وكيل صغير (proxy) تملكه بنفسك أمام هذه النقطة النهائية هو الخيار الوحيد الذي يعيد لك رابطًا بدون مفتاح يمكنك وضعه مباشرة داخل وسم <img>.لماذا لا تزال أدوات البرمجة بالذكاء الاصطناعي تولّد روابط source.unsplash.com في 2026؟
هل يمكنني الحصول على صورة عشوائية من Unsplash دون مفتاح API؟
/photos/random، الذي يتطلب Client-ID. لديك طريقان بدون مفتاح: وكيل (proxy) تستضيفه بنفسك حيث يبقى المفتاح في جهة الخادم بينما يبدو الرابط العام كالرابط القديم، أو خدمة عنصر نائب من طرف ثالث مثل Lorem Picsum (picsum.photos/800/600)، التي تقدّم صورًا حقيقية بلا مفتاح لكن بلا أي استهداف لموضوع الصورة.هل تسمح واجهة Unsplash البرمجية بتنزيل الصور واستضافتها ذاتيًا؟
ixid عند تغيير حجم الرابط أو اقتصاصه، ونسب الفضل للمصوّر ولـ Unsplash، وتفعيل نقطة تنزيل الصورة (download endpoint) عندما يقوم المستخدم بتنزيل الملف. نسخ الملفات إلى شبكة CDN خاصة بك هو التحسين الوحيد غير المسموح به.لماذا لم يحمِ إشعار الإهمال لعام 2021 المستخدمين الحاليين؟
Warning — فتنطبق على الحقول ونقاط النهاية الموثقة علنًا فقط، وتنص الفقرة نفسها على أن أي شيء غير موثق قد يتغير دون أي إشعار. ولم تكن Source يومًا نقطة نهاية موثقة ضمن الواجهة البرمجية، لذا وقعت خارج نطاق السياسة التي كانت ستحميها.كيف أعثر على كل روابط source.unsplash.com الميتة في مشروعي؟
grep -rn "source.unsplash.com" . — لأن هذه الروابط تبقى لأطول فترة في الأكواد النموذجية. استعلم قاعدة البيانات، لأن محتوى مقالات نظام إدارة المحتوى هو المكان الذي تختبئ فيه هذه الروابط (WHERE body LIKE '%source.unsplash.com%'). ثم افحص مخرجات البناء (build output): استخرج كل رابط صورة وأرسل طلبًا لكل واحد منها، وسجّل أي رابط لا يعيد 200. أضف هذه الخطوة الأخيرة إلى CI ليفشل البناء عند وجود أصل خارجي ميت بدلاً من أن تفشل الصفحة فقط.