source.unsplash.com หายไปแล้ว: การชันสูตรและทุกวิธีทดแทน

ถูกเลิกใช้ (deprecated) ในปี 2021 พร้อมคำมั่นว่า “การใช้งานเดิมจะยังทำงานต่อไป” ก่อนถูกปิดจริงในเดือนมิถุนายน 2024 และจนถึงวันนี้ก็ยังถูกเขียนลงในโค้ดใหม่อยู่เรื่อย ๆ นี่คือการชันสูตรอย่างรอบด้าน พร้อมทางเลือกทดแทนสามแบบ รวมถึงพร็อกซีที่คืนความสุ่มแบบไม่ต้องใช้คีย์ให้กลับมา

แชร์
ภาพถ่ายขาวดำของสมาร์ทโฟนจอแตกวางอยู่บนพื้นผิวไม้สีอ่อน โดยหน้าจอที่เสียหายแสดงโลโก้ Unsplash
ภาพจาก Unsplash

บริการภาพสต็อกแห่งหนึ่งปิด subdomain ตัวหนึ่งไป และสองปีให้หลังมันก็ยังคงทำให้เว็บไซต์เอกสาร หน้าล็อกอิน แบบฝึกหัดในคอร์สเรียน และโค้ดที่เพิ่งถูกสร้างขึ้นใหม่พังอยู่เรื่อย ๆ นี่คือการชันสูตร URL ตัวหนึ่ง — มันทำอะไรได้บ้าง อะไรที่ฆ่ามัน และควรใช้อะไรแทน — พร้อมกับการมองส่วนที่แปลกกว่านั้นของเรื่องนี้อย่างละเอียด: เครื่องจักรที่เขียนโค้ดให้เรายังไม่ทันสังเกตว่ามันหายไปแล้ว

การตอบกลับ HTTP, ข้อความ changelog ทั้งสองรายการที่ถูกยกมาคำต่อคำ, ขีดจำกัดของ API, issue tracker ของโปรเจกต์ที่พังไป และตัวเลขจาก GitHub, npm และ Stack Overflow ทั้งหมดนี้มาจากแหล่งข้อมูลปฐมภูมิ พร้อมวิธีการตรวจสอบแต่ละรายการอยู่ใน เชิงอรรถ ถ้าคุณต้องการแค่วิธีแก้ ให้ข้ามไปที่ ตารางการย้ายระบบ

สิ่งที่คุณจะได้รับในวันนี้ ถ้ายังคงเรียกใช้อยู่

คำสั่งเดียว ไม่ต้องใช้คีย์ ทำซ้ำได้จากเครื่องไหนก็ได้:

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: iframe ที่ชี้ไปยัง herokucdn.com/error-pages/application-error.html

ไม่มีอะไรตรงนี้ที่เป็น DNS failure source.unsplash.com ยัง resolve ได้ — มันเป็น CNAME ไปยังโฮสต์ herokudns.com — ดังนั้นคำขอจึงได้รับการตอบกลับ เพียงแต่ไม่ใช่จากแอปพลิเคชัน รายละเอียด นี้สำคัญกว่าที่ฟังดู: เบราว์เซอร์ที่ได้รับ 503 อย่างรวดเร็วพร้อม HTML body จะ render placeholder ของภาพที่เสีย และโค้ดใด ๆ ที่อ่าน response.ok หรือ handler onerror ที่คุณไม่เคยเขียนขึ้นมาก็จะเดินเข้าเส้นทาง failure ที่คุณไม่เคยทดสอบ

รูปแบบ URL เดิมคืนค่าอะไร วันนี้
source.unsplash.com/randomภาพสุ่ม ขนาดใดก็ได้503
source.unsplash.com/random/1600x900ภาพสุ่ม ครอปตามขนาด503
source.unsplash.com/1600x900/?apple,deskภาพสุ่มที่ตรงกับคำค้นหา503
source.unsplash.com/featured/1600x900?natureภาพสุ่มที่เป็น featured503
source.unsplash.com/collection/190727/800x600ภาพสุ่มจาก collection503
source.unsplash.com/user/scottwebb/1600x900ภาพสุ่มจากช่างภาพคนเดียว503
source.unsplash.com/dailyภาพประจำวัน503

ตรวจสอบทีละรายการด้วย curl -o /dev/null -w "%{http_code}" ฟีเจอร์ค้นหาถูกปิดตัวก่อน ตามที่ประกาศไว้; วันนี้แอปพลิเคชันทั้งหมดปิดตัวลงแล้ว ความแตกต่างนี้จึงไม่มีความหมายอีกต่อไป

สามปีระหว่าง "deprecated" กับ "ปิดตัว"

ประกาศทั้งสองรายการยังคงอ่านได้ อยู่ในที่เดียวกัน ที่ unsplash.com/documentation/changelog ยกมาแบบเต็ม ๆ เพราะถ้อยคำคือเนื้อเรื่องทั้งหมด:

25 พฤศจิกายน 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 มิถุนายน 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.”

อ่านตามลำดับแล้วรูปแบบความล้มเหลวก็ชัดเจน ประกาศปี 2021 มีคำสัญญา (existing uses will continue to work) แต่ไม่มีวันที่ระบุ นักพัฒนาที่อ่านมันในปี 2021 มีเหตุผลทุกอย่างที่จะปล่อยโค้ดที่ใช้งานได้ไว้เฉย ๆ; นักพัฒนาที่เข้าร่วมทีมในปี 2022 ไม่เคยอ่านมันเลยด้วยซ้ำ ประกาศปี 2024 ให้เวลา “the coming weeks” สามปีให้หลัง บนหน้าที่ไม่มีใคร bookmark ไว้

