11-OSS对象存储加速配置:静态资源、图片、视频缓存加速与防盗链
一、为什么需要 OSS + Nginx 配合?
无人售货柜系统里有大量静态资源:商品图片、商品视频、广告轮播图、促销素材。一开始可能直接放在服务器本地,但随着设备数量增长(几千台柜子、几万种商品),问题就来了:
- 存储不够:一台服务器挂几块硬盘,扩容要停机
- 带宽不够:几千台设备同时拉商品图片,单机带宽扛不住
- 可靠性差:服务器挂了图片全没了
这时候就轮到 OSS(对象存储服务)出场了。阿里云 OSS、腾讯云 COS、MinIO 自建都可以,本质都是分布式存储,容量无限、高可用、按量付费。
但直接让用户/设备访问 OSS 有两个问题:
- OSS 域名不统一:你的域名是
img.vending.com,OSS 的域名是vending-bucket.oss-cn-hangzhou.aliyuncs.com,不好看也不安全 - 每次都回源 OSS:热门商品图片被频繁访问,每次都从 OSS 拉取,浪费流量费用
所以标准架构是:OSS 做存储后端,Nginx 做反向代理 + 缓存层,用户只跟 Nginx 打交道。
二、Nginx 反向代理 OSS
基础代理配置
server { listen 443 ssl; server_name img.vending.com; ssl_certificate /etc/nginx/ssl/vending.pem; ssl_certificate_key /etc/nginx/ssl/vending.key; location / { proxy_pass https://vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; proxy_set_header Host vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 5s; proxy_read_timeout 10s; proxy_buffering on; proxy_buffer_size 16k; proxy_buffers 8 64k; } }几个关键点:
proxy_pass用内网域名:oss-cn-hangzhou-internal.aliyuncs.com是内网地址,Nginx 服务器和 OSS 在同一区域时走内网,不消耗公网带宽,速度快且免费。这个省钱技巧很多新手不知道。proxy_set_header Host必须设为 OSS 域名:OSS 是通过 Host 头来识别请求属于哪个 Bucket 的,如果 Host 传成img.vending.com,OSS 会返回404。这是最容易踩的坑。proxy_buffering on:开启缓冲,Nginx 先把 OSS 返回的数据完整接收到本地内存/磁盘,再发给客户端。避免慢客户端把 Nginx 和 OSS 之间的连接占着不放。
三、缓存策略配置:proxy_cache
光代理不缓存等于白搞,加上proxy_cache才是真正的加速。
缓存配置
# http 块中定义缓存区域 http { proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=oss_cache:100m max_size=50g inactive=7d use_temp_path=off; }参数详解:
levels=1:2:缓存目录层级,Nginx 会把缓存文件按hash值的前1位和后2位分目录存储,避免单目录文件过多导致性能下降keys_zone=oss_cache:100m:缓存区名称叫oss_cache,分配100MB内存存储缓存key,1MB大约能存8000个key,100MB够存80万个文件的索引max_size=50g:缓存文件最大占用50GB磁盘空间,超过后Nginx自动淘汰最久没用的缓存inactive=7d:缓存文件7天没被访问就删除,不管它有没有过期use_temp_path=off:临时文件直接写在缓存目录里,避免跨磁盘拷贝
在 location 中启用缓存
server { listen 443 ssl; server_name img.vending.com; ssl_certificate /etc/nginx/ssl/vending.pem; ssl_certificate_key /etc/nginx/ssl/vending.key; # 图片缓存 location ~* \.(jpg|jpeg|png|gif|webp|ico)$ { proxy_cache oss_cache; proxy_cache_key "$scheme$request_method$host$request_uri"; proxy_cache_valid 200 7d; proxy_cache_valid 404 1m; proxy_cache_use_stale error timeout updating invalid_header http_500 http_502 http_503 http_504; proxy_pass https://vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; proxy_set_header Host vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; # 添加缓存命中状态到响应头,方便调试 add_header X-Cache-Status $upstream_cache_status; add_header Cache-Control "public, max-age=604800"; } # 视频缓存 location ~* \.(mp4|flv|m3u8|ts)$ { proxy_cache oss_cache; proxy_cache_key "$scheme$request_method$host$request_uri"; proxy_cache_valid 200 30d; proxy_cache_valid 404 1m; proxy_cache_use_stale error timeout updating; proxy_pass https://vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; proxy_set_header Host vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; add_header X-Cache-Status $upstream_cache_status; add_header Cache-Control "public, max-age=2592000"; # 视频文件大,开启分片传输支持 proxy_force_ranges on; } }proxy_cache_valid 200 7d:HTTP 200响应缓存7天。proxy_cache_valid 404 1m:404也缓存1分钟,防止恶意刷不存在的URL把OSS打爆。
proxy_cache_use_stale这个参数很实用——当 OSS 不可用时,Nginx 可以继续返回旧的缓存数据(即使已过期),保证用户还能看到图片,而不是白屏。
X-Cache-Status响应头的值含义:
| 状态 | 含义 |
|---|---|
| MISS | 未命中缓存,回源OSS获取 |
| HIT | 命中缓存,直接从Nginx返回 |
| EXPIRED | 缓存已过期,回源获取新数据 |
| UPDATING | 缓存已过期,正在后台更新 |
| STALE | OSS不可用,返回了旧缓存 |
四、图片处理:压缩/裁剪/格式转换
阿里云 OSS 和腾讯云 COS 都自带图片处理功能,通过 URL 参数就能实现压缩、裁剪、格式转换,不需要在服务端做任何处理。
location ~* \.(jpg|jpeg|png)$ { proxy_pass https://vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; proxy_set_header Host vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; proxy_cache oss_cache; proxy_cache_key "$scheme$request_method$host$request_uri"; proxy_cache_valid 200 7d; # 替换请求URL,统一追加图片处理参数 # 小程序端统一返回webp格式 + 质量压缩 set $args "${args}&x-oss-process=image/format,webp/quality,q_80"; }这段配置的巧妙之处在于set $args——所有图片请求自动追加 OSS 图片处理参数,把 PNG/JPG 统一转成 WebP 格式(体积减少30%-50%),质量压缩到80%。前端完全不用改,Nginx 层面统一处理。
当然,如果不同场景需要不同尺寸(列表缩略图 vs 详情大图),建议前端直接在URL里拼参数,Nginx 只做缓存和防盗链:
# 缩略图:200x200裁剪 + webp https://img.vending.com/products/1001.jpg?x-oss-process=image/resize,m_fill,w_200,h_200/format,webp # 详情图:800x800等比缩放 + webp https://img.vending.com/products/1001.jpg?x-oss-process=image/resize,w_800/format,webp不同参数的URL会被 Nginx 当成不同缓存key,不会混淆。
五、防盗链配置
商品图片和视频是业务资产,不能让人随便白嫖你的带宽。Nginx 提供两种防盗链方式。
方式一:valid_referers 域名白名单
location ~* \.(jpg|jpeg|png|gif|mp4|webp)$ { valid_referers none blocked server_names *.vending.com *.vending.cn localhost 192.168.0.0/16; if ($invalid_referer) { return 403; } proxy_pass https://vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; proxy_set_header Host vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; proxy_cache oss_cache; proxy_cache_key "$scheme$request_method$host$request_uri"; proxy_cache_valid 200 7d; }none:允许没有 Referer 头的请求(直接在浏览器地址栏输入URL)blocked:允许有 Referer 头但被代理删掉值的请求*.vending.com:允许你自己的域名$invalid_referer为 true 时返回403
这种方式简单有效,但 Referer 头可以被伪造,安全性一般。
方式二:签名URL(更安全)
对于高价值资源(比如付费视频教程),用签名URL。签名URL是后端生成的、带过期时间和签名的临时URL:
// Java后端生成签名URL,有效期30分钟StringsignedUrl=ossClient.generatePresignedUrl("vending-bucket","videos/promotion.mp4",newDate(System.currentTimeMillis()+30*60*1000)).toString();Nginx 层面只需要做透传,不需要额外配置:
location /videos/ { proxy_pass https://vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; proxy_set_header Host vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; # 签名URL自带鉴权,不需要valid_referers proxy_cache oss_cache; proxy_cache_key "$scheme$request_method$host$request_uri"; proxy_cache_valid 200 30d; }六、CDN 加速配置
如果售货柜分布在全国各地,Nginx 缓存只部署在单机房,偏远地区的设备访问延迟依然很高。这时候需要 CDN。
CDN 架构下 Nginx 的角色变化:
设备/用户 → CDN边缘节点 → CDN中心节点 → Nginx(回源) → OSS此时 Nginx 作为 CDN 的回源服务器,需要做两个适配:
server { listen 443 ssl; server_name img.vending.com; # CDN回源时携带的头部,用于识别CDN节点 real_ip_header X-Forwarded-For; set_real_ip_from 0.0.0.0/0; location / { proxy_pass https://vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; proxy_set_header Host vending-bucket.oss-cn-hangzhou-internal.aliyuncs.com; proxy_set_header X-Real-IP $remote_addr; # CDN回源时带缓存控制头 proxy_cache oss_cache; proxy_cache_key "$scheme$request_method$host$request_uri"; proxy_cache_valid 200 7d; proxy_cache_use_stale error timeout updating; # 关键:CDN回源时带上Range头,支持分片请求 proxy_set_header Range $http_range; proxy_set_header If-Range $http_if_range; } }Range头是 CDN 分片回源的关键——CDN 节点可能只要视频的某一段,通过 Range 请求告诉 Nginx 只拉指定字节范围的数据,避免整文件传输。
七、缓存清理
商品图片更新了,但 Nginx 缓存还是旧的怎么办?两种方案:
方案一:文件名加版本号(推荐)
前端引用图片时带hash版本号:/products/1001.jpg?v=20260811。换图后改版本号URL,Nginx 自然就是新缓存。
方案二:主动清除缓存
使用proxy_cache_purge模块:
location ~ /purge(/.*) { allow 192.168.0.0/16; # 只允许内网清除 deny all; proxy_cache_purge oss_cache "$scheme$request_method$host$1"; }清除缓存:curl https://img.vending.com/purge/products/1001.jpg
架构设计上,OSS 做存储、Nginx 做缓存和代理、CDN 做边缘加速,三者配合才能应对全国几万台设备的图片视频分发需求。单靠任何一个组件都不够。