internal 是语义级闸门,仅允许 Nginx 内部跳转访问受保护资源,屏蔽外部直连;它不鉴权,但确保真实路径不暴露,需配合 auth_request、rewrite last 或 X-Accel-Redirect 实现安全下载链路。

用 internal 指令构建受保护的静态资源下载链路,核心不是“加个 internal 就安全了”,而是把它当作一道语义级闸门——只放行 Nginx 自己发起的内部跳转,彻底屏蔽所有外部直连。它本身不鉴权、不校验,但能确保真实文件路径永远不暴露在公网地址栏里。
明确 internal 的作用边界
internal 不是访问控制指令,也不检查 Token、IP 或 Referer。它的行为非常纯粹:
- 客户端直接请求
/_protected/report.pdf→ 立即返回 404(不是 403),路径“不存在” - 其他 location 通过
rewrite ... last、auth_request子请求或X-Accel-Redirect头触发跳转 → 请求被允许进入并读取文件 - 如果配置了
proxy_pass到外部服务,且该服务又返回 302,就会绕过internal—— 因为那是浏览器重定向,不是 Nginx 内部行为
标准两段式链路:对外入口 + 对内透传
真正可用的下载链路由两个明确分离的部分组成:
-
对外可见的入口 location:比如
/download/xxx.pdf,负责接收用户请求,并启动鉴权或参数校验 -
对内隐藏的透传 location:比如
location /_protected/ { internal; alias /var/www/protected/; },只响应内部跳转,不做任何逻辑,专注读文件
二者之间必须通过 Nginx 内部机制衔接,不能靠外部跳转。常见方式有三种:
- auth_request + 子请求鉴权:最推荐,由后端服务返回 2xx 才允许继续,失败直接拦截
-
rewrite last + 参数提取:适合简单场景,如从 URL 中提取时间戳和签名,用
if做基础校验后跳转 -
X-Accel-Redirect 响应头驱动:后端动态生成路径(如按用户 ID 分目录),只需返回头
X-Accel-Redirect: /_protected/user123/file.zip
关键配置细节避坑
很多配置失效,问题不出在逻辑,而在细节:
-
alias路径末尾斜杠必须和location末尾一致:若写location /_protected/,则alias必须以/结尾,如alias /data/files/; -
/auth鉴权子请求 location 也必须加internal,否则攻击者可绕过主入口直接调用 - 不要在
internallocation 中写proxy_pass到不可信后端;它应直接读磁盘文件,或只 proxy_pass 到可信的内部静态服务 - 用
root时路径拼接逻辑不同:root /data;+ 请求/_protected/a.pdf→ 实际读/data/_protected/a.pdf;而alias是替换前缀,更可控
配合后端实现完整闭环
如果采用 X-Accel-Redirect 方式,后端只需三件事:
- 校验权限后,不输出文件内容,只设置响应头:
X-Accel-Redirect: /_protected/20260507_invoice.pdf - 显式声明类型:
Content-Type: application/pdf - 强制下载并处理中文名:
Content-Disposition: attachment; filename="%E5%8F%91%E7%A5%A8.pdf"
Nginx 收到这些头后,会自动内部跳转到对应 internal location,读取文件并流式返回,整个过程对用户完全透明,且无法绕过。

