Unsplash เผยแพร่นโยบายการเลิกใช้งาน (deprecation policy) จริง ๆ และมันก็เป็นนโยบายที่สมเหตุสมผล — เอกสารระบุว่าสำหรับ field และ endpoint ที่มีเอกสารเผยแพร่สู่สาธารณะ การเปลี่ยนแปลงจะถูกประกาศบน changelog ล่วงหน้าอย่างน้อย 3 สัปดาห์ และ endpoint จะคืนค่า header Warning ในระหว่างช่วง deprecation ย่อหน้าเดียวกันนี้เองมีประโยคที่อธิบายว่าทำไมไม่มีข้อไหนที่ ปกป้อง Source ไว้เลย: “For any non-publicly documented fields or endpoints, we may make changes to these with no warning.” Source ไม่เคยเป็น endpoint ของ API ที่มีเอกสารรองรับ มันอยู่นอกเหนือ นโยบายที่ควรจะคุ้มครองมัน

  • บทเรียนในทางปฏิบัติไม่ใช่ "Unsplash ประมาท" แต่คือ URL ที่คุณใช้ได้โดยไม่ต้อง อ่านเอกสารใด ๆ เลย ก็คือ URL ที่คุณไม่เคยอ่านนโยบายการเลิกใช้งานของมันเช่นกัน
  • ระบบพังก่อนที่จะมีการประกาศเสียอีก issue ของ Drupal ที่ถูกยื่นเมื่อวันที่ 28 พฤศจิกายน 2022 รายงานไว้แล้วว่า “I get always a Heroku application error” ล่วงหน้าสิบแปดเดือนก่อนรายการ sunset ความล้มเหลวแบบเป็นพัก ๆ คือวิธีที่บริการเหล่านี้ตายลง: ค่อย ๆ ตายไป แล้วจึงมีการประกาศที่คุณไม่มีวันได้เห็น

ประกาศนั้นหาเจอได้ยากกว่าตัวปัญหาที่ระบบล่มเสียอีก

การ deprecate ในปี 2021 ถูกเผยแพร่บน changelog.unsplash.com และนั่นคือ URL ที่ bug report ทุกฉบับในยุคนั้นลิงก์ไปหา — รวมถึงของ Drupal ข้างต้นด้วย การวัดผลสามอย่าง:

  1. Endpoint HTTPS พังไปแล้ว openssl s_client -connect changelog.unsplash.com:443 คืนค่า tlsv1 alert internal error — handshake ล้มเหลวก่อนที่ certificate ใด ๆ จะถูกนำเสนอด้วยซ้ำ ลิงก์ในยุคปี 2021 ทุกอันที่เป็น https:// จึงตายในเบราว์เซอร์ทั้งหมด
  2. ผ่าน HTTP ธรรมดามันจะ redirect แต่ก็ไม่เป็นประโยชน์ การตาม http://changelog.unsplash.com/deprecations/2021/11/25/source-deprecation.html จะจบลง หลังผ่านไปสอง hop ที่ 400 บน unsplash.com/@documentation/changelog/deprecations/2021/11/25/source-deprecation/html — path ถูกกลืนไปโดย route username ของเว็บไซต์
  3. archive มีช่องว่างตรงที่ sunset เกิดขึ้น การเก็บภาพสำเร็จครั้งสุดท้ายของ Wayback Machine สำหรับ changelog เก่าคือวันที่ 24 มีนาคม 2024; การเก็บภาพครั้งแรก ของหน้าใหม่คือวันที่ 23 สิงหาคม 2024 การประกาศ sunset เกิดขึ้นเมื่อวันที่ 11 มิถุนายน 2024 — อยู่ในช่องว่างห้าเดือนนั้นพอดี

ทั้งหมดนี้ไม่ใช่ทฤษฎีสมคบคิด มันคือการย้าย CMS แบบธรรมดา แต่ผลลัพธ์นั้นเป็นเรื่องจริง และนี่คือเหตุผลที่ บทความนี้ยกข้อความทั้งสองรายการมาแบบเต็ม ๆ: บันทึกปฐมภูมิของการ deprecate ควรมีอายุยืนยาวกว่าสิ่งที่มัน ประกาศเลิกใช้ และในกรณีนี้มันเกือบจะไม่เป็นอย่างนั้น

สิ่งที่พังไปจริง ๆ

ไม่ใช่แค่โปรเจกต์เล็ก ๆ ความล้มเหลวด้านล่างนี้เป็นรายการใน issue tracker สาธารณะ; ชื่อเรื่อง วันที่ และสถานะมาจาก API ของ GitHub และ drupal.org

โปรเจกต์ Issue เปิดเมื่อ เนื้อหา
MUI (Material UI) #42736 24 มิ.ย. 2024 “[docs] Random Unsplash photo URL is no longer functional” — template อย่างเป็นทางการ Sign-in side ปล่อยภาพที่ตายไปแล้วออกมาให้ใช้งาน ปิดสามวันให้หลัง
Nextcloud #115 17 ม.ค. 2023 “Migrate to Unsplash API” — แอปพื้นหลังถูกสร้างขึ้นบน Source URI เปิดค้างไว้สิบแปดเดือน ปิดเมื่อวันที่ 16 กรกฎาคม 2024
sindresorhus/Actions #248 28 พ.ค. 2024 “Get Unsplash Image: 503 Error” — Shortcuts action สำหรับ iOS/macOS พังไปแล้ว สองสัปดาห์ก่อน ที่จะมีการประกาศ sunset
Drupal — Gin Login #3324054 28 พ.ย. 2022 “Unsplash has deprecated source.unsplash.com — this delays reCAPTCHA from loading, preventing users from logging in.”

อ่านแถวสุดท้ายอีกครั้ง เพราะมันคือแถวที่ควรจดจำไว้ ภาพตกแต่งข้าง ๆ ฟอร์มล็อกอิน — asset ที่ไม่สำคัญที่สุดในหน้าอย่างชัดเจน — กลับกลายเป็นระบบยืนยันตัวตนที่ล่ม เพราะคำขอจาก third-party ที่ช้าไปติดขวางหน้า CAPTCHA ที่ฟอร์มล็อกอินต้องการ ไม่มีใครเขียนมันแบบนั้นโดยตั้งใจ มันเกิดขึ้นจากลำดับที่เบราว์เซอร์โหลดสิ่งต่าง ๆ

