secure_link 指令返回空或403的主因是url中缺失st和e参数;它仅解码st并校验md5,不生成签名也不判断过期,过期需配合$secure_link_expires变量二次判断。

Secure-Link 能实现带签名和过期时间的下载链接,但必须配合 secure_link 和 secure_link_md5 指令协同工作,单独配置任意一个都会失效。
为什么 secure_link 指令总返回空或 403?
这是最常见误配:Nginx 的 secure_link 不是“开启开关”,而是“提取并解析签名参数”的指令。它依赖 URL 中显式传递的 st(签名)和 e(过期时间)两个参数,且二者缺一不可。
-
secure_link只负责解码st并比对本地计算的 MD5,不生成签名、也不校验时间——过期判断由后续if ($secure_link = "")或if ($secure_link_expires 手动完成 - URL 必须形如
/file.zip?st=xxx&e=1717027200,其中e是 Unix 时间戳(秒级),不是相对秒数 - 如果
secure_link解析失败(比如 base64 编码错误、字段缺失),变量$secure_link为空字符串,此时应直接 return 403
如何正确生成 st 签名值?
签名不是简单对文件路径哈希,而是对 “路径 + 过期时间 + 密钥” 三元组做 MD5,再 base64-url-safe 编码(去掉 +、/、=,替换为 -、_)。
- 例如保护
/downloads/report.pdf,过期时间1717027200,密钥为my_secret_key,则原始输入为/downloads/report.pdf1717027200my_secret_key - MD5 后得 16 字节二进制,需 base64 编码 → 再替换
+→-、/→_、去掉尾部= - Python 示例:
import hashlib, base64<br>path = "/downloads/report.pdf"<br>expires = "1717027200"<br>secret = "my_secret_key"<br>raw = (path + expires + secret).encode()<br>sig = hashlib.md5(raw).digest()<br>st = base64.urlsafe_b64encode(sig).decode().rstrip("=")
location 块里必须手动检查过期与签名有效性
Nginx 不自动拒绝已过期链接,$secure_link_expires 变量仅在 secure_link 成功解析时才被赋值,且它只是原样透传 e 参数值(字符串),需显式转为数字并与 $time 比较。
- 典型配置片段:
location /downloads/ {<br> secure_link $arg_st,$arg_e;<br> secure_link_md5 "$uri$arg_e my_secret_key";<br><br> if ($secure_link = "") {<br> return 403;<br> }<br><br> # 注意:$secure_link_expires 是字符串,需用 -gt 比较<br> if ($secure_link_expires !~ "^[0-9]+$") {<br> return 403;<br> }<br> if ($secure_link_expires -lt $time) {<br> return 403;<br> }<br><br> alias /var/www/files/;<br>} - 务必注意
secure_link_md5中的密钥要与生成签名时完全一致(包括空格),且$uri是带前缀的路径(如/downloads/report.pdf),不能漏掉/downloads/ - 若用
rewrite改写路径,$uri不会同步更新,应改用$request_uri或显式拼接
最容易被忽略的是 base64-url-safe 编码细节和 $secure_link_expires 的类型处理——它不是整数变量,直接参与算术比较会静默失败,必须先验证格式再比较。生产环境建议加日志记录 $arg_st 和 $secure_link_expires,方便排查签名不匹配或时间偏差问题。











