图片放在 OSS 上,如果不做处理配置,通常会出现两个问题:一是流量账单随访问量线性上涨,二是这些流量有很大一部分并不来自自己的站点。原因不难理解——OSS 上的原图是对外可达的完整 URL,任何一个页面把它嵌进 img 标签就能直接引用,费用由存储方承担。
这篇文章按"先算钱、再写参数、最后上防护"的顺序拆开讲,重点说明图片处理这一层的计费口径、x-oss-process 参数的写法,以及实际配置中最容易出问题的四个点:默认不落盘、CDN 忽略参数、防盗链与空 Referer、私有 Bucket 的签名 URL。
一、计费口径:图片处理是按什么收费的
OSS 的基础计费是三块:存储容量、外网流出流量、请求次数。图片处理是在此之上单独叠加的计费项,很多人只算了前两块,账单自然会超出预期。
| 计费项 | 计费口径 | 需要留意的点 |
|---|---|---|
| 图片处理 | 按被处理的原图实际大小计费,不按处理次数 | 每月有免费额度,超出部分按量计费,按小时出账 |
| 图片高级压缩 | 按处理次数计费 | 输出尺寸不同、单价不同;HEIF/AVIF 仅部分地域支持 |
| 外网流出流量 | 按实际传出去的大小计费 | 缩放后的图只按缩放后的大小计流量 |
| 请求次数 | 按 Put/Get 等操作次数计费 | 缩略图墙一页几十个请求,量大时不可忽略 |
第三行是这套机制里最有价值的一条:一张 20MB 的原图缩到 2MB 再回给客户端,流量费只按 2MB 计。也就是说,图片处理费并不是净支出,它同时把流量费降下来了——前提是参数真的用上了,而不是把原图直接吐给浏览器。
还有一个容易忽略的细节:图片处理费按原图大小计算,与处理了几个动作无关。同一张图做裁剪、加水印、转 WebP,只要在同一条请求里用 / 串起来,就只按这张原图收一次费。能合并成一条 URL 的,不要拆成三次请求。
各档位单价会随活动和地域调整,具体以官方计费页实时展示为准,本文只讲口径。
二、参数写法:一条 URL 完成缩放、压缩与格式转换
图片处理不需要改代码,在 URL 后追加 query 即可:
https://your-bucket.oss-cn-hangzhou.aliyuncs.com/cover/2026/11/sample.jpg
?x-oss-process=image/resize,w_750/quality,q_80/format,webp
这条请求做了三件事:宽度等比缩到 750px、有损压缩质量 80、输出 WebP。实测一张 5MB 的照片可以降到 80KB 量级,具体数值取决于原图内容,照片和纯色海报差别很大。
常用参数如下:
| 参数 | 作用 | 注意点 |
|---|---|---|
resize,w_750 |
按宽等比缩放 | 需要放大时必须加 limit_0,否则默认不放大,直接返回原图 |
resize,m_fill,w_300,h_300 |
强制裁成指定尺寸 | 适合缩略图墙,注意构图是否被裁切 |
quality,q_80 |
有损压缩质量 | 80 在多数照片上是体积与画质的平衡点 |
format,webp |
转 WebP | 老版本客户端不支持;动图 WebP 部分场景需开通后使用 |
watermark,... |
加水印 | 参数较长,建议在控制台生成后复制 |
建议把规格收敛到固定几档(例如列表 750 宽、正文 1080 宽、头像 200×200),前端只改 src。规格越少,后面的缓存命中率越好做。
三、坑一:默认不落盘,同一张图每次访问都重新计算
OSS 图片处理默认是实时处理:请求进来,边处理边返回,处理结果不保存。这个设计本身没问题,但在高访问量下意味着同一张图被请求一万次,就计算一万次,也计费一万次。
两条解法:
- 开启图片处理持久化。把处理结果写入指定的 Bucket,后续请求直接命中已保存的文件,不再重复计算。适合缩略图、头像这类规格固定、访问频繁的图片。
- 在 CDN 侧缓存。同一套参数对应同一个 URL,CDN 命中后不回源,自然不会重复处理。这种方式改造成本最低,但要配合下面第四节一起看。
四、坑二:CDN 的「忽略参数」会吞掉处理参数
在 OSS 前面挂 CDN 是常规做法,但有一个配置项会直接让图片处理失效:忽略参数。
该选项开启时,CDN 回源前会把 URL 上的 query 全部去掉,x-oss-process=image/resize,w_750 属于 query,被清理后 OSS 收到的是一条不带处理参数的请求,返回的就是原图。结果既没有省下流量,又产生了处理费用(如果同时还开着其他处理入口)。
关闭「忽略参数」后功能恢复正常,但要清楚它的副作用:关闭后,参数不同的 URL 会被视为不同资源。页面上一旦同时存在 w_300、w_750、w_1080 三种规格,CDN 上就是三份缓存,缓存命中率下降、回源流量上升。
比较稳妥的做法是:规格尽量统一;确实需要多规格时,改用持久化把结果落盘,URL 上不带参数,命中率和计费都能回到正常水平。
五、坑三:开了防盗链,App 和小程序出现裂图
OSS 防盗链本质是 Referer 白名单。配置逻辑不复杂:白名单填自己的域名,支持 * 和 ? 通配符,也支持带端口(如 https://www.example.com:8080);黑白名单同时存在时,先判黑名单再判白名单。
问题出在「空 Referer」这个选项上。白名单配好之后如果把空 Referer 设为"不允许",网页端一切正常,但 App 内的原生图片组件、小程序、以及用户直接在地址栏粘贴 URL 访问,这些请求通常不带 Referer,会被统一判为 403。
这个选项没有通用答案,取决于业务形态:
| 业务形态 | 空 Referer 建议 | 原因 |
|---|---|---|
| 纯网页站 | 可以不允许 | 挡掉直接粘贴链接的访问,策略最严 |
| 有 App / 小程序 | 必须允许 | 原生请求通常不带 Referer,否则大面积裂图 |
| 图片需要对外分享 | 必须允许 | 微信、邮件中打开的链接多为空 Referer |
| 视频分片播放 | 谨慎不允许 | 部分播放器的分片请求不带 Referer,会导致无法播放 |
验证方法:用一个不在白名单的域名引用图片,返回 403 即生效。规则下发需要几分钟,不要配置完成后立即下结论。
六、坑四:私有 Bucket 的签名 URL,过期即全站破图
比防盗链更严格的做法是把 Bucket 设为私有,由后端签发带时效的 URL。匿名请求拿不到任何对象,但有两个副作用需要提前设计。
副作用一:过期。 签名 URL 包含 Expires 与 Signature,有效期一到,页面上的图全部失效。如果 CDN 缓存了一份即将过期的链接,缓存期内整站图片都是破图。签名有效期必须显著长于 CDN 缓存时间,这是硬约束。
副作用二:带签名的请求会跳过防盗链校验。 走签名路线后,Referer 白名单基本不再起作用,安全边界从"来源"转移到了"签名本身"。
因此这两者不是叠加关系,而是分层关系:公开图片走 Referer 白名单,敏感图片走私有 Bucket + 签名 URL。
七、三层防护的组合方式
单靠任何一层都不完整,实际配置通常是多层叠加:
| 手段 | 能挡住什么 | 挡不住什么 |
|---|---|---|
| Referer 白名单 | 普通网页盗链,成本最低 | 伪造 Referer 的脚本;直接抓取 URL;签名请求 |
| 私有 Bucket + 签名 URL | 匿名直接抓取 | 拿到签名 URL 后的转发;链接过期导致破图 |
| 阻止公共访问 | 误把 Bucket 设为公共读 | 已在白名单内的正常访问 |
| 流量 / 请求数告警 + 用量封顶 | 异常时及时发现,兜住费用上限 | 拦不住流量本身 |
| 日志转存 + 实时日志查询 | 事后定位来源 IP、被刷文件 | 需要主动查看 |
一个可直接套用的组合:图片 Bucket 保持公共读(用于对外访问),开启 Referer 白名单并放行空 Referer,同时开启阻止公共访问防止误操作,云监控上给"外网流出流量"和"Get 请求数"各配一条阈值告警,访问日志按小时转存。出问题时按 RemoteIP、RequestURI、Referer 三个字段即可定位。
八、配置检查清单
按顺序过一遍,基本可以覆盖这篇文章提到的全部要点:
- 图片规格固定为 2~3 档,不为单个页面临时加尺寸;
- 参数合并到一条 URL(
resize+quality+format),需要放大时补limit_0; - 高频规格开启图片处理持久化,或确认 CDN 命中率正常;
- 使用 CDN 时确认「忽略参数」为关闭状态,缓存 key 包含处理参数;
- 防盗链白名单带协议头;有 App、小程序或对外分享需求时放行空 Referer;
- 云监控配置外网流出流量与 Get 请求数告警,OSS 侧开启用量封顶,日志转存打开;
- 如使用私有 Bucket + 签名,签名有效期必须大于 CDN 缓存时间,并明确这部分不再依赖 Referer 白名单。
最后补充一点:图片处理常被当成"上云就自动具备"的能力。它确实是内置的,但每个默认行为背后都对应一条计费规则——不显式配置,它就按最保守(也就是最贵)的方式执行:返回原图、每次重算、参数被缓存层丢弃。
各档位的当前价格与免费额度,以对象存储 OSS 官方页面实时展示为准,比本文的描述更准确。