ผู้ช่วยเขียนโค้ดของคุณยังไม่ได้รับ memo

นี่คือส่วนที่ทำให้เหตุการณ์ล่มในปี 2024 กลายเป็นปัญหาในปี 2026 source.unsplash.com ถูกจัดทำเป็นเอกสาร เขียนในบล็อก สอนกัน และก็อปปี้กันมาราวแปดปี ข้อความทั้งหมดนั้นอยู่ใน training data ของโมเดลที่ตอนนี้เขียน starter code ให้เรา — และข้อความก็ไม่มีวันหมดอายุ ตัวเลขสามชุด:

การวัดผลค่าเมื่อ 30 ส.ค. 2026วิธีการวัด
ไฟล์ที่มี source.unsplash.com 3,344 GitHub code search API, q=source.unsplash.com (นับเฉพาะโค้ดสาธารณะที่ถูก index ไว้เท่านั้น — คือพื้นที่ขั้นต่ำ ไม่ใช่ตัวเลขรวมทั้งหมด)
repository ในตัวอย่าง 100 ไฟล์ที่ถูกสร้างขึ้น หลังจาก sunset 12 จาก 77 คำค้นหาเดิม 100 ผลลัพธ์ deduplicate ได้ 77 repository เทียบ created_at กับวันที่ 11 มิ.ย. 2024
…และ repository ในตัวอย่างนั้นที่มี push ภายใน 12 เดือนที่ผ่านมา 20 จาก 77 repository ที่ยังใช้งานจริง ไม่ใช่ archive — รวมถึง elastic/kibana ที่ไฟล์ demo ยังคงเขียนว่า imageUrl: 'https://source.unsplash.com/64x64/?dingo'
ยอดดาวน์โหลดรายเดือนของ unsplash-source-es6 23 npm registry API — wrapper สำหรับบริการที่ตายไปแล้ว publish ล่าสุดเมื่อปี 2022 แต่ยังถูกติดตั้งอยู่
โพสต์ Stack Overflow ที่กล่าวถึงมัน 1,459 Stack Exchange API, ยอดรวมจาก /search/excerpts

