source.unsplash.com is verdwenen: het post-mortem en elke manier om het te vervangen
In 2021 afgeschaft met “bestaand gebruik blijft werken”, in juni 2024 uitgeschakeld, en tot op vandaag nog steeds in nieuwe code geschreven. Het genuanceerde post-mortem — en de drie vervangers, inclusief de proxy die de sleutelloze willekeur terugbrengt.
Een stockfoto-dienst zette een subdomein uit, en twee jaar later breekt het nog steeds documentatiesites, inlogschermen, cursusoefeningen en pas gegenereerde code. Dit is een post-mortem van een URL — wat hij deed, wat hem doodde, en wat je ervoor in de plaats zet — plus een afgewogen blik op het vreemdere deel van het verhaal: de machines die onze code schrijven hebben niet gemerkt dat hij weg is.
De HTTP-responses, de twee changelog-vermeldingen woordelijk geciteerd, de API-limieten, de issue- trackers van de projecten die kapotgingen en de aantallen van GitHub, npm en Stack Overflow komen allemaal uit primaire bronnen, met de methode voor elk ervan in de voetnoten. Als je alleen de oplossing wilt, ga dan naar de migratietabel.
Wat je vandaag krijgt, als je hem nog steeds aanroept
Eén commando, geen sleutel, reproduceerbaar op elke machine:
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: een iframe dat verwijst naar herokucdn.com/error-pages/application-error.html
Niets hiervan is een DNS-fout. source.unsplash.com is nog steeds resolvebaar — het is een CNAME naar
een herokudns.com-host — dus het verzoek wordt beantwoord, alleen niet door een applicatie. Dat
detail is belangrijker dan het klinkt: een browser die snel een 503 met een HTML-body krijgt,
rendert een kapotte-afbeelding-placeholder, en elke code die response.ok of een
onerror-handler leest die je nooit hebt geschreven, gaat het faalpad in dat je nooit hebt getest.
| URL-patroon | Wat het vroeger teruggaf | Vandaag |
|---|---|---|
source.unsplash.com/random | Een willekeurige foto, elke afmeting | 503 |
source.unsplash.com/random/1600x900 | Een willekeurige foto, bijgesneden op maat | 503 |
source.unsplash.com/1600x900/?apple,desk | Een willekeurige foto die bij zoektermen past | 503 |
source.unsplash.com/featured/1600x900?nature | Een willekeurige uitgelichte foto | 503 |
source.unsplash.com/collection/190727/800x600 | Een willekeurige foto uit een collectie | 503 |
source.unsplash.com/user/scottwebb/1600x900 | Een willekeurige foto van één fotograaf | 503 |
source.unsplash.com/daily | De foto van de dag | 503 |
Afzonderlijk gecontroleerd met curl -o /dev/null -w "%{http_code}". De zoekfunctie werd
eerst afgebouwd, zoals aangekondigd; vandaag is de hele applicatie uit, dus het onderscheid bestaat
niet meer.
Drie jaar tussen “verouderd” en “uit”
Beide aankondigingen zijn nog steeds te lezen, op één plek, op unsplash.com/documentation/changelog. Volledig geciteerd, want de bewoording is het hele verhaal:
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.”
Lees ze na elkaar en het faalpatroon is duidelijk. De aankondiging uit 2021 bevatte een belofte (existing uses will continue to work) en geen datum. Een ontwikkelaar die dit in 2021 las, had alle reden om werkende code met rust te laten; een ontwikkelaar die in 2022 instroomde, heeft het nooit gelezen. De aankondiging uit 2024 gaf “de komende weken”, drie jaar later, op een pagina die niemand had gebookmarkt.
Unsplash publiceert wel een deprecatiebeleid, en dat is een redelijk beleid — de documentatie stelt
dat voor publiek gedocumenteerde velden en endpoints, wijzigingen worden aangekondigd op de changelog met minstens
3 weken vooraankondiging, en dat endpoints tijdens de deprecatieperiode een Warning-header
teruggeven. Diezelfde alinea bevat de zin die verklaart waarom niets daarvan Source beschermde: “For any non-publicly documented fields or endpoints, we may make changes
to these with no warning.” Source is nooit een endpoint van de gedocumenteerde API geweest. Het viel buiten
het beleid dat het anders had gedekt.
- De praktische les is niet “Unsplash was onzorgvuldig”. Het is dat een URL die je kunt gebruiken zonder documentatie te lezen, ook een URL is waarvan je het deprecatiebeleid niet hebt gelezen.
- De storingen gingen aan de aankondiging vooraf. Een Drupal-issue ingediend op 28 november 2022 meldt al “I get always a Heroku application error”, achttien maanden vóór de sunset-vermelding. Zo sterven deze diensten: langzaam, en dan in een aankondiging die je nooit ziet.
De aankondiging is moeilijker te vinden dan de storing
De deprecatie uit 2021 werd gepubliceerd op changelog.unsplash.com, en dat is de URL
waarnaar elk bugrapport uit die tijd verwijst — inclusief die van Drupal hierboven. Drie
metingen:
- Het HTTPS-endpoint is kapot.
openssl s_client -connect changelog.unsplash.com:443geefttlsv1 alert internal errorterug — de handshake mislukt voordat er ook maar een certificaat wordt aangeboden. Elke link uit het 2021-tijdperk, diehttps://was, is daarmee dood in een browser. - Over gewone HTTP redirect het wel, maar niet nuttig. Als je
http://changelog.unsplash.com/deprecations/2021/11/25/source-deprecation.htmlvolgt, eindig je, twee hops later, op een400bijunsplash.com/@documentation/changelog/deprecations/2021/11/25/source-deprecation/html— het pad is opgeslokt door de gebruikersnaam-route van de site. - Het archief heeft precies op de sunset een gat. De laatste succesvolle capture van de oude changelog in de Wayback Machine is 24 maart 2024; de eerste capture van de nieuwe is 23 augustus 2024. De sunset werd aangekondigd op 11 juni 2024 — binnen dat gat van vijf maanden.
Niets hiervan is een samenzwering; het is een gewone CMS-migratie. Maar het gevolg is reëel, en het is de reden waarom dit artikel beide vermeldingen volledig citeert: het primaire verslag van een deprecatie zou langer moeten meegaan dan het ding dat het afschaft, en hier was dat bijna niet zo.
Wat er daadwerkelijk kapotging
Geen zijprojecten. De storingen hieronder zijn publieke vermeldingen uit issue-trackers; de titels, datums en statussen komen uit de API's van GitHub en drupal.org.
| Project | Issue | Geopend | Wat er staat |
|---|---|---|---|
| MUI (Material UI) | #42736 | 24 jun 2024 | “[docs] Random Unsplash photo URL is no longer functional” — het officiële Sign-in side-template leverde een dode afbeelding af. Drie dagen later gesloten. |
| Nextcloud | #115 | 17 jan 2023 | “Migrate to Unsplash API” — de achtergrond-app was gebouwd op Source-URI's. Achttien maanden open, gesloten op 16 juli 2024. |
| sindresorhus/Actions | #248 | 28 mei 2024 | “Get Unsplash Image: 503 Error” — een iOS/macOS Shortcuts-actie, kapot twee weken vóór de sunset werd aangekondigd. |
| Drupal — Gin Login | #3324054 | 28 nov 2022 | “Unsplash has deprecated source.unsplash.com — this delays reCAPTCHA from loading, preventing users from logging in.” |
Lees die laatste rij nog eens, want die verdient het om je eigen te maken. Een decoratieve afbeelding naast een inlogformulier — het meest overduidelijk niet-kritieke element op de pagina — ontaardde in een authenticatiestoring, omdat een traag verzoek naar een derde partij vóór de CAPTCHA stond die het inlogformulier nodig had. Niemand heeft het zo ontworpen. Het ontstond uit de volgorde waarin een browser dingen laadt.
Je codeerassistent heeft het memo niet gekregen
Hier zit het deel dat een storing uit 2024 tot een probleem in 2026 maakt. source.unsplash.com
is ongeveer acht jaar lang gedocumenteerd, geblogd, onderwezen en gekopieerd. Al die tekst zit in de
trainingsdata van de modellen die nu onze startercode schrijven — en tekst verloopt niet. Drie
tellingen:
| Meting | Waarde op 30 aug 2026 | Hoe gemeten |
|---|---|---|
Bestanden met source.unsplash.com |
3.344 | GitHub code search API, q=source.unsplash.com (alleen geïndexeerde publieke code — een ondergrens, geen totaal) |
| Repositories in een steekproef van 100 bestanden, aangemaakt na de sunset | 12 van 77 | Dezelfde query, 100 resultaten, ontdubbeld tot 77 repositories, created_at vergeleken met 11 jun 2024 |
| …en repositories in die steekproef waarnaar in de afgelopen 12 maanden is gepusht | 20 van 77 | Actieve repositories, geen archieven — waaronder elastic/kibana, waarvan het demobestand nog steeds imageUrl: 'https://source.unsplash.com/64x64/?dingo' bevat |
Maandelijkse downloads van unsplash-source-es6 |
23 | npm-registry-API — een wrapper voor een dode dienst, voor het laatst gepubliceerd in 2022, nog steeds geïnstalleerd |
| Stack Overflow-posts die het vermelden | 1.459 | Stack Exchange API, totaal van /search/excerpts |
Het meest directe bewijs zit niet eens in applicatiecode — het zit in prompts. Het topresultaat voor die zoekopdracht is een bibliotheek van GPT-systeemprompts met de regel “please use unsplash API( https://source.unsplash.com/1280x720/?<PUT YOUR QUERY HERE>”. Die instructie wordt vandaag nog steeds gekopieerd naar nieuwe assistenten. Het model controleert de URL niet; het kreeg de opdracht om hem te gebruiken, en elk voorbeeld dat het ooit zag, was het daarmee eens.
De storing in gegenereerde code heeft dus twee onafhankelijke oorzaken, en het oplossen van de ene lost de andere niet op: verouderde trainingsdata, en verouderde instructies die mensen erbovenop hebben geschreven. In beide gevallen is het symptoom dezelfde familie van afbeeldingen die nooit laden:
source.unsplash.com/random/1200x800 # 503 sinds medio 2024 — komt nooit meer terug
images.unsplash.com/photo-… # echte CDN, maar uit het geheugen onthouden ID's bestaan mogelijk niet
via.placeholder.com/400 # grijze rechthoek, naar productie gestuurd
placehold.co/800x600 # grijze rechthoek, met opzet
picsum.photos/800/600 # een echte foto, zonder verband met je pagina
/placeholder.png # een bestand dat nooit aan de repo is toegevoegd
Een voetnoot bij de tweede regel van die lijst: tijdens het schrijven hiervan wilde
via.placeholder.com ook vanaf ons testnetwerk geen TLS-handshake voltooien, en antwoordde 403
over gewone HTTP. Controleer het vanaf je eigen netwerk voordat je erop vertrouwt — de fallback die deze tools
gebruiken, kan zijn eigen storingsverhaal hebben.
Alleen het eerste voorbeeld is kapot. De andere zijn op een subtielere manier erger: ze laden, de layout ziet er afgewerkt uit, en niemand merkt dat de pagina is geïllustreerd met iets willekeurigs. En niets hiervan is specifiek voor afbeeldingen — het is de algemene vorm van het probleem. Het beeld dat een model van het web heeft, is een momentopname, en endpoints, CLI-vlaggen, pakketnamen en gratis abonnementen blijven veranderen nadat de sluiter is gesloten.
De migratietabel
Er zijn precies drie bestemmingen, en de eerlijke manier om ze te presenteren is aan de hand van wat je opgeeft. Kies eerst de kolom, lees dan je rij.
| Oude Source-URL | A. Vaste CDN-URLgeen sleutel · geen willekeur | B. Unsplash APIsleutel · server-side aanroep | C. Je eigen proxysleutel verborgen · willekeur terug |
|---|---|---|---|
/random |
images.unsplash.com/photo-… — één foto die jij hebt gekozen |
GET /photos/random |
/?w=1600 |
/random/1600x900 |
…?w=1600&h=900&fit=crop |
/photos/random + Imgix-parameters op de teruggegeven URL |
/?w=1600&h=900&fit=crop |
/1600x900/?apple,desk |
Geen equivalent — kies zelf een foto | /photos/random?query=apple,desk |
/?query=apple,desk&w=1600 |
/featured/1600x900?nature |
Geen equivalent | /photos/random?query=nature “featured” heeft geen opvolger |
/?query=nature&w=1600 |
/collection/67920491/1600x900 |
Geen equivalent | /photos/random?collections=67920491 |
/?collections=67920491&w=1600 |
/user/scottwebb/1600x900 |
Geen equivalent | /photos/random?username=scottwebb |
/?username=scottwebb&w=1600 |
/daily |
Pin één foto, roteer deze in je build | Geen equivalent — cache zelf één willekeurige foto voor 24 u | Hetzelfde, met de cache in de proxy |
Optie A is degene die de meeste mensen eigenlijk willen. Als de afbeelding decoratief was — een hero, een zijpaneel bij het inloggen, een kaartachtergrond — had je nooit een andere foto per verzoek nodig. Kies er één, bewaar de CDN-URL, en de pagina hangt niet meer af van iets willekeurigs:
<img src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1600&h=900&fit=crop&auto=format"
width="1600" height="900" alt="…">
# Officieel ondersteunde parameters: w, h, crop, fit, fm, auto=format, q, dpr.
# Bewaar elke ixid-parameter die de API je gaf — die rapporteert de weergave.
Optie B is het officiële pad, en verplaatst de aanroep naar de server, omdat een
Client-ID in front-end JavaScript een gepubliceerde credential is. Let op de twee regels waar mensen
over struikelen: collections/topics kunnen niet gecombineerd worden met
query in hetzelfde verzoek, en count (max 30) verandert de vorm van de respons
naar een array, zelfs als het 1 is.
curl "https://api.unsplash.com/photos/random?query=nature&orientation=landscape" \
-H "Authorization: Client-ID YOUR_ACCESS_KEY" \
-H "Accept-Version: v1"
# → JSON. De afbeelding staat op .urls.regular / .urls.raw (voeg zelf w/h/fit toe).
# → X-Ratelimit-Limit: 1000 X-Ratelimit-Remaining: 999
Optie C: bouw Source opnieuw, in ongeveer veertig regels
Als wat je kwijtraakte echt het gedrag was — een sleutelloze URL die elke keer een andere foto teruggeeft, direct bruikbaar vanuit een
<img>-tag, in een CMS-veld, of op een statische site zonder server — dan moet je
dat endpoint zelf draaien. Het is één kleine worker vóór de API, en de drie dingen die hem laten
overleven zodra hij productie raakt, zijn de cache, de referrer-check, en het doorgeven van de rest van de queryparameters aan de CDN.
// Cloudflare Workers. Elders (Deno Deploy, Val Town…) is de vorm hetzelfde,
// maar open een benoemde cache met caches.open() in plaats van caches.default.
// UNSPLASH_KEY blijft server-side. Aanroepers zien hem nooit.
const ALLOWED = ["example.com", "www.example.com"]; // alleen jouw domeinen
const API_PARAMS = ["query", "collections", "topics", "username", "orientation"];
const TTL = 60; // seconden — beschermt het uurquotum
const host = (value) => { try { return new URL(value).hostname; } catch { return null; } };
export default {
async fetch(req, env, ctx) {
// 0. Alleen GET: de Cache API weigert iets anders op te slaan, en een afbeeldings-
// endpoint heeft geen ander werkwoord om te beantwoorden.
if (req.method !== "GET")
return new Response("Method not allowed", { status: 405 });
const url = new URL(req.url);
// 1. Alleen je eigen pagina's mogen het inbedden — een publiek willekeurige-foto-endpoint
// op het open internet is andermans rate limit om op te branden.
const ref = req.headers.get("referer"); // ontbreekt bij tal van legitieme clients
if (ref && !ALLOWED.includes(host(ref)))
return new Response("Forbidden", { status: 403 });
// 2. Cache per parametercombinatie, zodat een pagina met 12 afbeeldingen
// één API-aanroep per minuut kost in plaats van twaalf per weergave.
const cache = caches.default;
const hit = await cache.match(req);
if (hit) return hit;
// 3. Vraag de officiële API om een willekeurige foto.
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 betekent meestal het uurquotum, niet een foute sleutel — verhoog TTL, geen paniek.
if (!r.ok) return new Response("Upstream " + r.status, { status: 502 });
const photo = await r.json();
// 4. Herbouw de afbeeldings-URL: behoud ixid, voeg de sizing-parameters van de aanroeper toe.
const img = new URL(photo.urls.raw); // .raw bevat al 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,
// De credit reist mee met de redirect; headerwaarden moeten ASCII zijn, vandaar de encodering.
"X-Photo-Credit": encodeURIComponent(photo.user.name + " on Unsplash"),
"X-Photo-Link": photo.links.html,
}});
ctx.waitUntil(cache.put(req, res.clone()));
return res;
},
};
Het migreren van een URL is dan een zoek-en-vervang, en precies dat maakt deze optie de twintig minuten waard:
- https://source.unsplash.com/collection/67920491/1600x900
+ https://img.example.com/?collections=67920491&w=1600&h=900&fit=crop
Twee ontwerpnotities, die allebei iemand een vervelende middag kostten voordat ze werden vastgelegd. De
redirect (302) in plaats van het doorproxyen van de bytes, houdt jou buiten schot voor bandbreedte
en zorgt dat de weergave meetelt op Unsplash's CDN, wat de richtlijnen vragen. En
de Referer-check is bewust soepel als de header ontbreekt — veel
legitieme clients strippen die — terwijl het toch het voor de hand liggende geval tegenhoudt waarin jouw endpoint
andermans gratis afbeeldings-API wordt.
De regels die je erft zodra je de API gebruikt
Source had geen regels omdat het geen account had. De API heeft er vijf die de manier waarop je het bouwt veranderen, allemaal uit de huidige documentatie:
- Rate limits gelden per uur, en zijn eerst klein. 50 verzoeken/uur
in demomodus; 1.000/uur nadat je applicatie is goedgekeurd voor productie. Alleen
aanroepen naar
api.unsplash.comtellen mee — afbeeldingsverzoeken naarimages.unsplash.comniet. LeesX-Ratelimit-Remainingbij elke respons. - Hotlinking is verplicht, niet slechts toegestaan. Unsplash vereist dat de afbeeldings- URL's die de API teruggeeft, rechtstreeks worden ingebed, zodat foto-weergaven kunnen worden toegeschreven aan de fotograaf. Het bestand naar je eigen CDN spiegelen is de ene optimalisatie die je niet vrij mag toepassen.
- Bewaar de
ixid-parameter. Het herschalen en bijsnijden van de teruggegeven URL wordt verwacht; het strippen van de parameter die je applicatie identificeert, niet. - Attributie en downloadtracking horen bij de deal — de fotograaf en Unsplash krijgen credit, en een “download” wordt gerapporteerd via het download-endpoint van de foto wanneer een gebruiker het bestand overneemt, wat een gebeurtenis is die je zelf moet triggeren.
- Gedistribueerde producten hebben dynamische clientregistratie nodig. Als je een plugin, een thema of een zelfgehost CMS uitlevert, is één gedeelde sleutel zowel een beleidsschending als een single point of failure; de API heeft precies voor dat geval een registratieflow.
Dit is het moment om eerlijk te zijn over de scope: als je toch al een API en een sleutel aan het inrichten bent, staat de keuze welke afbeeldings-API het wordt ineens weer open, en het is de moeite waard om er vijf minuten aan te besteden voordat je de client schrijft. We vergeleken de gratis opties — quota, regels, zoekgedrag en responsvormen — in de vergelijking van gratis stockfoto-API's.
Als je alleen een placeholder wilde, zeg dat dan ook
Een groot deel van het Source-gebruik ging nooit echt over Unsplash. Het was “zet hier iets afbeelding-achtigs terwijl ik de layout bouw”. Daarvoor bestaan er nog steeds sleutelloze diensten, en dat is het juiste antwoord:
| Dienst | Sleutel? | Wat je krijgt | Waar het ophoudt |
|---|---|---|---|
| Lorem Picsum | Nee | Echte foto's: picsum.photos/800/600, een stabiele met /id/237/… of /seed/xxx/…, plus ?grayscale en ?blur=1..10. Het /v2/list-endpoint vermeldt de Unsplash-pagina en auteur van elke foto. |
Geen enkele onderwerp-targeting. De foto zal geen verband houden met je pagina. |
| placehold.co | Nee | Gelabelde rechthoeken in elke maat — eerlijke wireframe-vulling. | Het is een grijs vlak, en ziet er ook als zodanig uit op een screenshot die met een klant wordt gedeeld. |
| Openverse | Nee | Een openlijk gelicentieerde catalogus met een publieke API, beheerd door WordPress.org. | Trefwoordmatching, en licenties verschillen per item — je moet ze zelf lezen. |
Het onderscheid dat ertoe doet: een placeholder is per definitie tijdelijk. Als de afbeelding de weg naar productie overleeft, is het geen placeholder meer — het is een illustratie die niemand heeft gekozen, en de lezer merkt dat.
Eén sleutel in plaats van drie
Hier zit het deel van de migratie waar niemand op rekent: mensen die Source verlaten, komen zelden bij
één API terecht. Een pagina heeft een hero nodig, twee sectie-afbeeldingen en iets voor een kaartraster, en
het eerlijke antwoord is meestal Unsplash plus Pexels plus Pixabay — drie
registraties, drie authenticatieschema's, drie JSON-vormen, drie paginatiemodellen en drie sets attributieregels,
allemaal om dezelfde <img>-tags te vullen. Dat integratiewerk is
de echte rekening voor het verdwijnen van een sleutelloze URL, en die komt weken na de storing die het veroorzaakte.
Dat samenvoegen tot één integratie is waarvoor we Pexafy hebben gebouwd: één sleutel voor 9 gratis-licentie-bibliotheken in één schema, met semantisch zoeken op zinsniveau — zodat een volledige omschrijving als “a cracked phone screen on a wooden desk, shot from above” gerangschikte resultaten oplevert in plaats van niets. Twee beperkingen, duidelijk vermeld, want dit hele artikel gaat erover niet twee keer verrast te worden: het vereist een sleutel, dus het herstelt niet wat Source was — het valt in dezelfde categorie als de officiële Unsplash API hierboven; en het draait om gratis-licentie-fotografie, geen redactionele of merkbeelden.
Het onderdeel dat echt nieuw is, richt zich op de assistenten hierboven: een MCP-
server op mcp.pexafy.com/mcp betekent dat een model dat anders uit het geheugen een dode
afbeelding-URL zou opdreunen, een echte catalogus kan doorzoeken en een foto teruggeven die bestaat, met credit erbij.
Dat is een beter antwoord op door machines geschreven dode URL's dan welke lint-regel dan ook, en de
redenering staat uitgewerkt in
image search
infrastructure for AI agents.
Doorzoek je eigen bestand in tien minuten
Waar je ook naartoe migreert, doe dit deel eerst — je kunt geen URL's repareren die je niet hebt gevonden. Source is gewoon het voorbeeld van vandaag; dezelfde drie stappen gelden voor elke externe asset die je inbedt.
# 1. Alles in de repo, inclusief docs, tests, fixtures en READMEs.
grep -rn --binary-files=without-match \
-e "source.unsplash.com" -e "via.placeholder.com" -e "/placeholder.png" .
# 2. Alles wat de database bevat — CMS-teksten zijn waar deze het langst verstopt blijven.
psql -c "SELECT id FROM posts WHERE body LIKE '%source.unsplash.com%'"
# 3. Alles wat de gebouwde site daadwerkelijk opvraagt: crawl hem en lijst de mislukkingen op.
# Match het attribuut, niet een bestandsextensie — afbeeldings-URL's eindigen zelden op .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 ← waar je naar op zoek bent
Beslis dan, één keer, hoeveel externe afbeeldingen je mogen kosten. Vier regels die de volgende sluiting overleven, wie hem ook veroorzaakt:
- Haal op bij het bouwen, niet bij het opvragen. Een afbeelding die tijdens de build wordt gecontroleerd, faalt in CI, voor de ogen van een ontwikkelaar, in plaats van om 3 uur 's nachts voor de ogen van een gebruiker.
- Laat een decoratieve asset nooit een kritiek pad blokkeren. Preload niets externs
boven een inlogformulier; geef elke externe
<img>eenonerror-fallback en explicietewidth/height, zodat een storing alleen een leeg vak kost, geen layout shift of vastgelopen script. - Voeg de controle toe aan CI. Stap 3 hierboven, uitgevoerd op je gebouwde output, maakt van “iemand heeft het uiteindelijk gemerkt” een rode build. Het is de enige stap die herhaling voorkomt.
- Budgetteer je externe afhankelijkheden zoals elke andere. Leg vast van welke hosts je pagina's mogen afhangen en wat er gebeurt als elk daarvan uitvalt. Een URL waarvoor je je niet hoefde te registreren, is nog steeds een afhankelijkheid — Source bewees alleen dat het er een is die niemand bezit.
Referenties & voetnoten
1 Elke statuscode, telling en geciteerde regel in dit artikel is
op 30 augustus 2026 uit de primaire bron gehaald. HTTP-statuscodes
zijn genomen met curl tegen elk URL-patroon; allemaal gaven 503 terug met
server: Heroku en een body met daarin
herokucdn.com/error-pages/application-error.html. DNS-resolutie is diezelfde
dag bevestigd (een CNAME naar een herokudns.com-host).
2 Beide changelog-vermeldingen zijn woordelijk geciteerd uit
unsplash.com/documentation/changelog. De bewoording van het deprecatiebeleid (3 weken vooraankondiging, Warning-header, en de uitzondering voor
niet-publiek gedocumenteerde endpoints) komt van unsplash.com/documentation, dezelfde dag.
3 TLS-fout gereproduceerd met
openssl s_client -connect changelog.unsplash.com:443 (tlsv1 alert internal
error). Redirect-keten gevolgd met curl -L. Archiefgat genomen uit de
Wayback CDX API: laatste 200-capture van changelog.unsplash.com op
20240324, eerste van unsplash.com/documentation/changelog op 20240823.
4 Titels, aanmaak- en sluitdatums van issues gelezen uit de GitHub REST-
API (mui/material-ui#42736, nextcloud/unsplash#115,
sindresorhus/Actions#248) en uit de drupal.org JSON-API voor
gin_login-issue 3324054, waarvan de body in november 2022 meldt “I get always a Heroku application
error”.
5 Tellingen: GitHub code search API
(3.344 bestanden; een steekproef van 100 resultaten ontdubbeld tot 77 repositories, waarvan er 12
zijn aangemaakt na 11 juni 2024 en 20 in de voorafgaande 12 maanden een push kregen);
npm-registry-downloads-API (unsplash-source-es6, 23 downloads in de voorafgaande
30 dagen); Stack Exchange /search/excerpts (1.459 posts). Code search
dekt alleen geïndexeerde publieke repositories, dus elk cijfer is een ondergrens.
Primaire bronnen: Unsplash API changelog · Unsplash API documentatie · Unsplash attributierichtlijn · Unsplash status · MUI #42736 · Nextcloud #115 · sindresorhus/Actions #248 · Drupal Gin Login #3324054 · Lorem Picsum · Openverse · Pexafy API & MCP docs.
Veelgestelde vragen
Ligt source.unsplash.com plat, of is het permanent uitgeschakeld?
/random, /1600x900/?query, /collection/…, /daily) geeft nu HTTP 503 terug met Heroku's generieke Application Error-pagina. De hostnaam is nog wel bereikbaar, waardoor het probleem zich uit als een gebroken afbeelding in plaats van een netwerkfout.Wat is de directe vervanger voor source.unsplash.com/random?
images.unsplash.com/photo-…?w=1600&h=900&fit=crop — vereist geen sleutel maar geeft altijd dezelfde foto terug, wat voor de meeste decoratieve toepassingen eigenlijk al voldoende was. De officiële API, GET https://api.unsplash.com/photos/random met een Authorization: Client-ID-header, herstelt de willekeur maar moet server-side worden aangeroepen. Een eigen kleine proxy vóór dat endpoint is de enige optie die je een sleutelloze URL teruggeeft die je rechtstreeks in een <img>-tag kunt plaatsen.Waarom genereren AI-codetools in 2026 nog steeds source.unsplash.com URL's?
Kan ik nog steeds een willekeurige Unsplash-foto krijgen zonder API-sleutel?
/photos/random, wat een Client-ID vereist. Je hebt twee sleutelloze routes: een proxy die je zelf host, waarbij de sleutel server-side blijft en de publieke URL lijkt op de oude, of een externe placeholder-dienst zoals Lorem Picsum (picsum.photos/800/600), die echte foto's serveert zonder sleutel maar ook zonder enige onderwerp-gerichtheid.Staat de Unsplash API toe om afbeeldingen te downloaden en zelf te hosten?
ixid-parameter wanneer je de URL verkleint of bijsnijdt, geef credit aan de fotograaf en Unsplash, en roep het download-endpoint van de foto aan wanneer een gebruiker het bestand opslaat. De bestanden naar je eigen CDN spiegelen is de ene optimalisatie die je niet vrij mag toepassen.Waarom beschermde de afschaffingsmelding van 2021 bestaande gebruikers niet?
Warning-header — geldt voor publiekelijk gedocumenteerde velden en endpoints, en diezelfde paragraaf stelt dat alles wat niet gedocumenteerd is zonder waarschuwing kan veranderen. Source was nooit een gedocumenteerd endpoint van de API, en viel dus buiten het beleid dat het had kunnen beschermen.Hoe vind ik elke dode source.unsplash.com URL in mijn project?
grep -rn "source.unsplash.com" . — want deze URL's overleven het langst in voorbeeldcode. Bevraag de database, want CMS-artikelinhoud is waar ze zich vaak verstoppen (WHERE body LIKE '%source.unsplash.com%'). Crawl vervolgens je gebouwde output: haal elke afbeeldings-URL eruit en verzoek elk ervan op, met een lijst van alles wat geen 200 teruggeeft. Voeg die laatste stap toe aan CI, zodat een dode externe asset een build laat falen in plaats van een pagina.