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 zwart-witfoto van een smartphone met een gebarsten scherm op een licht houten oppervlak, met het Unsplash-logo zichtbaar op het beschadigde display.
Foto via Unsplash

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:

terminal
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/randomEen willekeurige foto, elke afmeting503
source.unsplash.com/random/1600x900Een willekeurige foto, bijgesneden op maat503
source.unsplash.com/1600x900/?apple,deskEen willekeurige foto die bij zoektermen past503
source.unsplash.com/featured/1600x900?natureEen willekeurige uitgelichte foto503
source.unsplash.com/collection/190727/800x600Een willekeurige foto uit een collectie503
source.unsplash.com/user/scottwebb/1600x900Een willekeurige foto van één fotograaf503
source.unsplash.com/dailyDe foto van de dag503

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:

  1. Het HTTPS-endpoint is kapot. openssl s_client -connect changelog.unsplash.com:443 geeft tlsv1 alert internal error terug — de handshake mislukt voordat er ook maar een certificaat wordt aangeboden. Elke link uit het 2021-tijdperk, die https:// was, is daarmee dood in een browser.
  2. Over gewone HTTP redirect het wel, maar niet nuttig. Als je http://changelog.unsplash.com/deprecations/2021/11/25/source-deprecation.html volgt, eindig je, twee hops later, op een 400 bij unsplash.com/@documentation/changelog/deprecations/2021/11/25/source-deprecation/html — het pad is opgeslokt door de gebruikersnaam-route van de site.
  3. 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:

MetingWaarde op 30 aug 2026Hoe 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:

de dode-afbeeldingspatronen die het waard zijn om op te grepen
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:

een vaste, herschaalbare Unsplash-URL — geen sleutel, geen API-aanroep
<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.

de officiële vervanger voor /random
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.

worker.js — een sleutelloos, Source-vormig endpoint bovenop /photos/random
// 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:

één-op-één vervanging
- 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.com tellen mee — afbeeldingsverzoeken naar images.unsplash.com niet. Lees X-Ratelimit-Remaining bij 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:

DienstSleutel?Wat je krijgtWaar 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.

vind elke dode verwijzing, en houd ze daarna buiten
# 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:

  1. 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.
  2. Laat een decoratieve asset nooit een kritiek pad blokkeren. Preload niets externs boven een inlogformulier; geef elke externe <img> een onerror-fallback en expliciete width/height, zodat een storing alleen een leeg vak kost, geen layout shift of vastgelopen script.
  3. 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.
  4. 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.

Veelgestelde vragen

Ligt source.unsplash.com plat, of is het permanent uitgeschakeld?
Permanent. Unsplash kondigde de uitfasering aan op 11 juni 2024 — “we will first wind down by disabling the search feature, and in the coming weeks turn off the application entirely” — nadat de dienst al op 25 november 2021 was afgeschaft. Elk patroon (/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?
Er bestaat geen sleutelloze plug-and-play-oplossing, en dat is meteen het punt om te accepteren. Er zijn drie vervangers. Een vaste CDN-URL — 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?
Omdat de URL zo'n acht jaar lang gedocumenteerd, aangeleerd en gekopieerd is, en een trainingscorpus niet verloopt wanneer een dienst dat wel doet. Gemeten op 30 augustus 2026: GitHub code search geeft nog steeds 3.344 bestanden terug die de URL bevatten, en in een steekproef van 100 bestanden was 12 van de 77 repositories aangemaakt na de uitschakeling. Ook door mensen geschreven promptbibliotheken herhalen de instructie — één veelgekopieerde GPT-prompt zegt het model nog steeds om “use unsplash API( https://source.unsplash.com/1280x720/?… )” te gebruiken. Behandel elke afbeeldings-URL die een model schrijft als ongeverifieerd en controleer statuscodes in CI.
Kan ik nog steeds een willekeurige Unsplash-foto krijgen zonder API-sleutel?
Niet rechtstreeks via Unsplash — willekeurige selectie zit nu achter /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?
Nee. In tegenstelling tot de meeste API's vereist Unsplash hotlinking: de afbeeldings-URL's die de API teruggeeft moeten direct worden ingesloten, zodat foto-weergaven worden geteld voor de fotograaf. Er komen drie verplichtingen mee — behoud de 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?
Omdat de melding en het beleid niet over hetzelfde gingen. De changelog-entry van 25 november 2021 beloofde dat “existing uses will continue to work” en gaf geen einddatum. Het gepubliceerde afschaffingsbeleid van Unsplash — minstens drie weken vooraankondiging plus een 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?
Drie stappen, tien minuten. Doorzoek de repository inclusief documentatie, tests, fixtures en READMEs — 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.

Stop met jagen op trefwoorden. Beschrijf wat je bedoelt.

Doorzoek 9M+ vrij te gebruiken afbeeldingen op betekenis — in elke taal, in minder dan 100 ms.