หลักฐานที่ตรงที่สุดไม่ได้อยู่ในโค้ดแอปพลิเคชันเลยด้วยซ้ำ — มันอยู่ในพรอมต์ ผลลัพธ์อันดับต้น ๆ ของ การค้นหานั้นคือคลังพรอมต์ระบบของ GPT ที่มีบรรทัด “please use unsplash API( https://source.unsplash.com/1280x720/?<PUT YOUR QUERY HERE>” คำสั่งนั้นยังคงถูกก็อปปี้ไปใช้ในผู้ช่วยตัวใหม่ ๆ จนถึงทุกวันนี้ โมเดลไม่ได้ตรวจสอบ URL นั้นเลย มันแค่ถูกสั่งให้ใช้ และตัวอย่างทุกตัวที่มันเคยเห็นก็เห็นตรงกันหมด

ดังนั้นความล้มเหลวของโค้ดที่ถูก generate ขึ้นจึงมีสาเหตุอิสระสองอย่าง และการแก้ไขอย่างหนึ่งไม่ได้แก้อีก อย่างหนึ่ง: training data ที่เก่าไปแล้ว และคำสั่งที่เก่าไปแล้วซึ่งมนุษย์เขียนทับลงไปอีกที ไม่ว่าจะแบบไหน อาการก็เหมือนกัน คือกลุ่มภาพที่ไม่มีวันโหลดขึ้นมา:

รูปแบบภาพที่ตายแล้วซึ่งควรค้นหาด้วย grep
source.unsplash.com/random/1200x800   # 503 ตั้งแต่กลางปี 2024 — ไม่กลับมาอีกแล้ว
images.unsplash.com/photo-…           # CDN จริง แต่ ID ที่จำมาอาจไม่มีอยู่จริง
via.placeholder.com/400               # กล่องสี่เหลี่ยมสีเทา ที่หลุดเข้าไปใน production
placehold.co/800x600                  # กล่องสี่เหลี่ยมสีเทา แบบตั้งใจ
picsum.photos/800/600                 # ภาพจริง แต่ไม่เกี่ยวกับหน้าของคุณ
/placeholder.png                      # ไฟล์ที่ไม่เคยถูกเพิ่มเข้า repo

เชิงอรรถสำหรับบรรทัดที่สองในรายการนั้น: ระหว่างที่เขียนบทความนี้ via.placeholder.com ก็ทำ TLS handshake ไม่สำเร็จจาก network ทดสอบของเราเหมือนกัน และตอบกลับ 403 ผ่าน HTTP ธรรมดา ตรวจสอบมันจาก network ของคุณเองก่อนเชื่อถือ — fallback ที่เครื่องมือเหล่านี้เอื้อมไปหา ก็อาจมีเรื่องราวการล่มของตัวมันเองเช่นกัน

มีแค่ตัวแรกเท่านั้นที่เสียจริง ๆ ตัวอื่น ๆ แย่กว่านั้นในแบบที่ละเอียดอ่อนกว่า: มันโหลดได้ เลย์เอาต์ ดูเหมือนเสร็จสมบูรณ์ และไม่มีใครสังเกตว่าหน้านั้นถูกประดับด้วยอะไรบางอย่างที่ไม่เกี่ยวข้องเลย และ เรื่องทั้งหมดนี้ไม่ได้เจาะจงแค่กับภาพ — มันคือรูปแบบทั่วไปของปัญหา ภาพของเว็บที่โมเดลมีอยู่คือ snapshot และ endpoint, CLI flag, ชื่อ package และแผน free tier ก็ยังคงเปลี่ยนแปลงต่อไปหลังจาก ชัตเตอร์ปิดลง

ตารางการย้ายระบบ

มีปลายทางอยู่พอดีสามแบบ และวิธีที่ตรงไปตรงมาในการนำเสนอคือดูจากสิ่งที่คุณต้องยอมสละไป เลือกคอลัมน์ก่อน แล้วจึงอ่านแถวของคุณ

URL Source เดิม A. URL CDN คงที่ไม่ต้องใช้คีย์ · ไม่มีความสุ่ม B. Unsplash APIใช้คีย์ · เรียกฝั่งเซิร์ฟเวอร์ C. Proxy ของคุณเองซ่อนคีย์ · ได้ความสุ่มกลับมา
/random images.unsplash.com/photo-… — ภาพเดียวที่คุณเลือกเอง GET /photos/random /?w=1600
/random/1600x900 …?w=1600&h=900&fit=crop /photos/random + พารามิเตอร์ Imgix บน URL ที่ได้กลับมา /?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 ปักหมุดภาพหนึ่งภาพ หมุนเวียนใน build ของคุณ ไม่มีตัวเทียบเท่า — ต้อง cache ภาพสุ่มหนึ่งภาพไว้ 24 ชั่วโมงเอง เหมือนกัน แต่ cache อยู่ใน proxy

ตัวเลือก A คือตัวที่คนส่วนใหญ่ต้องการจริง ๆ ถ้าภาพนั้นเป็นแค่การตกแต่ง — hero, แผงข้างหน้าล็อกอิน, พื้นหลังการ์ด — คุณไม่เคยต้องการภาพที่แตกต่างกันในทุกคำขอเลย เลือกภาพเดียว เก็บ URL ของ CDN ไว้ แล้วหน้าเว็บก็จะเลิกพึ่งพาอะไรที่สุ่มได้:

URL Unsplash แบบคงที่ ปรับขนาดได้ — ไม่ต้องใช้คีย์ ไม่ต้องเรียก API
<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 ที่ API ให้มาไว้ — มันคือสิ่งที่ใช้รายงานการเข้าชม

ตัวเลือก B คือเส้นทางอย่างเป็นทางการ และมันย้ายการเรียกไปฝั่งเซิร์ฟเวอร์ เพราะ Client-ID ที่อยู่ใน JavaScript ฝั่ง front-end ก็คือ credential ที่เผยแพร่ออกมา ให้สังเกตกฎสองข้อที่คนมักพลาด: collections/topics ใช้รวมกับ query ในคำขอเดียวกันไม่ได้ และ count (สูงสุด 30) จะเปลี่ยนรูปแบบ ของ response เป็น array แม้ว่าจะเป็น 1 ก็ตาม

ตัวทดแทนอย่างเป็นทางการสำหรับ /random
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

ตัวเลือก C: สร้าง Source ขึ้นมาใหม่ ในประมาณสี่สิบบรรทัด

ถ้าสิ่งที่คุณเสียไปคือพฤติกรรมจริง ๆ — URL ที่ไม่ต้องใช้คีย์ ที่คืนภาพต่างกันในทุกครั้ง ใช้งานได้ตรง จากแท็ก <img> ใน field ของ CMS หรือในเว็บไซต์แบบ static ที่ไม่มีเซิร์ฟเวอร์เลย — งั้นคุณก็ต้องรัน endpoint นั้นด้วยตัวเอง มันเป็นเพียง worker เล็ก ๆ ตัวหนึ่งที่อยู่หน้า API และสามสิ่ง ที่ทำให้มันรอดจากการปะทะกับ production คือ cache, การตรวจสอบ referrer, และการส่ง query string ที่เหลือต่อไปยัง CDN

worker.js — endpoint ไม่ต้องใช้คีย์ในรูปแบบของ Source บน /photos/random
// Cloudflare Workers ที่อื่น (Deno Deploy, Val Town…) รูปแบบเดียวกัน
// แต่เปิด named cache ด้วย 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 ปฏิเสธการเก็บอะไรก็ตามที่ไม่ใช่ GET และ
    //    endpoint ของภาพก็ไม่มี verb อื่นให้ตอบสนองอยู่แล้ว
    if (req.method !== "GET")
      return new Response("Method not allowed", { status: 405 });
    const url = new URL(req.url);

    // 1. อนุญาตให้เฉพาะหน้าของคุณฝังมันได้ — endpoint สุ่มภาพสาธารณะ
    //    บนอินเทอร์เน็ตแบบเปิดคือ rate limit ของคนอื่นที่จะถูกใช้จนหมด
    const ref = req.headers.get("referer");       // ไม่มีใน client ที่ถูกต้องหลายตัว
    if (ref && !ALLOWED.includes(host(ref)))
      return new Response("Forbidden", { status: 403 });

    // 2. Cache ตามชุดพารามิเตอร์ ทำให้หน้าที่มี 12 ภาพ
    //    เสียค่า API หนึ่งครั้งต่อนาที แทนที่จะเป็นสิบสองครั้งต่อการ render
    const cache = caches.default;
    const hit = await cache.match(req);
    if (hit) return hit;

    // 3. ขอภาพสุ่มจาก API อย่างเป็นทางการ
    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. สร้าง URL ภาพขึ้นใหม่: เก็บ 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,
      // เครดิตไปพร้อมกับ redirect; ค่า header ต้องเป็น ASCII จึงต้อง encode
      "X-Photo-Credit": encodeURIComponent(photo.user.name + " on Unsplash"),
      "X-Photo-Link": photo.links.html,
    }});
    ctx.waitUntil(cache.put(req, res.clone()));
    return res;
  },
};

การย้าย URL หนึ่งตัวก็แค่ search-and-replace เท่านั้น ซึ่งนี่แหละคือสิ่งที่ทำให้ตัวเลือกนี้คุ้มค่ากับ เวลายี่สิบนาทีที่เสียไป:

การแทนที่แบบหนึ่งต่อหนึ่ง
- https://source.unsplash.com/collection/67920491/1600x900
+ https://img.example.com/?collections=67920491&w=1600&h=900&fit=crop

