You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Nginx实现URI仅限首个请求IP访问(secure_link模块受限场景)

实现单个URI仅限首个请求IP访问的解决方案

针对你这个小型视频CDN遇到的问题——因为客户端到请求路由器和边缘服务器的remote_address不一致,没法用Nginx的secure_link模块按IP限制,要实现单个URI仅限首个请求它的IP访问,我这边有一套基于Nginx+Redis的落地方案,也给你补充下备选思路:

核心思路

既然没法在生成token时提前绑定客户端IP,那我们就把IP绑定的逻辑放到边缘服务器(Nginx)侧来做动态处理:当第一个请求某个URI的请求到达时,我们把这个URI和请求IP绑定并存储起来;后续所有请求这个URI的请求,只要IP和存储的不一致就直接拒绝。同时还要和原有的expires过期逻辑联动,避免无效记录占用资源。

具体落地方案(Nginx + Redis)

这个方案适合单台或多台边缘服务器的场景,多台的话用Redis做共享存储来同步绑定记录,单台也可以用Redis或者Nginx内存缓存,不过Redis更可靠。

1. 前置准备

首先确保你的Nginx安装了ngx_http_redis_module和ngx_http_set_misc_module,如果用OpenResty的话这些模块都是自带的,会更省心。另外需要部署一个Redis实例(多边缘的话建议用Redis集群)。

2. Nginx配置示例

下面是完整的配置逻辑,我给你加了详细注释:

http {
    # 配置Redis连接信息
    redis_server 127.0.0.1 6379; # 换成你的Redis地址和端口
    # redis_password your_redis_pwd; # 如果Redis设了密码就打开这行

    server {
        listen 80;
        server_name your_edge_domain;

        # 匹配你的视频URI格式:/videos/{token}/{expires}/{视频路径}
        location ~ ^/videos/([^/]+)/([^/]+)/(.+)$ {
            # 从URI里提取关键参数
            set $token $1;
            set $expires $2;
            set $video_path $3;
            # 构造唯一的Redis键:用token+expires+视频路径组合,确保每个URI对应唯一的键
            set $redis_key "cdn_uri_lock:$token:$expires:$video_path";

            # 第一步:从Redis获取这个URI已经绑定的IP
            redis_get $redis_key;
            set $bound_ip $redis_reply;

            # 第二步:如果还没绑定IP,就把当前请求的IP存到Redis,同时设置过期时间和URI的expires同步
            if ($bound_ip = "") {
                # 用Lua脚本计算剩余过期时间,避免Nginx原生指令的局限性
                rewrite_by_lua_block {
                    local expires_ts = tonumber(ngx.var.expires)
                    local now = ngx.time()
                    local ttl = expires_ts - now
                    if ttl <= 0 then
                        # URI已经过期,直接返回403
                        ngx.exit(ngx.HTTP_FORBIDDEN)
                    end
                    ngx.var.ttl = ttl
                }
                # 将当前IP存入Redis,过期时间和URI的expires保持一致
                redis_setex $redis_key $ttl $remote_address;
            }

            # 第三步:如果已经绑定了IP,对比当前请求IP和绑定IP,不一致就拒绝
            if ($bound_ip != "" and $bound_ip != $remote_address) {
                return 403;
            }

            # 第四步:正常转发到源服务器或者本地存储
            proxy_pass http://your_source_server/$video_path;
            # 如果视频存在边缘本地,就换成:
            # root /path/to/your/video/storage;
            # try_files /$video_path =404;
        }
    }
}

3. 关键细节说明

  • Redis键的唯一性:用token:$expires:$video_path组合作为键,因为这三个部分完全确定了一个唯一的访问URI,不会和其他URI冲突。
  • 过期时间同步:Redis键的过期时间和URI中的expires时间戳保持一致,这样URI过期后,Redis会自动删除这条绑定记录,不用手动清理。
  • Lua脚本的作用:Nginx原生指令没法直接计算时间差,所以用Lua来判断URI是否已经过期,避免给过期的URI做无效的IP绑定。
  • 多边缘服务器场景:只要所有边缘服务器连接同一个Redis集群,就能保证URI的绑定IP在所有节点中同步,不会出现不同节点允许不同IP访问的情况。

备选方案(请求路由器侧优化)

如果你能修改请求路由器的代码,也可以调整token生成逻辑:生成URI时附带一个一次性的临时标识,然后在边缘服务器侧,第一个请求用这个标识绑定IP,后续请求需要同时验证标识和IP。不过这个方案需要改动请求路由器的代码,相对复杂一些,适合能完全控制请求路由器开发的场景。

注意事项

  • 性能影响:Redis的读写操作非常轻量,对边缘服务器的性能几乎没有影响,但要确保Redis和边缘服务器在同一个机房,避免网络延迟。
  • 异常降级:如果Redis宕机,可以考虑临时降级(比如暂时允许所有IP访问,或者直接返回503),避免影响正常业务。
  • 安全补充:别忘了保留原有的token验证逻辑,防止恶意构造大量URI来占用Redis资源。

内容的提问来源于stack exchange,提问作者Nguyễn Tuấn Nghĩa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:01:37