我想使用 nginx 作为缓存反向代理。我还有一个特殊要求,我认为可以用 nginx 来实现。
我使用 Amazon s3 作为原始服务器,并使用签名网址以保护内容。因此,每个用户都会获得一个在一定时间后过期的唯一 URL。尽管每个用户都有一个唯一的 URL,但为了让 nginx 无论如何都能缓存内容,我将缓存键定义为仅由请求文件名组成(请参阅下面的配置)。
到目前为止,效果非常好。问题是,如果请求 URL 无效(因为查询字符串中的签名太旧或无效),服务器仍然会传送文件。因为它已被缓存。
我确认初始请求必须包含有效签名。如果用户请求中的签名无效,nginx 无法从服务器获取它(当然)。
现在我想要的是每次请求时重新查找文件。此重新查找应使用用户指定的 URL 进行。如果请求成功,则应传送缓存的文件。
这正是应该使用以下方式实现的行为Cache-control: must-revalidate
因此我在我的原始服务器(amazon s3)上配置了这个标头。
然后我意识到 nginx 没有相应地运行。
因此文件直接从缓存中传送,而无需与源服务器进行验证。因此,错误的签名不会被识别,用户可以下载。
问题 1:在这种情况下,有没有办法让 nginx 遵守必须重新验证的标头?
这是我的配置文件
proxy_cache_path /home/sbgag/cache keys_zone=MYZONE:10m inactive=365d max_size=10g;
server {
listen 80;
server_name test.mydomain.com;
location / {
proxy_pass http://s3-eu-west-1.amazonaws.com;
proxy_set_header Host s3-eu-west-1.amazonaws.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_key "$request_filename";
more_set_headers "X-My-Proxy-Cache-Key $request_filename";
more_set_headers "X-My-Proxy-Cache-realpath_root $realpath_root";
more_set_headers "X-My-Proxy-Cache-uri $uri";
proxy_cache MYZONE;
proxy_cache_valid 200 365d;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
more_set_headers "X-AppServer $upstream_addr"; # Backend Server / Port
more_set_headers "X-AppServer-Status $upstream_status"; # Backend HTTP Status
more_set_headers "X-Cache $upstream_cache_status"; # HIT / MISS / BYPASS / EXPIRED
}
}
我还发现了这个变更日志
*) Feature: the "proxy_cache_revalidate", "fastcgi_cache_revalidate", "scgi_cache_revalidate", and "uwsgi_cache_revalidate" directives.
所以我想试试。将 nginx 升级到最新版本后,我将缓存时间设置为 0s。如果设置为 0s,则文件永远不会被缓存,因此我将其设置为 1s。
这产生了几乎我想要的行为。它会导致文件在 1 秒后在服务器上重新验证。然后使用用户提供的签名 URL 重新验证。如果不正确,则失败。此外,文件不会被删除,因为 nginx 似乎不会立即删除文件,而只会在空间已满时删除。因此,即使文件超时,即使另一个客户端提供了无效的 URL,下一个具有有效 URL 的客户端也可以从缓存中下载。
然而,在 1 秒的时间范围内,每个人都可以下载,但这对我来说并不是什么问题。
好的,这几乎就是我想要的,我不喜欢的是,它是一种丑陋的工作,它基于偶然而非功能性的行为。
问题2:没有更好的办法吗?
我最希望的是将请求传递给验证脚本,这样我就可以在后台使用我自己的脚本来验证请求。只有当该脚本返回成功时,才允许下载。然后利用现有的、经过验证的 nginx 缓存算法。
也许重写规则也可以。使用重写映射重写脚本,并且仅在验证成功时输出 URL。
欢迎任何意见!
答案1
我认为您正在寻找的实际上是添加这个:
代理缓存绕过$http_cache_control;
这样,发送设置了 Cache-Control:must-revalidate 标头的请求将绕过缓存。