มีข้อสังเกตด้านการออกแบบสองข้อ ทั้งสองข้อนี้ทำให้ใครสักคนต้องเจอบ่ายวันแย่ ๆ ก่อนที่มันจะถูกจดไว้ การ redirect (302) แทนที่จะ proxy ไบต์ภาพเองทำให้คุณไม่ต้องรับผิดชอบเรื่อง bandwidth และยังทำให้การเข้าชมถูกนับบน CDN ของ Unsplash ซึ่งตรงกับที่แนวทางกำหนดไว้ ส่วนการตรวจสอบ Referer จงใจปล่อยผ่านเมื่อ header นั้นไม่มีอยู่ — เพราะ client ที่ถูกต้องหลายตัวก็ตัด มันทิ้ง — ในขณะที่ยังคงป้องกันกรณีชัดเจนที่ endpoint ของคุณกลายเป็น API ภาพฟรีของคนอื่น

กฎที่คุณต้องรับสืบทอดทันทีที่ใช้ API

Source ไม่มีกฎเพราะไม่มีบัญชี API มีกฎห้าข้อที่เปลี่ยนวิธีการออกแบบสถาปัตยกรรม ทั้งหมดมาจาก เอกสารปัจจุบัน:

  • Rate limit นับเป็นรายชั่วโมง และเริ่มต้นน้อย 50 คำขอ/ชั่วโมง ในโหมด demo; 1,000/ชั่วโมง หลังจากแอปพลิเคชันของคุณได้รับการอนุมัติสำหรับ production มีแค่การเรียก api.unsplash.com เท่านั้นที่นับ — คำขอภาพไปยัง images.unsplash.com ไม่นับ อ่านค่า X-Ratelimit-Remaining ในทุก response
  • Hotlinking เป็นข้อบังคับ ไม่ใช่แค่ได้รับอนุญาตเฉย ๆ Unsplash กำหนดให้ URL ของภาพที่ API คืนมาต้องถูกฝังเข้าไปโดยตรง เพื่อให้การเข้าชมภาพสามารถระบุที่มาไปยังช่างภาพได้ การ mirror ไฟล์ไปยัง CDN ของคุณเองคือการปรับปรุงหนึ่งเดียวที่คุณทำไม่ได้
  • เก็บพารามิเตอร์ ixid ไว้ การปรับขนาดและครอป URL ที่ได้คืนมาเป็นสิ่งที่คาดหวัง แต่การตัดพารามิเตอร์ที่ระบุแอปพลิเคชันของคุณทิ้งไม่ใช่
  • การให้เครดิตและการติดตามการดาวน์โหลดเป็นส่วนหนึ่งของข้อตกลง — ช่างภาพและ Unsplash ต้องได้รับเครดิต และ "การดาวน์โหลด" จะถูกรายงานผ่าน download endpoint ของภาพนั้น เมื่อผู้ใช้นำไฟล์ไปใช้ ซึ่งเป็น event ที่คุณต้อง fire เอง
  • ผลิตภัณฑ์ที่กระจายออกไปต้องมีการลงทะเบียนไคลเอนต์แบบไดนามิก ถ้าคุณเผยแพร่ plugin ธีม หรือ self-hosted CMS การใช้คีย์เดียวร่วมกันคือทั้งการละเมิดนโยบายและจุดล้มเหลวเดียว (single point of failure) API มีขั้นตอนการลงทะเบียนสำหรับกรณีนี้โดยเฉพาะ

นี่คือช่วงเวลาที่ควรพูดตรง ๆ เรื่องขอบเขต: ถ้าคุณจะต้องต่อ API และใช้คีย์อยู่แล้ว การเลือก API ภาพตัวไหน ก็เปิดกว้างขึ้นมาทันที และมันก็คุ้มค่าที่จะเสียเวลาห้านาทีก่อนเขียน client เราได้เปรียบเทียบตัวที่ฟรี — โควตา กฎ พฤติกรรมการค้นหา และรูปแบบ response — ไว้ใน การเปรียบเทียบ API ภาพสต็อกฟรี

ถ้าคุณต้องการแค่ placeholder ก็ควรบอกไปตรง ๆ

การใช้งาน Source ส่วนใหญ่ไม่เคยเกี่ยวกับ Unsplash เลยด้วยซ้ำ มันคือ "ใส่อะไรที่หน้าตาเหมือนภาพ ไปก่อน ระหว่างที่ฉันสร้างเลย์เอาต์" สำหรับกรณีนั้น บริการแบบไม่ต้องใช้คีย์ยังคงมีอยู่และเป็นคำตอบ ที่ถูกต้อง:

บริการต้องใช้คีย์ไหม?คุณจะได้อะไรข้อจำกัด
Lorem Picsum ไม่ ภาพถ่ายจริง: picsum.photos/800/600, แบบคงที่ด้วย /id/237/… หรือ /seed/xxx/…, พร้อม ?grayscale และ ?blur=1..10 endpoint /v2/list ของมันให้เครดิตหน้า Unsplash และผู้เขียนของภาพแต่ละภาพ ไม่มีการเจาะจงหัวข้อเลย ภาพจะไม่เกี่ยวข้องกับหน้าของคุณ
placehold.co ไม่ สี่เหลี่ยมมีป้ายกำกับ ขนาดใดก็ได้ — filler สำหรับ wireframe แบบตรงไปตรงมา มันคือกล่องสีเทา และมันก็ดูเป็นแบบนั้นในภาพหน้าจอที่แชร์ให้ลูกค้าดู
Openverse ไม่ แคตตาล็อกที่มีสัญญาอนุญาตแบบเปิด พร้อม API สาธารณะ ดำเนินการโดย WordPress.org จับคู่ด้วยคำสำคัญ และสัญญาอนุญาตแตกต่างกันไปในแต่ละรายการ — คุณต้องอ่านเอง

ความแตกต่างที่สำคัญคือ: placeholder เป็นสิ่งชั่วคราวโดยนิยาม ถ้าภาพนั้นรอดไปจนถึง production มันก็ไม่ใช่ placeholder อีกต่อไป — มันคือภาพประกอบที่ไม่มีใครเลือก และผู้อ่านก็บอกได้

