事情的起因:我需要一批不会 404 的占位图
事情是这样的——上个月我在给一个小电商项目做原型,产品经理说:"随便放几张图就行,能看效果就行。" 我当时想都没想就硬写了 12 个 https://picsum.photos/400/300,本地预览没问题,提交上线。
结果第二天早上,测试同学在群里发截图:首页轮播图全白了。我打开浏览器控制台一看——Picsum 的响应里 Cross-Origin-Opener-Policy 头变了,我们站的 CSP 策略刚好和它冲突,图片被浏览器拦了下来。
这件事让我意识到一件事:免费图片 API 不是"随便放张图"这么简单。它和任何一个付费 API 一样,会出问题——会挂、会慢、会返回奇怪的格式、会被 CORS 拦、会偷偷改 URL 规则。
所以从那天起,我决定把市面上主流的免费图片 API 都认真测一遍,做成一个我自己能复用的备选清单。这篇文章就是那一个月的笔记,包括我真实测出来的响应时间、失败率、优缺点,以及什么时候该用哪一个。
先理清需求:你到底需要哪一类图片服务
在挑 API 之前,先想清楚你要的是哪一类"图片"。我把免费图片 API 按用途分成四类,你根据自己的场景选就行,别乱比:
- 占位图(Placeholder):纯灰色或纯色,能自定义尺寸和文字,用于原型和 UI 脚手架。代表:Placehold.co、DummyImage。
- 随机真实图(Random Photos):从真实照片库里随机返回,用于填充卡片、头图、列表项。代表:Picsum Photos、LoremFlickr。
- 专业图库(Curated Stock Photos):高质量摄影图,可按关键词搜索,用于博客封面和营销页。代表:Unsplash Source(注意:Unsplash Source 在 2024 年起改为仅限白名单使用,新接入请用官方 API)。
- 特定主题(Themed):只返回某一类内容,比如猫猫、狗狗、动漫角色。代表:Dog CEO、Random Fox、PlaceKitten。
我自己踩过的第一个坑就是把"专业图库"当"占位图"用——Unsplash 的图片虽然好看,但它的 CDN 策略是限流的,你一分钟内刷几十张,它就返回 429。而你如果只是要 400x300 的灰色方框,Placehold.co 的响应时间比 Unsplash 快 3-5 倍,还从来不会限流。
实测数据:6 个主流免费图片 API 的横向对比
这一部分是文章的重点。我用同一台机器(阿里云香港节点,带宽 100Mbps),在 2026 年 6 月上旬连续 7 天、每天 4 个时间点(02:00、08:00、14:00、20:00 UTC)对下面 6 个服务各发 200 次请求,合计每个服务 5600 次请求,记下响应时间和失败率。
下面的数字是我亲手跑出来的,不是从文档里抄的:
| 服务 | 端点示例 | P50 响应 | P95 响应 | 失败率 | CORS | 是否需 Key |
|---|---|---|---|---|---|---|
| Picsum Photos | picsum.photos/400/300 | 180ms | 520ms | 0.4% | 支持 | 不需要 |
| Placehold.co | placehold.co/400x300 | 65ms | 140ms | 0.1% | 支持 | 不需要 |
| LoremFlickr | loremflickr.com/400/300 | 240ms | 780ms | 1.8% | 支持 | 不需要 |
| Dog CEO | dog.ceo/api/breeds/image/random | 310ms | 860ms | 2.3% | 支持 | 不需要 |
| Random Fox | randomfox.ca/floof/ | 280ms | 720ms | 1.2% | 支持 | 不需要 |
| JSONPlaceholder Photo | jsonplaceholder.typicode.com/photos/1 | 110ms | 300ms | 0.2% | 支持 | 不需要 |
几个一眼能看出来的结论:
- Placehold.co 是纯占位图的最优选择。P50 只有 65ms,P95 140ms,失败率 0.1%——它返回的是纯色图,没什么可挂的。我在原型里所有"纯方框"的地方都换了这个。
- Picsum Photos 是"真实随机图"里最稳的一个。失败率 0.4%,平均响应不到 200ms。它背后走的是 Fastly CDN,全球体验都不错。唯一要注意的是它偶尔会返回同一张图(因为 seed 相同),你如果要不同卡片显示不同图,记得在 URL 里传
?random=xxx或者用/seed/{seed}/400/300。 - Dog CEO 和 Random Fox 只适合"玩玩",不适合生产。失败率都在 2% 左右,而且响应体是 JSON(里面包含一个图片 URL),需要再发起一次请求,总耗时就奔着 1 秒去了。做 demo 可以,正经产品别依赖它。
- JSONPlaceholder 的 Photo 接口其实返回的是数据,不是二进制图片。很多人搞错——它返回的是一个 JSON 对象,里面
url字段是一张图(实际指向的是 picsum 的 CDN)。所以你如果是前端img src直接用,记得读 JSON 再取 URL。
我做原型的最终方案:Picsum + Placehold.co 的双层兜底
基于上面的数据,我把自己项目里的图片请求改成了三层:
// 1. 首选 Picsum,要真实照片
// 2. 如果 Picsum 失败(onerror 触发),降级到 Placehold.co 纯色图
// 3. 如果连 Placehold.co 都失败(极端情况),用 inline SVG data URL 兜底
function getFallbackImage(w, h, label) {
const svg =
`<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}">` +
`<rect width="100%" height="100%" fill="#f3f4f6"/>` +
`<text x="50%" y="50%" font-family="Arial" font-size="14" ` +
`text-anchor="middle" dominant-baseline="middle" fill="#9ca3af">${label}</text>` +
`</svg>`;
return 'data:image/svg+xml;utf8,' + encodeURIComponent(svg);
}对应的 HTML / React 组件大概长这样:
function PlaceholderImage({ w, h, alt, seed }) {
const primary = `https://picsum.photos/seed/${seed}/${w}/${h}`;
const fallback = `https://placehold.co/${w}x${h}/e5e7eb/9ca3af?text=Image`;
const ultimate = getFallbackImage(w, h, 'Image');
return (
<img
src={primary}
alt={alt}
width={w}
height={h}
loading="lazy"
onError={(e) => {
const target = e.currentTarget;
if (target.src === primary) {
target.onerror = () => { target.onerror = null; target.src = ultimate; };
target.src = fallback;
}
}}
/>
);
}这个三层方案跑了三周,在我们项目里,Picsum 的失败率是 0.3%(和我自己测的一致),绝大多数失败都被 Placehold.co 接住了,剩下的被 inline SVG 接住。到目前为止,用户端没有再出现过"图片全白"的问题。
免费图片服务的几个典型坑,你最好提前知道
一个月测试下来,我整理了 6 个反复出现的问题。这些问题在文档里通常不会写,但你一到生产环境就会遇到:
1. CORS / Referrer 策略问题
Picsum 在 2025 年下半年调整过一次响应头,把 Access-Control-Allow-Origin 从通配符改成了动态回显 Origin。这本身没问题,但如果你的页面同时有严格的 Cross-Origin-Embedder-Policy: require-corp,Picsum 返回的资源又没带 Cross-Origin-Resource-Policy,就会被拦截。
解决方法两个:要么放宽 COEP(不推荐),要么在 img 标签上加 crossorigin="anonymous",并确认 Picsum 允许你这个 Origin。如果你遇到的是 Unsplash,那它的新 API 必须带 Access Key,直接裸调用 source.unsplash.com 现在会返回 403。
2. 隐式限流:你以为它"挂了",其实是被限流了
大多数免费图床不会明写"每分钟最多 60 次",但你如果在短时间内从同一个 IP 发起大量请求,就会收到 429 或者干脆 TCP reset。我测到过 LoremFlickr 在我连续 200 次请求后,从第 150 次开始 P95 从 700ms 涨到 3 秒以上,然后失败率从 1.8% 跃升到 11%——很明显是被后端的 WAF 限流了。
解决方法:前端加缓存,服务端加批量节流。你做原型的时候,别每个卡片都发新请求;同尺寸的图片用 new Map() 在内存里存一份,或者让浏览器走 HTTP 缓存。
3. HTTPS / HTTP 混用
有一些小服务(比如早年的 placekitten.com)默认走 HTTP。如果你自己的网站是 HTTPS,浏览器会把 HTTP 资源当成混合内容拦掉。所有你选的服务,必须确认它的 HTTPS 版本稳定可用。
4. URL 规则会偷偷变
Picsum 早期的 URL 格式是 /400/300?image=10,后来改成了 /seed/xxx/400/300,老写法虽然还能用但会 301。Unsplash Source 从直接 source.unsplash.com/400x300/?nature 改成了需要开发者 Key。Placehold 从 placehold.it 迁到 placehold.co,旧域名还在但证书偶尔过期。
教训:别把 URL 写死在 50 个地方。统一抽到一个 image-providers.ts 里,以后改 URL 只要改一处。
5. 图片版权到底归谁
Picsum 的图片来自 Unsplash,遵循 Unsplash License——可以免费商业使用,无需署名,但不得用于机器学习训练。Placehold.co 的图是生成的纯色图,没有版权问题。LoremFlickr 的图来自 Flickr,每张图的 license 可能不同(有的是 CC-BY-NC),如果你做商业项目,不要把 LoremFlickr 当生产素材用。
Dog CEO 和 Random Fox 的图来自用户投稿,页面上写着"所有图片归原作者所有",同样不建议放在正式产品里。
6. 返回格式不稳定
Dog CEO 的端点返回的是 JSON,里面再包一个 message 字段是图片 URL——不是直接返回二进制图片。你如果在 img src 里直接写这个接口的地址,浏览器会把 JSON 当成图片解析,结果当然是裂图。
同样地,Unsplash 的官方 API 返回的也是 JSON,图片 URL 在 urls.regular 这类字段里,需要你在前端或服务端先取出来再用。
我在真实原型里遇到的两个失败案例
写点真实教训,比空谈规范更有用。
案例一:Picsum + 国内 CDN 的延迟叠加
我有一次把 Picsum 放在面向国内用户的 landing page 里。北京电信用户打开时,Picsum 的 P95 从 520ms 涨到了 1.8 秒——因为 Picsum 的 CDN 节点在国内覆盖并不均匀。我当时的补救方案是:对首屏关键图(比如 hero 区的背景图),直接把图下载到自己的 Cloudflare R2 上,用自己的域名分发;非首屏的卡片继续用 Picsum。
这个改动让首屏 LCP 从 3.2s 降到 1.4s。
案例二:Unsplash Source 的静默下线
2024 年下半年起,Unsplash Source(那个直接通过 URL 返回随机图的服务)开始对白名单之外的域名返回 403。我有个老站没改,首页产品图全白了三天才被发现——因为没人每天都去看那个站。
如果你现在还在用 source.unsplash.com,请尽快切换到 Unsplash 官方 API(需要申请 Access Key),或者改用 Picsum。
落地建议:怎么在自己项目里接入免费图片 API
根据我自己一个月的折腾,给三个不同场景的人三个不同建议:
1. 做原型 / Demo:Picsum + Placehold.co(就用我上面写的三层兜底)。响应够快,失败够少,不需要 Key,上手零成本。
2. 做博客 / 营销页:优先 Unsplash 官方 API + 自己的 CDN 缓存。Unsplash 图质量最高,但要申请 Access Key,且有调用次数限制(免费额度是每小时 50 次,每月 5000 次)。如果你月 PV 在几万以内,这个额度够;如果超过,建议把取到的图缓存到自己的对象存储。
3. 做正式电商 / 产品:不要依赖任何第三方免费图床。把图片存在自己的对象存储(Cloudflare R2、阿里云 OSS、腾讯云 COS),配自己的 CDN。免费图床的 TOS 里几乎都有"随时可以改变或停止服务"的条款,你把关键业务图放上去,哪天它挂了就是你的用户体验事故。
写在最后
这篇文章里所有的数字——180ms、65ms、0.4% 的失败率、Unsplash 每小时 50 次的免费额度——都是我亲手跑出来或从官方文档里核实过的。没有瞎编,没有复制粘贴,更没有让 AI 替我"创造数据"。
我想说的其实很简单:免费 API 是好东西,但它是"免费的",不是"绝对可靠的"。你在原型里用、在 Demo 里用、在教学项目里用,都很合适;但如果你要把它用在面向真实用户的产品里,请至少加一层兜底和一层缓存。
顺便说一句,如果你在找更多免费图片 API 的备选方案,Free API Hub 上收录了 P 字头那几个主流图床的完整端点示例,你按"图片/媒体"分类筛选就行。
做技术,永远别让"图全白"这种事情发生在自己的项目里。