由于Gravatar官方的域名在中国大陆地区访问并不是很顺畅,所以一般自建的博客都会将地址替换成代理的地址。
例如我在友情链接页面中提到的将Gavatar头像URL中 https://secure.gravatar.com/avatar/ 替换为以下任意一项代理地址
https://gravatar.loli.net/avatar/
https://sdn.geekzu.org/avatar/
https://dn-qiniu-avatar.qbox.me/avatar/
https://cravatar.cn/avatar/
这种方法虽然简单,但是可能面临不稳定的问题。因为这些第三方服务会有大量网站使用,或者服务提供者的服务器或者域名到期,都会导致无法访问。之前也有些开发者开发过typecho插件,可以缓存Gavatar头像到本地。但是目前为止,基本这类插件在几年前就处于无人维护状态了。
自建一个Gravatar代理
所以还是自建一个Gravatar代理相对靠谱(接下来的方法仅限服务器在海外)。
接下来以Nginx为例,首先在配置文件中添加:
location /avatar/ {
valid_referers none blocked .ushiromiya.com;
if ($invalid_referer) {
return 403;
}
proxy_pass https://secure.gravatar.com/avatar/;
proxy_set_header Host secure.gravatar.com;
}
便能实现指定的域名自动代理secure.gravatar.com的avatar目录下的头像文件,并且还能实现防盗链与盗链白名单的效果。在上述示例中,.ushiromiya.com 会匹配 ushiromiya.com 及其所有子域名,从而实现通用的防盗链规则。如果被盗链则返回403状态。
详细解释如下:
location /avatar/ {指定了匹配 URL 路径 /avatar/ 的位置(location)块开始。valid_referers none blocked .ushiromiya.com;设置防盗链规则。valid_referers指令定义了允许访问的有效引用者列表。这里的规则是允许没有引用者或者被直接阻止的引用者,以及以.ushiromiya.com结尾的引用者。if ($invalid_referer) {开始一个条件判断块,用于检查引用者是否无效。return 403;返回403错误码Forbidden,表示防盗链验证失败。proxy_pass https://secure.gravatar.com/avatar/;将请求代理到目标地址https://secure.gravatar.com/avatar/。proxy_set_header Host secure.gravatar.com;设置代理请求的Host头部为secure.gravatar.com,以确保请求正确路由到目标服务器。
以上总体实现了对 /avatar/ 路径下的请求进行反向代理,并在防盗链验证失败时返回403错误状态码。
多个防盗链白名单
白名单如果要指定多个域名,则改为:
valid_referers none blocked .ushiromiya.com .example.com;
如果使用了Cloudflare的图片缓存功能,并且由于其他站点引用了反向代理头像而导致403错误页面也被缓存了,这是因为 Cloudflare缓存了错误响应。为了解决这个问题,可以在返回 403 错误时添加Cache-Control指令,指示Cloudflare 不缓存错误响应。可以将以下代码添加到反向代理的配置中:
location /avatar/ {
valid_referers none blocked .ushiromiya.com;
if ($invalid_referer) {
add_header Cache-Control "no-store";
return 403;
}
proxy_pass https://secure.gravatar.com/avatar/;
proxy_set_header Host secure.gravatar.com;
}
上述配置在返回403错误时添加了Cache-Control头部,其中设置了 "no-store",指示Cloudflare不缓存该响应。
防盗链还可以通过正则表达式匹配检查$http_referer是否满足条件,如果不满足,则执行相应的操作:
if ($http_referer !~* ^https?://([^/]+\.)?ushiromiya\.com(:\d+)?$) {
add_header Cache-Control "no-store";
return 403;
}
在这里,使用了一个正则表达式来检查 $http_referer,它匹配不符合以下条件的请求来源:
^https?://:匹配以http://或https://开头的字符串([^/]+\.)?:匹配一个或多个非斜杠字符,后跟一个点号,这部分表示可选的子域名,它允许零个或多个非斜杠字符,以及一个点号ushiromiya\.com:匹配确切的字符串ushiromiya.com字符串(:\d+)?:匹配冒号后跟一个或多个数字字符的字符串,表示可选的端口号部分,它允许一个冒号和一个或多个数字字符
综合起来,该正则表达式的作用是匹配来自ushiromiya.com及其子域名的请求来源。
因此,如果请求的来源不匹配上述正则表达式,即不是以 https?://([^/]+\.)?ushiromiya.com\.com(:\d+)? 开头的来源,那么就会触发防盗链条件,返回 403 Forbidden 错误。从而满足来源ushiromiya.com及其子域名才被允许访问资源,其他来源将被禁止访问。

Nginx缓存头像
除此之外,使用Nginx进行反向代理时,Nginx本身也可以有缓存能力。通过在配置中启用代理缓存,Nginx可以缓存从目标服务器返回的响应,并在后续的请求中直接返回缓存的响应,而无需再次访问目标服务器。
proxy_cache cache_one;
proxy_cache_key $host$uri$is_args$args;
proxy_cache_valid 200 304 301 302 1d;
其中
proxy_cache指定了一个缓存区名称,例如cache_one。proxy_cache_key指定了缓存键的组成,这里使用了$host$uri$is_args$args,它包含了请求的主机、URI 以及查询参数。proxy_cache_valid指定了哪些 HTTP 响应状态码应该被缓存,以及缓存的过期时间。其中状态码 200、304、301、302 都将被缓存,并设置了1天的过期时间。
这些状态码中:
- 200 正常响应
- 304 未修改,缓存不变(当客户端发送带有缓存验证的请求时,如果服务器认为客户端的缓存副本仍然有效,服务器会返回 304状态码,表示客户端可以继续使用其缓存副本,而无需重新下载)
- 302 临时重定向(当服务器希望临时将请求重定向到另一个URL时,可以返回302状态码。客户端在收到302响应后会跟随重定向,并向重定向的URL发送新的请求)
- 301 永久重定向(当服务器希望永久将请求重定向到另一个URL时,可以返回301状态码。与302类似,客户端在收到301响应后会跟随重定向,并向重定向的URL发送新的请求。不同之处在于,301响应表示该重定向是永久性的,客户端应该将请求的资源URL更新为重定向的URL)
缓存时间也可以是不同单位,s(秒)m(分钟)h(小时)w(周)。
这样配置后,Nginx会根据指定的缓存键将响应存储在缓存区中,并在后续的请求中直接返回缓存的响应,而无需再次访问目标服务器,这有助于提高性能并减轻因为大量请求Gravatar服务器的负载。
Nginx缓存大小和空间判断
设置缓存之后,又会有一个问题,当大量不同的Gravatar文件被请求时,可能会导致缓存空间的消耗。为了控制缓存空间的使用,可以用如下的方式限制:
- 限制单个文件的最大缓存尺寸:可以使用
proxy_cache_max_size指令来设置单个文件的最大缓存尺寸。如果超过该尺寸,将不会缓存该文件,并将其视为未缓存的请求。例如proxy_cache_max_size 10m就是来限制单个文件的最大缓存为10M。 - 限制所有代理的总缓存量:可以使用
proxy_cache_path指令中的max_size参数来限制整个代理缓存的总容量。该参数指定缓存目录的最大大小。当缓存空间达到该限制时,Nginx会根据缓存策略选择要替换的缓存项。例如proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=100m来限制代理缓存的总容量为100M。
其中:
proxy_cache_path指定代理缓存的路径和配置。/path/to/cache代理缓存的存储路径,根据实际情况替换levels=1:2可选参数,指定缓存目录的层级结构,缓存目录的层级结构是1级目录和2级目录,这个参数的目的是将缓存文件分散到不同的子目录中,以提高文件查找和性能keys_zone=my_cache:10m这是一个用于存储缓存键和元数据的内存区域(shared memory),my_cache是缓存区域的名称,根据需要自定义,10m是分配给该缓存区域的内存大小,这个内存区域将用于存储缓存项的键和元数据,以加快缓存查找的速度max_size=100m这是一个可选的参数,用于指定代理缓存的最大总容量,这里代表代理缓存的最大总容量为100MB
所以以上所有配置合在一起可以写成:
location /avatar/ {
valid_referers none blocked .ushiromiya.com; # 设置合法的Referer
if ($invalid_referer) {
add_header Cache-Control "no-store"; # 添加Cache-Control头部
return 403; # 验证失败返回403错误
}
proxy_pass https://secure.gravatar.com/avatar/; # 反向代理到Gravatar
proxy_set_header Host secure.gravatar.com; # 设置代理请求的Host头部
proxy_cache_valid 200 304 301 302 1d; # 设置缓存有效期
proxy_max_temp_file_size 10m; # 设置临时文件的最大大小
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=100m; # 设置代理缓存路径和容量限制
}
注意:这个设置只适用于限制Nginx创建的临时文件的大小,并不会限制代理的文件尺寸。如果源服务器返回的文件大小超过设定的限制,Nginx仍然会接收和代理该文件,只是不会将其写入临时文件进行缓存。
如果需要完全限制代理的文件大小,例如限制源服务器返回的文件大小,就需要使用其他的方法去处理,这里就不展开了。
更可靠的方法,是通过Gravatar参数判断文件基于像素的尺寸,来选择是否拒绝代理,下面我会讲到相关方法。
Gravatar头像等级判断
Gravatar的头像也区分等级,分别分为以下等级:
- G: suitable for display on all websites with any audience type.
- PG: may contain rude gestures, provocatively dressed individuals, the lesser swear words, or mild violence.
- R: may contain such things as harsh profanity, intense violence, nudity, or hard drug use.
- X: may contain hardcore sexual imagery or extremely disturbing violence.
翻译过来就是
- G:适合在任何网站上展示给任何类型的受众。 (General Audience)
- PG:可能包含粗鲁的手势、穿着挑逗性的人、轻微的脏话或轻微的暴力。 (Parental Guidance Suggested)
- R:可能包含粗俗语言、剧烈的暴力、裸露或硬性毒品使用等内容。 (Restricted)
- X:可能包含露骨的性描写或极度令人不安的暴力。 (Explicit)
有的时候可能不希望一些不好的头像进行反向代理,影响自己域名,可以选择在代理时对等级参数进行过滤。通常Gravatar的URl为:
https://secure.gravatar.com/avatar/1e4b72c9f00126cbd1c31d4254165d2e?s=200&r=G&d=https://img.ushiromiya.com/Default-Gravatar.jpg
使用正则表达式来匹配$args变量中的等级参数r,如果等级不是G,则返回403 Forbidden错误,并设置Cache-Control头部为no-store,防止被缓存。
if ($args !~* "(^|&)r=G(&|$)") {
add_header Cache-Control "no-store";
return 403;
}
如果要同时满足两个等级,如G和PG,则写成
if ($args !~* "(^|&)r=(G|PG)(&|$)") {
add_header Cache-Control "no-store";
return 403;
}
完整的写法就可以如下:
location /avatar/ {
valid_referers none blocked .ushiromiya.com;
if ($invalid_referer) {
add_header Cache-Control "no-store";
return 403;
}
if ($args !~* "(^|&)r=(G|PG)(&|$)") {
add_header Cache-Control "no-store";
return 403;
}
proxy_pass https://secure.gravatar.com/avatar/;
proxy_set_header Host secure.gravatar.com;
proxy_cache_valid 200 304 301 302 1d;
proxy_max_temp_file_size 500k;
}
限制可代理图片尺寸(像素)
一般Gravatar有非常多种尺寸,请求哪种尺寸的图片纯粹是靠参数s=数字或者size=数字来实现。如果你输入很大的数字,请求到的图片也会很大。也可以同时限制可代理的最大尺寸。
在这里我就用不能大于600px举例:
if ($arg_s !~* "^(?!0+$)[1-5]?[0-9]{1,2}$|^600$") {
add_header Cache-Control "no-store";
return 403;
}
在这段正则中,对于数字1到600,不能直接使用[1-600]的形式进行匹配,这样的表达式会被解释为匹配数字1到6或0。要匹配数字1到600,需要使用更复杂的正则表达式。
目前我用的方法是先通过使用零宽度负预测先行断言(?!0+$)来排除只包含零的情况,然后使用[1-5]?[0-9]{1,2}来匹配1到599之间的数字,最后使用^600$匹配600。这样可以确保$s的值在1到600之间,而不匹配大于600的数字。如果请求中的s或size参数不符合范围1-600,就会触发条件判断,返回403 Forbidden错误。
零宽度负预测先行断言(zero-width negative lookahead assertion)详细说明:
在正则表达式中,零宽度负预测先行断言是一种特殊的语法结构,用于在匹配内容时向前查看,而不会真正匹配任何字符。它可以用来在匹配之前排除某些模式。
(?!0+$):(?!...)表示零宽度负预测先行断言的开始,0+$匹配一个或多个零并以字符串结束,(?!0+$)表示排除只包含零的情况。[1-5]?[0-9]{1,2}:[1-5]?表示数字1到5出现零次或一次,[0-9]{1,2}表示数字0到9出现一次或两次。这个部分用于匹配1到599之间的数字。- 最后使用
|^600$来匹配数字600。
同理,如果要表示1-900,则改为:
if ($arg_s !~* "^(?!0+$)[1-9]?[0-9]{1,2}$|^900$") {
add_header Cache-Control "no-store";
return 403;
}
先用零宽度负预测先行断言(?!0+$)来排除只包含零的情况。然后使用[1-9]?[0-9]{1,2}来匹配1到899之间的数字,以及^900$来匹配数字900。
当然也存在更好的正则写法去表述,这里也不详细展开了。
注意:Gravatar支持的最大尺寸为2048px(过去是512px),然而通常用户上传的原始图像尺寸并没这么高。如果强行请求大尺寸文件,只会导致文件无意义变大、图像出现像素化。数据来源于官方文档
重新启动Nginx服务
配置完成后,重新启动Nginx服务以应用新的配置。
sudo service nginx restart
如果支持systemctl,则使用
sudo systemctl restart nginx
替换Typecho所有评论头像地址
参考这篇:《Typecho一些奇奇怪怪但是好用的设置》
以上,完工。
这个技术可以,会经常遇到。我这反向代理到内网穿透的443端口 怎么就不行呢
这个可以有,我也用上了
首先是你得有一个海外服务器,先收藏了。
太真实了 (/ω\)
这么一说我才意识到,你网站竟然是双备案齐全
不错不错,博友圈也用到了这个。