คีย์เดียว แทนที่จะเป็นสาม

นี่คือส่วนของการย้ายระบบที่ไม่มีใครวางแผนไว้: คนที่ออกจาก Source แทบไม่เคยไปลงเอยที่ API ตัวเดียว เลย หน้าหนึ่งต้องการ hero สองภาพประกอบส่วน และภาพสำหรับ card grid และคำตอบ ที่ตรงไปตรงมามักจะเป็น Unsplash บวกกับ Pexels บวกกับ Pixabay — สามการลงทะเบียน สามรูปแบบการยืนยันตัวตน สามรูปแบบ JSON สามโมเดล pagination และสามชุดกฎการให้เครดิต ทั้งหมดนี้ เพื่อเติมแท็ก <img> เดียวกัน งานการต่อระบบนั้นคือค่าใช้จ่ายที่แท้จริงจากการที่ URL แบบไม่ต้องใช้คีย์หายไป และมันมาถึงหลายสัปดาห์หลังจากที่ระบบล่มซึ่งเป็นสาเหตุของมัน

การรวมทั้งหมดนั้นให้เป็นการต่อระบบเดียวคือสิ่งที่เราสร้าง Pexafy ขึ้นมาเพื่อการนี้: คีย์เดียวครอบคลุม 9 คลังภาพที่มีสัญญาอนุญาตฟรีในสคีมาเดียว พร้อมการค้นหาเชิงความหมาย ระดับประโยค — ดังนั้นคำอธิบายเต็ม ๆ อย่าง “a cracked phone screen on a wooden desk, shot from above” จะได้ผลลัพธ์ที่จัดอันดับแล้ว แทนที่จะไม่ได้อะไรเลย ข้อจำกัดสองข้อ พูดตรง ๆ เพราะบทความ ทั้งบทนี้ก็คือเรื่องของการไม่ให้ประหลาดใจซ้ำสอง: มันต้องใช้คีย์ ดังนั้นมันไม่ได้ ฟื้นฟูสิ่งที่ Source เคยเป็น — มันอยู่ในหมวดหมู่เดียวกับ Unsplash API อย่างเป็นทางการข้างต้น; และมันมีเฉพาะภาพถ่ายที่มีสัญญาอนุญาตฟรี ไม่ใช่ภาพเชิงบรรณาธิการหรือภาพแบรนด์

ส่วนที่ใหม่จริง ๆ มุ่งไปที่ผู้ช่วย AI ข้างต้น: MCP server ที่ mcp.pexafy.com/mcp หมายความว่าโมเดลที่ปกติจะท่อง URL ภาพจากความจำ สามารถค้นหา แคตตาล็อกจริงและคืนภาพที่มีอยู่จริง พร้อมเครดิตติดมาด้วย นี่คือคำตอบที่ดีกว่าสำหรับปัญหา URL ที่ตายแล้วซึ่งเครื่องจักรเขียนขึ้นมา มากกว่ากฎ lint ใด ๆ และเหตุผลเบื้องหลังนี้ถูกเขียนไว้ใน โครงสร้างพื้นฐาน การค้นหาภาพสำหรับ AI agent

ตรวจสอบระบบของคุณใน 10 นาที

ไม่ว่าคุณจะย้ายไปใช้อะไรก็ตาม ทำขั้นตอนนี้ก่อน — คุณแก้ URL ที่ยังหาไม่เจอไม่ได้ Source ก็แค่ ตัวอย่างของวันนี้; สามขั้นตอนเดียวกันนี้ใช้ได้กับ asset ภายนอกทุกตัวที่คุณฝังไว้

ค้นหา reference ที่ตายแล้วทุกตัว แล้วกันไม่ให้เกิดขึ้นอีก
# 1. ทุกอย่างใน repo รวมถึงเอกสาร, test, fixture และ README
grep -rn --binary-files=without-match \
  -e "source.unsplash.com" -e "via.placeholder.com" -e "/placeholder.png" .

# 2. ทุกอย่างที่ฐานข้อมูลเก็บไว้ — เนื้อหาใน CMS คือที่ที่สิ่งเหล่านี้ซ่อนอยู่นานที่สุด
psql -c "SELECT id FROM posts WHERE body LIKE '%source.unsplash.com%'"

# 3. ทุกอย่างที่เว็บไซต์ที่ build แล้วเรียกใช้จริง: crawl มันแล้วแสดงรายการที่ล้มเหลว
#    จับคู่ที่ attribute ไม่ใช่นามสกุลไฟล์ — URL ภาพไม่ค่อยลงท้ายด้วย .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   ← สิ่งที่คุณกำลังตามหาอยู่

จากนั้นก็ตัดสินใจ ครั้งเดียว ว่าจะยอมให้ภาพภายนอกมีค่าใช้จ่ายกับคุณได้มากแค่ไหน กฎสี่ข้อที่ยังใช้ได้ ในการปิดตัวครั้งต่อไป ไม่ว่าใครจะเป็นสาเหตุ:

  1. ดึงภาพตอน build ไม่ใช่ตอน request ภาพที่ resolve ระหว่าง build จะล้มเหลวใน CI ต่อหน้านักพัฒนา แทนที่จะล้มเหลวตอนตีสามต่อหน้าผู้ใช้
  2. อย่าปล่อยให้ asset ที่เป็นแค่การตกแต่งไปขวาง critical path อย่า preload อะไรจากภายนอกเหนือฟอร์มล็อกอิน; ให้ทุก <img> ของ third-party มี fallback onerror และมี width/height ที่ชัดเจน เพื่อให้ความล้มเหลว แค่เสียกล่องเปล่า ไม่ใช่ layout shift หรือสคริปต์ที่ค้าง
  3. เพิ่มการตรวจสอบเข้าไปใน CI ขั้นตอนที่ 3 ข้างต้น รันบน output ที่ build แล้ว ของคุณ เปลี่ยน "สุดท้ายมีคนสังเกตเห็น" ให้กลายเป็น build ที่แดง มันคือขั้นตอนเดียวที่ป้องกันการ เกิดซ้ำได้
  4. กำหนดงบประมาณให้กับ dependency ภายนอกเหมือนกับที่อื่น ๆ เขียนไว้ว่าหน้าเว็บของ คุณได้รับอนุญาตให้พึ่งพาโฮสต์ไหนได้บ้าง และจะเกิดอะไรขึ้นเมื่อแต่ละตัวล่ม URL ที่คุณไม่ต้องลงทะเบียน ก็ยังคงเป็น dependency อยู่ดี — Source พิสูจน์แล้วว่ามันเป็นเพียงตัวที่ไม่มีใครเป็นเจ้าของ

