免费版的cloudflare只有三条缓存规则,对于一般网站来说勉强够用了,例如后台链接的缓存策略设置为绕过,某个时常更新的页面缓存策略设置为1个小时,其它不经常更新的页面缓存设置为半个月,本来我制作的网站也是按照这个策略正常运行的,直到发现最近很少收到国际友人的邮件,尤其是在上了CDN之后,一封都没有!抱着试试的心态自己在网站上填写了“联系我们”的表单,结果发现还真有问题,返回了403代码!
403是客户端有错误,导致禁止访问,当时就有种预感跟csrf_token有关,网站采用的是django框架,在配置文件中,存在一个CsrfViewMiddleware的中间件,为了防止跨站请求伪造,所以在以post请求提交form表单时,必须要验证随机数csrf_token,而由于缓存的存在,这个随机数无法通过后端的验证,所以一直返回403(但是在清除缓存之后,还是可以暂时正常提交表单,因为csrf验证通过)。
尝试按照django官网的教程,使用csrf_exempt装饰器标记视图,使其不受CSRF保护,官网对于csrf_protect的解释:
This decorator marks a view as being exempt from the protection ensured by the middleware.
但是在我实际测试中,下面这种方法不可行
from django.views.decorators.csrf import csrf_exempt
class ContactUsView(View):
...
@csrf_exempt
def post(cls, request):
...
但也有可能是我的方法不对,因为django中装饰类,似乎和函数不同,需要装饰dispatch函数,这个后面再来测试吧。
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
总之,就是因为这个原因导致联系我们的表单需要占据一个缓存“绕过”的策略,导致网站时常更新的那个页面策略随大流也缓存了半个月,这可真是太不友好了,现在就剩两种解决办法,一种很简单,花钱!升级下一个版本pro就会多达20个(这个数量不确定)缓存规则;或者辛苦点每隔2个小时清除一次该页面的缓存。
查阅CF的官方文档,确实提供了API方便用户清理缓存,主要有三种,分别是清除所有缓存文件“Purge All files”,单个缓存文件“Purge Files by URL”,还有第三种,根据域名、前缀、缓存标签来清除“Purge Files by Cache-Tags, Host or Prefix”。第一种,全站清缓存,没啥好写的,在CF控制台点一下就可以,没必要利用API;第三种方法只能用于企业版,由于价格原因我也不会去用它,没去了解;主要就是第二种清除单个url可以解决我的问题。
先来看下CF的官方文档,第一眼看的一脸懵逼,后来琢磨了下,算是搞清楚了,首先这个功能FREE版本是可用的,要求该账号有#cache_purge:edit权限,使用的方法是发送POST请求,然后需要一个Zone ID标识符(identifier)
首先打开个人资料,选择API令牌中的Global API Key,这个钥匙的作用如下,其实就是在请求时放在头部的X-Auth-Key
The Global API Key is an all purpose token that
can read and edit any data or settings
that you can access inthe dashboard.
获知到这个X-Auth-Key之后,找个可以使用curl请求的命令行窗口,输入以下请求向CF请求你账号的权限有哪些
curl -X GET "https://api.cloudflare.com/client/v4/zones" \
-H "X-Auth-Email: youraccount@gmail.com" \
-H "X-Auth-Key: Global API Key" \
-H "Content-Type: application/json"
可以看到我的账号permissions确实存在#cache_purge:edit权限,(当然,返回的json内容很多,可以将其格式化后自行了解)
那么接下来的操作就没有问题了,找到自己域名下面的Zone ID
然后使用如下命令清除缓存链接,下面的data后面其实是一行,为了看的清除,我将其分了几行,将下方的zone_id、账号、全局钥匙、实际想清除缓存的链接填入进去,就可以正常使用了
curl -X POST "https://api.cloudflare.com/client/v4/zones/zone_id/purge_cache" \
-H "X-Auth-Email: youraccount@gmail.com" \
-H "X-Auth-Key: Global API Key" \
-H "Content-Type: application/json" \
--data '{"files":
["https://will_purge_cache_page.com",
{"url":"https://domain.com",
"headers":{"Origin":"https://www.cloudflare.com","CF-IPCountry":"US","CF-Device-Type":"desktop"}
}]}'
在这里,我得吐槽一下CF,首先清除缓存的是紧接着files后面列表的第一个链接will_purge_cache_page.com,无法在列表中增加多个链接,这就意味着如果想要清除多个链接,必须发送多次post请求;第二,这个url参数对应的键值对https://domain.com没搞清楚是什么作用,必须要是同一个域名下,但是实测中不会被清除缓存,而且必须存在,否则清除单个页面缓存会报错!
另外:
文档路径(https://developers.cloudflare.com/cache/how-to/purge-cache/)
关于开发者中心里面存在的清除缓存教程“Purge cache key resources”,要求的是zone_tag,并非Zone ID,所以这种方法是无法使用的。
接下来的事情就简单了,在服务器上写一个shell脚本,然后写入到crontab文件中,定时运行即可自动清除指定的页面缓存了。
00 */2 * * * root /bin/bash /srv/purge_cache.sh > /srv/purge.log
当然,如果页面上想要加入“清除缓存”的功能也很简单,这样可以避免前往CF清除缓存。
原理:在web中,页面的访问会带有refer,因此,我们只需要为登录授权用户在前端添加一个清除的按钮,当在点击“清除缓存”按钮时,向指定的url发送一个refer链接过去就可以把该页的缓存清除了。使用这种方法就一定要判断用户是否登录,未登录则不让其访问该页面!
Django示例代码:
#urls.py
urlpatterns = [
path("purge-page", PurgePageCache.as_view(),name="purgePage"),
]
# base.html
{% if request.user.is_authenticated %}
用户已登陆。
<div class="purgePage">
<a href="{% url 'purgePage' %}">Purge This Page Cache</a>: {{request.build_absolute_uri }}
</div>
{% endif %}
#views.py
import requests
class PurgePageCache(View):
def get(self,request):
# 判断用户是否登录
if request.user.is_authenticated:
zong_id = "zong_id "
headers = {
'X-Auth-Email': 'X-Auth-Email',
'X-Auth-Key': 'X-Auth-Key',
'Content-Type': 'application/json',
}
my_refer = request.META.get('HTTP_REFERER')
# print(my_refer)
cf_url = f"https://api.cloudflare.com/client/v4/zones/{zong_id}/purge_cache"
data = '{"files":["%s",{"url":"%s","headers":{"Origin":"https://www.cloudflare.com","CF-IPCountry":"US","CF-Device-Type":"desktop"}}]}'%(my_refer,my_refer)
requests.post(cf_url, headers = headers, data = data)
return HttpResponse(f"Purge {my_refer} ")
else:
return redirect(reverse('index'))
备注:排除某个文件夹下方所有的png图片进行打包命令
tar -zcvf test230527.tar.gz --exclude test/media/*.png test/
参考:# 跨站请求伪造
https://zhuanlan.zhihu.com/p/22521378
# django csrf
https://docs.djangoproject.com/en/4.0/ref/csrf/
# 基于类的装饰
https://docs.djangoproject.com/zh-hans/4.0/topics/class-based-views/intro/#id1
# CF help
https://api.cloudflare.com/#zone-purge-all-files