source.unsplash.com ist verschwunden: Der Post-Mortem und alle Wege, es zu ersetzen
2021 abgekündigt mit „bestehende Verwendungen funktionieren weiter“, im Juni 2024 abgeschaltet, und wird bis heute in neuen Code geschrieben. Der nüchterne Post-Mortem — und die drei Ersatzlösungen, inklusive Proxy, der die schlüssellose Zufälligkeit zurückbringt.
Ein Stockfoto-Dienst hat eine Subdomain abgeschaltet, und zwei Jahre später bricht sie immer noch Dokumentationsseiten, Login-Bildschirme, Kursübungen und frisch generierten Code. Dies ist ein Post-Mortem einer URL — was sie tat, was sie umgebracht hat, und was an ihre Stelle gehört — plus ein nüchterner Blick auf den seltsameren Teil der Geschichte: Die Maschinen, die unseren Code schreiben, haben nicht bemerkt, dass sie verschwunden ist.
Die HTTP-Antworten, die beiden Wort-für-Wort zitierten Changelog-Einträge, die API-Limits, die Issue-Tracker der betroffenen Projekte sowie die Zählungen von GitHub, npm und Stack Overflow stammen allesamt aus Primärquellen, mit der Methode für jede einzelne in den Fußnoten. Wer nur die Lösung sucht, springt direkt zu der Migrationstabelle.
Was man heute bekommt, wenn man es trotzdem anfragt
Ein Befehl, kein Schlüssel, reproduzierbar von jeder beliebigen Maschine aus:
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
# body: ein iframe, das auf herokucdn.com/error-pages/application-error.html zeigt
Hier liegt kein DNS-Fehler vor. source.unsplash.com lässt sich weiterhin auflösen — es
ist ein CNAME auf einen herokudns.com-Host —, die Anfrage wird also beantwortet, nur
eben nicht von einer Anwendung. Dieses Detail ist wichtiger, als es klingt: Ein Browser, der ein
schnelles 503 mit HTML-Body erhält, rendert einen kaputten Bild-Platzhalter, und jeder
Code, der response.ok oder einen onerror-Handler abfragt, den man nie
geschrieben hat, läuft in genau den Fehlerpfad, den man nie getestet hat.
| URL-Muster | Was früher zurückkam | Heute |
|---|---|---|
source.unsplash.com/random | Ein zufälliges Foto, jede Größe | 503 |
source.unsplash.com/random/1600x900 | Ein zufälliges Foto, auf Größe zugeschnitten | 503 |
source.unsplash.com/1600x900/?apple,desk | Ein zufälliges Foto passend zu Suchbegriffen | 503 |
source.unsplash.com/featured/1600x900?nature | Ein zufälliges featured-Foto | 503 |
source.unsplash.com/collection/190727/800x600 | Ein zufälliges Foto aus einer Collection | 503 |
source.unsplash.com/user/scottwebb/1600x900 | Ein zufälliges Foto eines Fotografen | 503 |
source.unsplash.com/daily | Das Foto des Tages | 503 |
Einzeln geprüft mit curl -o /dev/null -w "%{http_code}". Die Suchfunktion wurde, wie
angekündigt, zuerst zurückgefahren; heute ist die gesamte Anwendung abgeschaltet, sodass diese
Unterscheidung nicht mehr existiert.
Drei Jahre zwischen „deprecated“ und „aus“
Beide Ankündigungen sind weiterhin lesbar, an einem Ort, unter unsplash.com/documentation/changelog. Vollständig zitiert, weil der Wortlaut die ganze Geschichte erzählt:
25. November 2021 — „Unsplash Source being deprecated“
„Unsplash Source is being deprecated. Existing uses will continue to work, however for new projects use the full Unsplash API.“
11. Juni 2024 — „Unsplash Source sunset“
„Unsplash Source has been officially unsupported since its deprecation in 2021. As part of the final sunsetting, we will first wind down by disabling the search feature, and in the coming weeks turn off the application entirely. Existing uses of Source — particularly production-level ones — should migrate as soon as possible to the full Unsplash API.“
Liest man beide nacheinander, wird das Fehlermuster offensichtlich. Die Mitteilung von 2021 enthielt ein Versprechen (existing uses will continue to work) und kein Datum. Ein Entwickler, der sie 2021 las, hatte jeden Grund, funktionierenden Code unangetastet zu lassen; ein Entwickler, der 2022 dazustieß, hat sie nie gelesen. Die Mitteilung von 2024 nannte „the coming weeks“ — drei Jahre später, auf einer Seite, die niemand als Lesezeichen gesetzt hatte.
Unsplash veröffentlicht tatsächlich eine Deprecation-Policy, und sie ist durchaus vernünftig — die
Dokumentation besagt, dass Änderungen an öffentlich dokumentierten Feldern und Endpunkten im
Changelog mit mindestens 3 Wochen Vorlauf angekündigt werden und Endpunkte
während der Deprecation-Phase einen Warning-Header zurückgeben. Derselbe Absatz enthält
den Satz, der erklärt, warum davon nichts Source schützte: „For any non-publicly documented
fields or endpoints, we may make changes to these with no warning.“ Source war nie ein
Endpunkt der dokumentierten API. Es lag außerhalb der Policy, die es hätte abdecken können.
- Die praktische Lehre lautet nicht „Unsplash war nachlässig“. Sie lautet: Eine URL, die man ohne jede Dokumentation nutzen kann, ist auch eine URL, deren Deprecation-Policy man nicht gelesen hat.
- Der Ausfall ging der Ankündigung voraus. Ein am 28. November 2022 eingereichtes Drupal-Issue berichtet bereits „I get always a Heroku application error“ — achtzehn Monate vor dem Sunset-Eintrag. So sterben diese Dienste: langsam, dann in einer Ankündigung, die man nie zu Gesicht bekommt.
Die Ankündigung ist schwerer zu finden als der Ausfall
Die Deprecation von 2021 wurde auf changelog.unsplash.com veröffentlicht, und genau
auf diese URL verweist jeder zeitgenössische Bugreport — einschließlich des Drupal-Issues oben.
Drei Messungen:
- Der HTTPS-Endpunkt ist defekt.
openssl s_client -connect changelog.unsplash.com:443lieferttlsv1 alert internal error— der Handshake scheitert, bevor überhaupt ein Zertifikat präsentiert wird. Jeder Link aus der 2021er-Ära, derhttps://nutzte, ist damit im Browser tot. - Über reines HTTP wird umgeleitet, aber nicht nützlich. Folgt man
http://changelog.unsplash.com/deprecations/2021/11/25/source-deprecation.html, landet man zwei Hops später bei einem400unterunsplash.com/@documentation/changelog/deprecations/2021/11/25/source-deprecation/html— der Pfad wurde von der Username-Route der Seite verschluckt. - Das Archiv hat genau dort ein Loch, wo der Sunset liegt. Der letzte erfolgreiche Snapshot des alten Changelogs in der Wayback Machine stammt vom 24. März 2024; der erste Snapshot des neuen vom 23. August 2024. Der Sunset wurde am 11. Juni 2024 angekündigt — innerhalb dieser fünfmonatigen Lücke.
Nichts davon ist eine Verschwörung; es ist eine gewöhnliche CMS-Migration. Aber die Konsequenz ist real, und sie ist der Grund, warum dieser Artikel beide Einträge vollständig zitiert: Die Primärquelle einer Deprecation sollte länger existieren als das, was sie deprecatet — und hier wäre das beinahe nicht der Fall gewesen.
Was tatsächlich kaputtging
Keine Nebenprojekte. Die folgenden Ausfälle sind öffentliche Issue-Tracker-Einträge; Titel, Daten und Status stammen aus den APIs von GitHub und drupal.org.
| Projekt | Issue | Eröffnet | Was es besagt |
|---|---|---|---|
| MUI (Material UI) | #42736 | 24. Jun. 2024 | „[docs] Random Unsplash photo URL is no longer functional“ — das offizielle Sign-in side-Template lieferte ein totes Bild aus. Drei Tage später geschlossen. |
| Nextcloud | #115 | 17. Jan. 2023 | „Migrate to Unsplash API“ — die Hintergrund-App war auf Source-URIs aufgebaut. Achtzehn Monate offen, geschlossen am 16. Juli 2024. |
| sindresorhus/Actions | #248 | 28. Mai 2024 | „Get Unsplash Image: 503 Error“ — eine iOS/macOS-Shortcuts-Action, kaputt zwei Wochen vor Ankündigung des Sunsets. |
| Drupal — Gin Login | #3324054 | 28. Nov. 2022 | „Unsplash has deprecated source.unsplash.com — this delays reCAPTCHA from loading, preventing users from logging in.“ |
Diese letzte Zeile lohnt eine zweite Lektüre, denn sie ist es, die man sich einprägen sollte. Ein dekoratives Bild neben einem Login-Formular — das offenkundig unkritischste Asset auf der Seite — verschlechterte sich zu einem Authentifizierungsausfall, weil eine langsame Drittanbieter- Anfrage vor dem CAPTCHA hing, das das Login-Formular benötigte. Niemand hat es so geplant. Es entstand aus der Reihenfolge, in der ein Browser Dinge lädt.
Ihr Coding-Assistent hat die Mitteilung nicht bekommen
Hier kommt der Teil, der aus einem Ausfall von 2024 ein Problem von 2026 macht.
source.unsplash.com wurde etwa acht Jahre lang dokumentiert, gebloggt, gelehrt und
kopiert. All dieser Text steckt in den Trainingsdaten der Modelle, die heute unseren Starter-Code
schreiben — und Text verfällt nicht. Drei Zählungen:
| Messung | Wert am 30. Aug. 2026 | Wie erhoben |
|---|---|---|
Dateien mit source.unsplash.com |
3.344 | GitHub-Codesuch-API, q=source.unsplash.com (nur indexierter öffentlicher Code — eine Untergrenze, keine Gesamtzahl) |
| Repositories in einer 100-Dateien-Stichprobe, erstellt nach dem Sunset | 12 von 77 | Gleiche Query, 100 Ergebnisse, dedupliziert auf 77 Repositories, created_at verglichen mit 11. Jun. 2024 |
| …und Repositories in dieser Stichprobe mit Pushes in den letzten 12 Monaten | 20 von 77 | Lebende Repositories, keine Archive — darunter elastic/kibana, dessen Demo-Datei noch imageUrl: 'https://source.unsplash.com/64x64/?dingo' enthält |
Monatliche Downloads von unsplash-source-es6 |
23 | npm-Registry-API — ein Wrapper für einen toten Dienst, zuletzt 2022 veröffentlicht, wird noch immer installiert |
| Stack-Overflow-Beiträge, die es erwähnen | 1.459 | Stack-Exchange-API, /search/excerpts, Gesamtsumme |
Der direkteste Beleg liegt gar nicht im Anwendungscode — er liegt in Prompts. Das Top-Ergebnis dieser Suche ist eine Sammlung von GPT-Systemprompts mit der Zeile „please use unsplash API( https://source.unsplash.com/1280x720/?<PUT YOUR QUERY HERE>“. Diese Anweisung wird heute noch in neue Assistenten hineinkopiert. Das Modell überprüft die URL nicht; es wurde angewiesen, sie zu benutzen, und jedes Beispiel, das es je gesehen hat, stimmte damit überein.
Der Fehler in generiertem Code hat also zwei unabhängige Ursachen, und die eine zu beheben behebt nicht die andere: veraltete Trainingsdaten und veraltete, von Menschen darauf aufbauende Anweisungen. So oder so ist das Symptom dieselbe Familie von Bildern, die nie laden:
source.unsplash.com/random/1200x800 # 503 seit Mitte 2024 — kommt nie zurück
images.unsplash.com/photo-… # echtes CDN, aber gemerkte IDs existieren vielleicht nicht
via.placeholder.com/400 # graues Rechteck, in Produktion ausgeliefert
placehold.co/800x600 # graues Rechteck, mit Absicht
picsum.photos/800/600 # ein echtes Foto, ohne Bezug zur Seite
/placeholder.png # eine Datei, die nie ins Repo aufgenommen wurde
Eine Fußnote zur zweiten Zeile dieser Liste: Beim Schreiben dieses Artikels vollzog auch
via.placeholder.com von unserem Testnetz aus keinen TLS-Handshake und antwortete über
reines HTTP mit 403. Prüfen Sie es im eigenen Netz, bevor Sie sich darauf verlassen —
der Fallback, auf den diese Tools zurückgreifen, kann seine eigene Ausfallgeschichte haben.
Nur der erste dieser Fälle ist wirklich kaputt. Die anderen sind auf subtilere Weise schlimmer: Sie laden, das Layout wirkt fertig, und niemand bemerkt, dass die Seite mit nichts Bestimmtem bebildert ist. Und das alles ist nicht auf Bilder beschränkt — es ist die allgemeine Form des Problems. Das Bild eines Modells vom Web ist eine Momentaufnahme, und Endpunkte, CLI-Flags, Paketnamen und Gratis-Tarife bewegen sich weiter, nachdem der Verschluss zugeklappt ist.
Die Migrationstabelle
Es gibt genau drei Zielpunkte, und der ehrliche Weg, sie darzustellen, ist über das, was man aufgibt. Zuerst die Spalte wählen, dann die eigene Zeile lesen.
| Alte Source-URL | A. Feste CDN-URLkein Schlüssel · keine Zufälligkeit | B. Unsplash-APISchlüssel · serverseitiger Aufruf | C. Eigener ProxySchlüssel verborgen · Zufälligkeit zurück |
|---|---|---|---|
/random |
images.unsplash.com/photo-… — ein selbst gewähltes Foto |
GET /photos/random |
/?w=1600 |
/random/1600x900 |
…?w=1600&h=900&fit=crop |
/photos/random + Imgix-Parameter auf der zurückgegebenen URL |
/?w=1600&h=900&fit=crop |
/1600x900/?apple,desk |
Kein Äquivalent — Foto manuell auswählen | /photos/random?query=apple,desk |
/?query=apple,desk&w=1600 |
/featured/1600x900?nature |
Kein Äquivalent | /photos/random?query=nature für „featured“ gibt es keinen Nachfolger |
/?query=nature&w=1600 |
/collection/67920491/1600x900 |
Kein Äquivalent | /photos/random?collections=67920491 |
/?collections=67920491&w=1600 |
/user/scottwebb/1600x900 |
Kein Äquivalent | /photos/random?username=scottwebb |
/?username=scottwebb&w=1600 |
/daily |
Ein Foto fixieren, im Build rotieren | Kein Äquivalent — ein zufälliges Foto selbst 24 h lang cachen | Dasselbe, mit dem Cache im Proxy |
Option A ist die, die die meisten eigentlich wollen. War das Bild dekorativ — ein Hero, ein seitliches Login-Panel, ein Card-Hintergrund —, brauchte man nie ein anderes Foto pro Anfrage. Eines wählen, die CDN-URL behalten, und die Seite hängt von nichts Zufälligem mehr ab:
<img src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1600&h=900&fit=crop&auto=format"
width="1600" height="900" alt="…">
# Offiziell unterstützte Parameter: w, h, crop, fit, fm, auto=format, q, dpr.
# Jeden ixid-Parameter behalten, den die API geliefert hat — er meldet den View.
Option B ist der offizielle Weg, und dabei verlagert sich der Aufruf auf die
Serverseite, weil ein Client-ID im Frontend-JavaScript eine veröffentlichte
Anmeldeinformation ist. Zwei Regeln, an denen sich viele stoßen: collections/
topics lassen sich nicht mit query in derselben Anfrage kombinieren, und
count (max. 30) ändert die Antwortform in ein Array, selbst wenn es 1 ist.
curl "https://api.unsplash.com/photos/random?query=nature&orientation=landscape" \
-H "Authorization: Client-ID YOUR_ACCESS_KEY" \
-H "Accept-Version: v1"
# → JSON. Das Bild liegt unter .urls.regular / .urls.raw (w/h/fit selbst ergänzen).
# → X-Ratelimit-Limit: 1000 X-Ratelimit-Remaining: 999
Option C: Source nachbauen, in rund vierzig Zeilen
Ging es tatsächlich um das Verhalten — eine schlüssellose URL, die jedes Mal ein anderes Foto
liefert, direkt aus einem <img>-Tag, in einem CMS-Feld oder auf einer statischen
Seite ohne Server nutzbar — dann muss man diesen Endpunkt selbst betreiben. Es ist ein kleiner
Worker vor der API, und die drei Dinge, die dafür sorgen, dass er den Kontakt mit der Produktion
überlebt, sind der Cache, die Referrer-Prüfung und das Durchreichen des restlichen Query-Strings an
das CDN.
// Cloudflare Workers. Anderswo (Deno Deploy, Val Town…) ist die Form dieselbe,
// aber einen benannten Cache mit caches.open() öffnen statt caches.default.
// UNSPLASH_KEY bleibt serverseitig. Aufrufer sehen ihn nie.
const ALLOWED = ["example.com", "www.example.com"]; // nur eigene Domains
const API_PARAMS = ["query", "collections", "topics", "username", "orientation"];
const TTL = 60; // Sekunden — schützt das Stunden-Kontingent
const host = (value) => { try { return new URL(value).hostname; } catch { return null; } };
export default {
async fetch(req, env, ctx) {
// 0. Nur GET: Die Cache-API verweigert das Speichern von allem anderen, und ein
// Bild-Endpunkt hat ohnehin kein anderes Verb zu beantworten.
if (req.method !== "GET")
return new Response("Method not allowed", { status: 405 });
const url = new URL(req.url);
// 1. Nur eigene Seiten dürfen einbetten — ein öffentlicher Zufallsbild-Endpunkt
// im offenen Internet ist fremdes Rate-Limit, das man verbrennt.
const ref = req.headers.get("referer"); // fehlt bei vielen legitimen Clients
if (ref && !ALLOWED.includes(host(ref)))
return new Response("Forbidden", { status: 403 });
// 2. Pro Parameterkombination cachen, damit eine Seite mit 12 Bildern
// einen API-Aufruf pro Minute kostet statt zwölf pro Render.
const cache = caches.default;
const hit = await cache.match(req);
if (hit) return hit;
// 3. Die offizielle API nach einem Zufallsfoto fragen.
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 hier bedeutet meist das Stunden-Kontingent, nicht einen falschen Schlüssel — TTL erhöhen, nicht in Panik geraten.
if (!r.ok) return new Response("Upstream " + r.status, { status: 502 });
const photo = await r.json();
// 4. Bild-URL neu aufbauen: ixid behalten, die Sizing-Parameter des Aufrufers anhängen.
const img = new URL(photo.urls.raw); // .raw trägt ixid bereits mit sich
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,
// Credit reist mit dem Redirect; Header-Werte müssen ASCII sein, daher die Kodierung.
"X-Photo-Credit": encodeURIComponent(photo.user.name + " on Unsplash"),
"X-Photo-Link": photo.links.html,
}});
ctx.waitUntil(cache.put(req, res.clone()));
return res;
},
};
Eine URL zu migrieren wird damit zu Suchen-und-Ersetzen — und genau das macht diese Option die zwanzig Minuten wert:
- https://source.unsplash.com/collection/67920491/1600x900
+ https://img.example.com/?collections=67920491&w=1600&h=900&fit=crop
Zwei Design-Hinweise, die beide erst nach einem schlechten Nachmittag jemandem aufgefallen sind. Der
Redirect (302) statt des Durchreichens der Bytes hält Bandbreitenkosten von Ihnen fern
und sorgt dafür, dass der View weiterhin über Unsplashs CDN gezählt wird, was die
Richtlinien verlangen. Und die Referer-Prüfung ist absichtlich nachsichtig, wenn der
Header fehlt — viele legitime Clients entfernen ihn —, verhindert aber trotzdem den offensichtlichen
Fall, dass der eigene Endpunkt zur kostenlosen Bild-API für Dritte wird.
Die Regeln, die man mit der API erbt
Source hatte keine Regeln, weil es kein Konto gab. Die API hat fünf, die die Architektur des Ganzen verändern — allesamt aus der aktuellen Dokumentation:
- Rate-Limits gelten pro Stunde und sind anfangs klein.
50 Anfragen/Stunde im Demo-Modus; 1.000/Stunde, sobald die
Anwendung für Produktion freigegeben wurde. Nur Aufrufe an
api.unsplash.comzählen — Bildanfragen animages.unsplash.comnicht.X-Ratelimit-Remainingbei jeder Antwort lesen. - Hotlinking ist Pflicht, nicht nur erlaubt. Unsplash verlangt, dass die von der API zurückgegebenen Bild-URLs direkt eingebettet werden, damit Fotoaufrufe dem Fotografen zugeordnet werden können. Die Datei auf ein eigenes CDN zu spiegeln, ist die eine Optimierung, die nicht frei steht.
- Den
ixid-Parameter behalten. Größenänderung und Zuschnitt der zurückgegebenen URL sind vorgesehen; das Entfernen des Parameters, der die Anwendung identifiziert, nicht. - Attribution und Download-Tracking gehören zur Vereinbarung — Fotograf und Unsplash werden genannt, und ein „Download“ wird über den Download-Endpunkt des Fotos gemeldet, sobald ein Nutzer die Datei bezieht — ein Ereignis, das man selbst auslösen muss.
- Verteilte Produkte brauchen dynamische Client-Registrierung. Wer ein Plugin, ein Theme oder ein selbstgehostetes CMS ausliefert, für den ist ein gemeinsam genutzter Schlüssel sowohl ein Verstoß gegen die Policy als auch ein Single Point of Failure; die API hat für genau diesen Fall einen Registrierungsablauf.
Hier lohnt Ehrlichkeit über den Umfang: Wer ohnehin einen API-Schlüssel verdrahtet, dem steht auch die Wahl offen, welche Bild-API es sein soll — und es lohnt sich, dafür fünf Minuten aufzuwenden, bevor der Client geschrieben wird. Wir haben die kostenlosen verglichen — Kontingente, Regeln, Suchverhalten und Antwortformen — in dem Vergleich kostenloser Stockfoto-APIs.
Wer nur einen Platzhalter wollte, sollte das auch sagen
Ein großer Teil der Source-Nutzung hatte nie etwas mit Unsplash im engeren Sinn zu tun. Es ging um „irgendetwas Bildförmiges hierhin, während ich das Layout baue“. Dafür gibt es weiterhin schlüssellose Dienste, und sie sind die richtige Antwort:
| Dienst | Schlüssel? | Was man bekommt | Wo es endet |
|---|---|---|---|
| Lorem Picsum | Nein | Echte Fotografien: picsum.photos/800/600, ein stabiles mit /id/237/… oder /seed/xxx/…, dazu ?grayscale und ?blur=1..10. Der Endpunkt /v2/list nennt zu jedem Foto Unsplash-Seite und Urheber. |
Kein Subject-Targeting. Das Foto hat keinen Bezug zur eigenen Seite. |
| placehold.co | Nein | Beschriftete Rechtecke in jeder Größe — ehrlicher Wireframe-Füller. | Es ist eine graue Box, und sieht auch in einem Screenshot für den Kunden so aus. |
| Openverse | Nein | Ein offen lizenzierter Katalog mit öffentlicher API, betrieben von WordPress.org. | Schlüsselwort-Matching, und Lizenzen variieren pro Element — man muss sie lesen. |
Die entscheidende Unterscheidung: Ein Platzhalter ist per Definition vorübergehend. Übersteht das Bild bis in die Produktion, ist es kein Platzhalter mehr — es ist eine Illustration, die niemand ausgewählt hat, und das merkt man ihr an.
Ein Schlüssel statt drei
Hier ist der Teil der Migration, mit dem niemand plant: Wer Source verlässt, landet selten bei
einer API. Eine Seite braucht einen Hero, zwei Sektionsbilder und etwas für ein Card-Grid,
und die ehrliche Antwort ist meist Unsplash plus Pexels plus Pixabay — drei
Registrierungen, drei Auth-Schemata, drei JSON-Formen, drei Paginierungsmodelle und drei Sätze von
Attributionsregeln, alles um dieselben <img>-Tags zu füllen. Diese Integrations-
arbeit ist die eigentliche Rechnung für eine verschwindende schlüssellose URL, und sie kommt Wochen
nach dem Ausfall, der sie ausgelöst hat.
Genau dafür haben wir Pexafy gebaut: einen einzigen Schlüssel für 9 Bibliotheken mit freien Lizenzen in einem Schema, mit satzweiser semantischer Suche — sodass eine vollständige Beschreibung wie „ein gesprungenes Handy-Display auf einem Holztisch, von oben fotografiert“ gerankte Ergebnisse liefert statt nichts. Zwei Grenzen, klar benannt, weil dieser ganze Artikel davon handelt, kein zweites Mal überrascht zu werden: Es braucht einen Schlüssel, stellt also nicht wieder her, was Source war — es gehört in dieselbe Kategorie wie die offizielle Unsplash-API oben; und es führt freilizenzierte Fotografie, keine redaktionellen oder Marken-Bilder.
Der wirklich neue Teil richtet sich an die Assistenten oben: Ein
MCP-Server unter mcp.pexafy.com/mcp bedeutet, dass ein Modell, das sonst eine
Bild-URL aus dem Gedächtnis zitieren würde, stattdessen einen echten Katalog durchsuchen und ein
Foto zurückgeben kann, das existiert, samt zugehöriger Credit-Zeile. Das ist eine bessere Antwort
auf maschinell geschriebene tote URLs als jede Lint-Regel, und die Überlegung dazu steht in
Bildsuch-
Infrastruktur für KI-Agenten.
Den eigenen Bestand in zehn Minuten prüfen
Egal wohin migriert wird — das hier zuerst tun: URLs, die man nicht gefunden hat, kann man nicht reparieren. Source ist nur das Beispiel von heute; dieselben drei Schritte gelten für jedes externe Asset, das eingebettet wird.
# 1. Alles im Repo, einschließlich Docs, Tests, Fixtures und READMEs.
grep -rn --binary-files=without-match \
-e "source.unsplash.com" -e "via.placeholder.com" -e "/placeholder.png" .
# 2. Alles, was die Datenbank enthält — CMS-Inhalte sind dort, wo sich das am längsten hält.
psql -c "SELECT id FROM posts WHERE body LIKE '%source.unsplash.com%'"
# 3. Alles, was die gebaute Seite tatsächlich anfragt: crawlen und Fehler auflisten.
# Das Attribut abgleichen, nicht eine Dateiendung — Bild-URLs enden selten auf .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 ← wonach Sie suchen
Dann einmalig festlegen, wie viel externe Bilder kosten dürfen. Vier Regeln, die den nächsten Shutdown überleben, egal wer ihn verursacht:
- Zur Build-Zeit abrufen, nicht zur Request-Zeit. Ein Bild, das während des Builds aufgelöst wird, scheitert in der CI, vor den Augen eines Entwicklers — nicht um 3 Uhr nachts vor den Augen eines Nutzers.
- Ein dekoratives Asset darf nie einen kritischen Pfad blockieren. Nichts
Externes vor einem Login-Formular preloaden; jedem Drittanbieter-
<img>einenonerror-Fallback sowie explizitewidth/heightgeben, damit ein Fehlschlag eine leere Box kostet, keinen Layout-Shift oder blockierten Skriptablauf. - Die Prüfung in die CI einbauen. Schritt 3 oben, auf dem gebauten Output ausgeführt, macht aus „irgendjemand hat es irgendwann bemerkt“ einen roten Build. Das ist der einzige Schritt, der Wiederholungen verhindert.
- Externe Abhängigkeiten wie jede andere budgetieren. Festhalten, von welchen Hosts die eigenen Seiten abhängen dürfen und was passiert, wenn jeder einzelne ausfällt. Eine URL, für die man sich nicht registrieren musste, ist trotzdem eine Abhängigkeit — Source hat bewiesen, dass es einfach eine ist, die niemandem gehört.
Quellen & Fußnoten
1 Jeder Statuscode, jede Zahl und jede zitierte Zeile in diesem
Artikel wurde am 30. August 2026 aus der jeweiligen Primärquelle
entnommen. HTTP-Statuscodes wurden mit curl gegen jedes URL-Muster ermittelt; alle
lieferten 503 mit server: Heroku und einem Body, der
herokucdn.com/error-pages/application-error.html einbettet. Die DNS-Auflösung wurde
am selben Tag bestätigt (ein CNAME auf einen herokudns.com-Host).
2 Beide Changelog-Einträge sind wörtlich zitiert aus
unsplash.com/documentation/changelog. Der Wortlaut der Deprecation-Policy (3 Wochen
Vorlauf, Warning-Header und die Ausnahme für nicht öffentlich dokumentierte
Endpunkte) stammt aus unsplash.com/documentation, vom selben Tag.
3 Der TLS-Fehler wurde reproduziert mit
openssl s_client -connect changelog.unsplash.com:443 (tlsv1 alert internal
error). Die Redirect-Kette wurde mit curl -L verfolgt. Die Archivlücke stammt
aus der Wayback-CDX-API: letzter 200-Snapshot von
changelog.unsplash.com am 24.03.2024, erster von
unsplash.com/documentation/changelog am 23.08.2024.
4 Issue-Titel sowie Erstellungs- und Schließdaten wurden aus der
GitHub-REST-API gelesen (mui/material-ui#42736,
nextcloud/unsplash#115, sindresorhus/Actions#248) sowie aus der
drupal.org-JSON-API für gin_login-Issue 3324054, dessen Text im November 2022 „I get
always a Heroku application error“ berichtet.
5 Zählungen: GitHub-Codesuch-API
(3.344 Dateien; eine Stichprobe von 100 Ergebnissen, dedupliziert auf 77
Repositories, von denen 12 nach dem 11. Juni 2024 erstellt und 20 in den vorangegangenen
12 Monaten mit Pushes versehen worden waren); npm-Registry-Download-API
(unsplash-source-es6, 23 Downloads in den vorangegangenen 30 Tagen);
Stack-Exchange-/search/excerpts (1.459 Beiträge). Die Codesuche erfasst
nur indexierte öffentliche Repositories, jede Zahl ist also eine Untergrenze.
Primärquellen: Unsplash-API-Changelog · Unsplash-API-Dokumentation · Unsplash-Attributionsrichtlinie · Unsplash-Status · MUI #42736 · Nextcloud #115 · sindresorhus/Actions #248 · Drupal Gin Login #3324054 · Lorem Picsum · Openverse · Pexafy-API- & MCP-Dokumentation.
Häufig gestellte Fragen
Ist source.unsplash.com nur down, oder wurde es dauerhaft abgeschaltet?
/random, /1600x900/?query, /collection/…, /daily) liefert HTTP 503 mit Herokus generischer Application Error-Seite. Der Hostname wird weiterhin aufgelöst, weshalb der Fehler als kaputtes Bild statt als Netzwerkfehler in Erscheinung tritt.Was ist der direkte Ersatz für source.unsplash.com/random?
images.unsplash.com/photo-…?w=1600&h=900&fit=crop — benötigt keinen Key, liefert aber immer dasselbe Foto, was für die meisten dekorativen Zwecke ohnehin ausreichte. Die offizielle API, GET https://api.unsplash.com/photos/random mit einem Authorization: Client-ID-Header, stellt die Zufälligkeit wieder her, muss aber serverseitig aufgerufen werden. Ein eigener kleiner Proxy vor diesem Endpoint ist die einzige Option, die eine schlüssellose URL zurückgibt, die man direkt in ein <img>-Tag einsetzen kann.Warum generieren KI-Coding-Tools 2026 immer noch source.unsplash.com-URLs?
Kann ich ohne API-Key noch ein zufälliges Unsplash-Foto bekommen?
/photos/random, was eine Client-ID erfordert. Zwei schlüssellose Wege bleiben: ein selbst gehosteter Proxy, bei dem der Key serverseitig bleibt und die öffentliche URL wie die alte aussieht, oder ein Drittanbieter-Platzhalterdienst wie Lorem Picsum (picsum.photos/800/600), der echte Fotografien ohne Key liefert, aber ganz ohne Motiv-Targeting.Erlaubt die Unsplash-API das Herunterladen und Selbst-Hosten der Bilder?
ixid-Parameter bei, wenn du die URL skalierst oder zuschneidest, nenne Fotograf und Unsplash als Quelle, und rufe den Download-Endpoint des Fotos auf, wenn ein Nutzer die Datei herunterlädt. Die Dateien auf ein eigenes CDN zu spiegeln, ist die eine Optimierung, die man sich nicht erlauben darf.Warum hat die Abkündigung von 2021 bestehende Nutzer nicht geschützt?
Warning-Header — gilt für öffentlich dokumentierte Felder und Endpoints, und derselbe Absatz besagt, dass alles Undokumentierte sich ohne Vorwarnung ändern kann. Source war nie ein dokumentierter Endpoint der API und fiel damit aus der Richtlinie heraus, die es hätte schützen können.Wie finde ich jede tote source.unsplash.com-URL in meinem Projekt?
grep -rn "source.unsplash.com" . — da diese URLs in Beispielcode am längsten überleben. Frag die Datenbank ab, denn in CMS-Artikeltexten verstecken sie sich (WHERE body LIKE '%source.unsplash.com%'). Dann crawle deinen gebauten Output: Extrahiere jede Bild-URL, rufe sie einzeln auf und liste alles auf, was nicht 200 liefert. Füge diesen letzten Schritt der CI hinzu, dann lässt ein totes Drittanbieter-Asset den Build fehlschlagen statt eine Seite kaputtzumachen.