แหล่งอ้างอิง & เชิงอรรถ

1 รหัสสถานะ ตัวเลข และบรรทัดที่ยกมาทุกอันในบทความนี้ถูกนำมาจาก แหล่งข้อมูลปฐมภูมิของมันเมื่อวันที่ 30 สิงหาคม 2026 รหัสสถานะ HTTP ถูกดึงด้วย curl เทียบกับทุกรูปแบบ URL; ทั้งหมดคืนค่า 503 พร้อม server: Heroku และ body ที่ฝัง herokucdn.com/error-pages/application-error.html ไว้ การ resolve DNS ได้รับ การยืนยันในวันเดียวกัน (เป็น CNAME ไปยังโฮสต์ herokudns.com)

2 ข้อความ changelog ทั้งสองรายการถูกยกมาแบบคำต่อคำจาก unsplash.com/documentation/changelog ถ้อยคำของนโยบายการเลิกใช้งาน (แจ้งล่วงหน้า 3 สัปดาห์, header Warning, และข้อยกเว้นสำหรับ endpoint ที่ไม่มีเอกสารเผยแพร่สู่ สาธารณะ) มาจาก unsplash.com/documentation วันเดียวกัน

3 ความล้มเหลวของ TLS ถูกทำซ้ำด้วย openssl s_client -connect changelog.unsplash.com:443 (tlsv1 alert internal error) โซ่การ redirect ถูกตามด้วย curl -L ช่องว่างของ archive ถูกดึงมาจาก Wayback CDX API: การเก็บภาพสำเร็จ (200) ครั้งสุดท้ายของ changelog.unsplash.com เมื่อวันที่ 20240324 และครั้งแรกของ unsplash.com/documentation/changelog เมื่อวันที่ 20240823

