使用Nginx代理多张网页图片,解决502和403问题

🎉 前言

要想代理多张网页图片,首先当然得能够代理一张网页图片,相信大家看了我之前的文章,已经能够代理一张网页图片了,这里我就简单放一下配置:

location /img{
            proxy_set_header Referer "http://i1.hdslb.com";
            proxy_pass http://i1.hdslb.com/bfs/archive/66aef0f84042cf7e56e0ab9528f81346a8ffe1ca.jpg;
            proxy_ssl_server_name on;
            proxy_set_header Host i1.hdslb.com; 
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            # 添加 CORS 头,允许所有来源
            proxy_hide_header Access-Control-Allow-Origin;
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Methods "GET, OPTIONS";
            add_header Access-Control-Allow-Headers "Authorization,Content-Type";
        }

但是现在有一个问题,就是直接将本地的代理地址输入到浏览器地址栏,能够显示网页图片,但是如果将这个url地址嵌入到前端的代码(img的src属性),就会显示403 Forbidden,这个问题有些让我丈二和尚摸不着头脑,于是我将这个情况告诉了chatgpt,回答中有几个点,其中有个原因是:防盗链机制(Referer 检查),即很多图片服务器会根据 Referer 来判断请求的来源。如果 Referer 不符合预期,比如从 localhost 或 127.0.0.1 发出的请求,服务器可能认为这是未经授权的访问,因而返回 403 错误。我看了一下浏览器控制台,referer还真是127.0.0.1,于是根据ai回复,改成了 "http://example.com"的形式; 完整语句如下:

proxy_set_header Referer "http://example.com";

或者直接将http://example.com 替换成空字符串也可以。这样,403问题就解决了,我们便可以顺利的在前端代码中使用网页图片路径了。

🎉 进阶,如何代理多张图片

首先,明确一下问题,刚才我们通过代理http://i1.hdslb.com/bfs/archive/66aef0f84042cf7e56e0ab9528f81346a8ffe1ca.jpg;
实现了将单张图片代理,现在我们是不是只要将archive路径后面的内容动态替换就可以了,那么该怎么实现动态替换呢?其实我们可以使用正则表达式和nginx的变量来实现这一点,先贴下配置:

server{
        listen 8005; # 第一处
        server_name i1.hdslb.com; # 第二处
        resolver 8.8.8.8;
        #resolver 127.0.0.1;
        location / {
            root   html;
            index  index.html index.htm;
        }

        location ~ ^/img/(.+)$ {
            # 提取 URL 中的图片路径部分
            set $img_path $1;

            # 设置代理目标 URL,这里使用了变量
            proxy_pass https://i1.hdslb.com/bfs/archive/$img_path;
            #https://i1.hdslb.com/bfs/archive/66aef0f84042cf7e56e0ab9528f81346a8ffe1ca.jpg

            # 代理请求时设置的头部
            proxy_set_header Referer "http://i1.hdslb.com";
            
            proxy_set_header Host i1.hdslb.com;
            proxy_ssl_server_name on;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            # 添加 CORS 头,允许所有来源
            proxy_hide_header Access-Control-Allow-Origin;
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Methods "GET, OPTIONS";
            add_header Access-Control-Allow-Headers "Authorization,Content-Type";
        }
    }

首先第一处比较重要的一点就是我们将location后面的路径替换成了一个正则表达式,代表匹配以 /img/ 开头并以任意字符结尾的字符串,例如 /img/pic.jpg 或 /img/another-image.png。捕获的部分是 /img/ 之后的所有内容,这里我们不妨以 pic.jpg 为例。

第二处重要的点就是 set $img_path $1;这里的 $1就是我们刚才匹配的字符串,也就是“pic.jpg”,并将其赋值给 $img_path 。

第三处重要的点就是我们将原先代理一张图片时候的路径http://i1.hdslb.com/bfs/archive/66aef0f84042cf7e56e0ab9528f81346a8ffe1ca.jpg;
替换成了一个由变量拼接而成的动态路径proxy_pass https://i1.hdslb.com/bfs/archive/$img_path;

这样,nginx就可以实现根据请求的内容来动态代理多张图片。

再看看前端应该怎么组织代码,首先,我们假设这样一个场景,假设我们已经可以获得网页图片的url,但是由于跨域问题,我们需要通过nginx代理才能在代码中使用,而原始网页图片的url格式如下:

http://i1.hdslb.com/bfs/archive/66aef0f84042cf7e56e0ab9528f81346a8ffe1ca.jpg

我们此时要干的就是要使用字符串切割的方法,获取到末尾的66aef0f84042cf7e56e0ab9528f81346a8ffe1ca.jpg部分,实现原理如下:

element.pic = (element.pic).substring((element.pic).lastIndexOf('/') + 1)

大家可以参考一下,其中element是请求得到的data,pic属性就是网页图片路径。

最后,再使用本地代理的路径去拼接前端请求路径,如:

http://127.0.0.1:8004/img/ + element.pic

至此,我们就可以将这段路径写在前端的src属性中,nginx会根据elemnet.pic的不同响应不同的内容。

让我们来总结一下,前端请求路径http://127.0.0.1:8004/img/ + element.pic会被nginx截取element.pic部分,也就是诸如 66aef0f84042cf7e56e0ab9528f81346a8ffe1ca.jpg的形式,然后nginx会将截取部分赋值给变量$img_path,最后将这个变量拼接到公共前缀http://i1.hdslb.com/bfs/archive/后面,得到完整的路径,如http://i1.hdslb.com/bfs/archive/66aef0f84042cf7e56e0ab9528f81346a8ffe1ca.jpg

🎉 补充

其实在配置过程中我还遇到一个非常吊诡问题,就是一开始我并没有使用解析器resover,或者说使用的resolver是127.0.0.1,具体到配置上就是,我没有使用

 resolver 127.0.0.1;

这一句配置,又或者说我可能使用过,但结果就是我曾经配置成功过一次,但后来我尝试了其他方法无果后,想要重现那次成功的配置,但是无论我加不加这一句,配置都会失败,而且我能保证我的其他配置和成功那次一模一样,因为我将server模块的配置做了备份,但很不幸的是 resolver 127.0.0.1;这句配置在server模块之外,这也是为什么我不确定有没有使用过这句配置。但朋友们,这不是重点,因为我在后续更改的配置的时候无论加没加上这句,配置都无法重现当时的成功,我差点没一口老血喷出来,难到有鬼不成!

最后没有办法,我又想到了nginx错误日志,打开一看,赫然出现这么一条报错

[error] 61134#0: *5 no resolver defined to resolve i1.hdslb.com, client: 127.0.0.1, server: i1.hdslb.com

所以问题就是出在resolver的配置上,于是我将resolver设置成谷歌的DNS解析,具体配置如下:

resolver 8.8.8.8;  # Google DNS 服务器

重启一下nginx,问题解决,配置成功了。因此如果读者在前面两节当中依旧没有配置好,可以尝试加上这一句。

所以那一次配置成功到底是什么原因,是偶然吗?我想到一种解释,可能是 Nginx 或操作系统的 DNS 缓存已经解析并存储了目标服务器的 IP 地址,代理请求时没有遇到 DNS 问题,但是 Nginx 或系统的 DNS 缓存过期后,没有有效的 DNS 服务器进行解析,导致代理失败。姑且这么认为吧,总不会是精诚所至,金石为开吧,我的诚意打动了上天,哈哈。

今天的分享和总结到这里差不多就结束了,我们下一期再见吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋窗7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值