4 ชื่อ issue วันที่สร้างและปิดอ่านมาจาก GitHub REST API (mui/material-ui#42736, nextcloud/unsplash#115, sindresorhus/Actions#248) และจาก drupal.org JSON API สำหรับ gin_login issue 3324054 ซึ่งเนื้อหารายงานว่า “I get always a Heroku application error” ในเดือนพฤศจิกายน 2022

5 ตัวเลข: GitHub code search API (3,344 ไฟล์; ตัวอย่าง 100 ผลลัพธ์ deduplicate ได้ 77 repository ซึ่ง 12 ตัวถูกสร้างขึ้น หลังวันที่ 11 มิถุนายน 2024 และ 20 ตัวมีการ push ภายใน 12 เดือนก่อนหน้า); npm registry downloads API (unsplash-source-es6, 23 ดาวน์โหลดใน 30 วันก่อนหน้า); Stack Exchange /search/excerpts (1,459 โพสต์) การค้นหาโค้ดครอบคลุม เฉพาะ repository สาธารณะที่ถูก index ไว้เท่านั้น ดังนั้นแต่ละตัวเลขจึงเป็นค่าขั้นต่ำ

คำถามที่พบบ่อย

source.unsplash.com ล่มอยู่ หรือถูกปิดตัวถาวรไปแล้ว?
ถาวรแล้ว Unsplash ประกาศยุติการให้บริการเมื่อ 11 มิถุนายน 2024 — “เราจะเริ่มต้นด้วยการปิดฟีเจอร์ค้นหาก่อน และในอีกไม่กี่สัปดาห์ถัดไปจะปิดแอปพลิเคชันทั้งหมด” — หลังจากประกาศเลิกใช้บริการนี้ไปเมื่อวันที่ 25 พฤศจิกายน 2021 ทุกรูปแบบ URL (/random, /1600x900/?query, /collection/…, /daily) จะคืนค่า HTTP 503 พร้อมหน้า Application Error แบบทั่วไปของ Heroku เนื่องจากชื่อโฮสต์ยัง resolve ได้อยู่ ความล้มเหลวจึงปรากฏเป็นรูปภาพเสียแทนที่จะเป็น network error
อะไรคือตัวทดแทนโดยตรงของ source.unsplash.com/random?
ไม่มีทางเลือกแบบไม่ต้องใช้คีย์ที่ใช้แทนได้ทันที และนี่คือสิ่งที่ควรทำใจยอมรับตั้งแต่แรก มีทางเลือกอยู่สามแบบ URL แบบ CDN ตายตัว — images.unsplash.com/photo-…?w=1600&h=900&fit=crop — ไม่ต้องใช้คีย์แต่จะได้ภาพเดิมซ้ำทุกครั้ง ซึ่งจริง ๆ แล้วก็เพียงพอสำหรับการใช้งานเชิงตกแต่งส่วนใหญ่ API อย่างเป็นทางการ คือ GET https://api.unsplash.com/photos/random พร้อมส่ง header Authorization: Client-ID ซึ่งคืนความสุ่มกลับมาได้ แต่ต้องเรียกใช้จากฝั่งเซิร์ฟเวอร์เท่านั้น พร็อกซีขนาดเล็กที่คุณสร้างเอง ครอบ endpoint นั้นไว้ เป็นทางเลือกเดียวที่จะคืน URL แบบไม่ต้องใช้คีย์ให้คุณใส่ลงใน tag ได้โดยตรง
ทำไมเครื่องมือ AI coding ในปี 2026 ยังคงสร้าง URL ของ source.unsplash.com อยู่?
เพราะ URL นี้ถูกบันทึกไว้ในเอกสาร ถูกสอน และถูกคัดลอกต่อกันมานานเกือบแปดปี และชุดข้อมูลฝึกฝน (training corpus) ไม่ได้หมดอายุไปพร้อมกับบริการที่ถูกปิด จากการวัดผลเมื่อวันที่ 30 สิงหาคม 2026: GitHub code search ยังคืนผลลัพธ์ 3,344 ไฟล์ ที่มี URL นี้อยู่ และจากตัวอย่าง 100 ไฟล์ พบว่า 12 จาก 77 repository ถูกสร้างขึ้นหลังจากบริการถูกปิดแล้ว คลังคำสั่ง prompt ที่มนุษย์เขียนเองก็ยังคงย้ำคำสั่งนี้ซ้ำ — ตัวอย่างเช่น GPT prompt ที่ถูกคัดลอกกันอย่างแพร่หลายตัวหนึ่งยังคงบอกโมเดลว่าให้ “use unsplash API( https://source.unsplash.com/1280x720/?… )” ควรถือว่า URL รูปภาพใด ๆ ที่โมเดลเขียนขึ้นยังไม่ผ่านการยืนยัน และตรวจสอบ status code ใน CI เสมอ
ฉันยังสามารถขอรูปภาพสุ่มจาก Unsplash โดยไม่ใช้ API key ได้ไหม?
จาก Unsplash โดยตรงไม่ได้แล้ว — การเลือกภาพแบบสุ่มตอนนี้อยู่หลัง endpoint /photos/random ซึ่งต้องใช้ Client-ID เส้นทางแบบไม่ต้องใช้คีย์ที่คุณมีมีสองแบบ คือพร็อกซีที่คุณโฮสต์เอง ซึ่งคีย์จะอยู่ฝั่งเซิร์ฟเวอร์และ URL สาธารณะจะมีหน้าตาคล้ายของเดิม หรือบริการภาพตัวแทนจากบุคคลที่สาม เช่น Lorem Picsum (picsum.photos/800/600) ซึ่งให้ภาพถ่ายจริงโดยไม่ต้องใช้คีย์ แต่ก็ไม่สามารถกำหนดหัวข้อของภาพได้เลย
Unsplash API อนุญาตให้ดาวน์โหลดและโฮสต์รูปภาพเองได้ไหม?
ไม่ได้ ต่างจาก API ส่วนใหญ่ Unsplash บังคับให้ hotlink เท่านั้น กล่าวคือ URL รูปภาพที่ API คืนมาต้องถูกฝังใช้งานโดยตรง เพื่อให้ยอดการเข้าชมภาพถูกนับให้กับช่างภาพ มีข้อผูกพันสามอย่างที่มาพร้อมกัน ได้แก่ ต้องคงพารามิเตอร์ ixid ไว้เมื่อทำการปรับขนาดหรือครอป URL, ต้องให้เครดิตช่างภาพและ Unsplash, และต้องเรียก endpoint สำหรับดาวน์โหลดของภาพนั้นเมื่อผู้ใช้ทำการดาวน์โหลดไฟล์ การทำสำเนาไฟล์ไปไว้บน CDN ของตัวเองเป็นสิ่งเดียวที่คุณไม่มีสิทธิ์ทำ
ทำไมประกาศเลิกใช้บริการปี 2021 จึงไม่ได้ปกป้องผู้ใช้เดิม?
เพราะประกาศและนโยบายไม่ได้ครอบคลุมเรื่องเดียวกัน รายการ changelog วันที่ 25 พฤศจิกายน 2021 ให้คำมั่นว่า “การใช้งานเดิมจะยังทำงานต่อไป” โดยไม่ได้ระบุวันสิ้นสุดใด ๆ ในขณะที่นโยบายการเลิกใช้บริการที่ Unsplash เผยแพร่ไว้ — ซึ่งกำหนดให้แจ้งล่วงหน้าอย่างน้อยสามสัปดาห์พร้อม header Warning — จะใช้กับฟิลด์และ endpoint ที่ มีการบันทึกไว้เป็นเอกสารสาธารณะ เท่านั้น และในย่อหน้าเดียวกันก็ระบุว่าสิ่งที่ไม่มีเอกสารรองรับอาจเปลี่ยนแปลงได้โดยไม่ต้องแจ้งเตือน Source ไม่เคยเป็น endpoint ที่มีเอกสารรองรับของ API เลย จึงตกอยู่นอกเหนือนโยบายที่ควรจะปกป้องมันไว้
จะหา URL ของ source.unsplash.com ที่ตายไปแล้วทั้งหมดในโปรเจกต์ของฉันได้อย่างไร?
สามขั้นตอน ใช้เวลาสิบนาที Grep ทั้ง repository รวมถึงเอกสาร, เทสต์, fixtures และไฟล์ README ด้วยคำสั่ง grep -rn "source.unsplash.com" . เนื่องจาก URL เหล่านี้มักหลงเหลืออยู่นานที่สุดในโค้ดตัวอย่าง ค้นหาในฐานข้อมูล เพราะเนื้อหาบทความใน CMS มักเป็นที่ซ่อนตัว (WHERE body LIKE '%source.unsplash.com%') จากนั้น ไล่สแกน build output ของคุณ: ดึง URL รูปภาพทุกตัวออกมาแล้วส่งคำขอไปยังแต่ละตัว โดยแสดงรายการที่ไม่ได้ผลลัพธ์เป็น 200 เพิ่มขั้นตอนสุดท้ายนี้เข้าไปใน CI แล้ว asset ของบุคคลที่สามที่ตายไปแล้วจะทำให้ build ล้มเหลวแทนที่จะทำให้หน้าเว็บพัง

เลิกตามหาคำค้น แล้วบรรยายสิ่งที่คุณหมายถึงแทน

ค้นหารูปภาพใช้งานได้ฟรี 9M+ รูปตามความหมาย — ทุกภาษา ภายในเวลาไม่ถึง 100 มิลลิวินาที