使用 Vercel 加速 Gravatar 和 G Analytics

Vercel Edge Functions 类似于 Cloudflare Workers,可以在边缘节点运行 js 程序,目前 Edge Functions 没有限制调用次数

Google Analytics 加速

我一直以来都在使用 Google Analytics 统计自己的博客和几个网站访问情况。但是一个 gzip 以后都还有 45KB 大小的 analytics.js、Cache-Control 还只有 7200 秒;Google 国内的数据中心会被抽风不说,www.google-analytics.com 域名早就上了各个广告屏蔽软件的黑名单。

采用博主 Sukka 的 Cloudflare Workers 版本改造而成,前端使用方法见:原项目 GitHub

analytics.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
const AllowedReferrer = ["cuojue.org"]; // multiple domains is supported in array format

export const config = {
runtime: "experimental-edge",
};

export default function (req) {
return response(req);
}

async function senData(request, url, uuid, user_agent) {
const encode = (data) => encodeURIComponent(decodeURIComponent(data));

const getReqHeader = (key) => request.headers.get(key);
const getQueryString = (name) => url.searchParams.get(name);

const reqParameter = {
headers: {
Host: "www.google-analytics.com",
"User-Agent": user_agent,
Accept: getReqHeader("Accept"),
"Accept-Language": getReqHeader("Accept-Language"),
"Accept-Encoding": getReqHeader("Accept-Encoding"),
"Cache-Control": "max-age=0",
},
};

const pvData = `tid=${encode(getQueryString("ga"))}&cid=${uuid}&dl=${encode(
getQueryString("dl")
)}&uip=${getReqHeader("CF-Connecting-IP")}&ua=${user_agent}&dt=${encode(
getQueryString("dt")
)}&de=${encode(getQueryString("de"))}&dr=${encode(
getQueryString("dr")
)}&ul=${getQueryString("ul")}&sd=${getQueryString("sd")}&sr=${getQueryString(
"sr"
)}&vp=${getQueryString("vp")}`;

const perfData = `plt=${getQueryString("plt")}&dns=${getQueryString(
"dns"
)}&pdt=${getQueryString("pdt")}&rrt=${getQueryString(
"rrt"
)}&tcp=${getQueryString("tcp")}&srt=${getQueryString(
"srt"
)}&dit=${getQueryString("dit")}&clt=${getQueryString("clt")}`;

const pvUrl = `https://www.google-analytics.com/collect?v=1&t=pageview&${pvData}&z=${getQueryString(
"z"
)}`;
const perfUrl = `https://www.google-analytics.com/collect?v=1&t=timing&${pvData}&${perfData}&z=${getQueryString(
"z"
)}`;

const response1 = await fetch(pvUrl, reqParameter);
const response2 = await fetch(perfUrl, reqParameter);
// To sent data to google analytics after response id finished
await response1.waitUntil();
await response2.waitUntil();
}

async function response(request) {
const url = new URL(request.url);

const getReqHeader = (key) => request.headers.get(key);

const Referer = getReqHeader("Referer");
const user_agent = getReqHeader("User-Agent");
const ref_host = (() => {
try {
return new URL(Referer).hostname;
} catch (e) {
return "";
}
})();

let needBlock = false;

needBlock =
!ref_host ||
ref_host === "" ||
!user_agent ||
!url.search.includes("ga=UA-")
? true
: false;

if (
typeof AllowedReferrer !== "undefined" &&
AllowedReferrer !== null &&
AllowedReferrer
) {
let _AllowedReferrer = AllowedReferrer;

if (!Array.isArray(AllowedReferrer)) _AllowedReferrer = [_AllowedReferrer];

const rAllowedReferrer = new RegExp(_AllowedReferrer.join("|"), "g");

needBlock = !rAllowedReferrer.test(ref_host) ? true : false;
console.log(_AllowedReferrer, rAllowedReferrer, ref_host);
}

if (needBlock) {
return new Response("403 Forbidden", {
headers: { "Content-Type": "text/html" },
status: 403,
statusText: "Forbidden",
});
}

const getCookie = (name) => {
const pattern = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
const r = (getReqHeader("cookie") || "").match(pattern);
return r !== null ? unescape(r[2]) : null;
};

const createUuid = () => {
let s = [];
const hexDigits = "0123456789abcdef";
for (let i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = "-";

return s.join("");
};

const _uuid = getCookie("uuid");
const uuid = _uuid ? _uuid : createUuid();

senData(request, url, uuid, user_agent, Referer);

// Return an 204 to speed up: No need to download a gif
let response = new Response(null, {
status: 204,
statusText: "No Content",
});

if (!_uuid)
response.headers.set(
"Set-Cookie",
`uuid=${uuid}; Expires=${new Date(
new Date().getTime() + 365 * 86400 * 30 * 1000
).toGMTString()}; Path='/'; SameSite=None; Secure`
);

return response;
}

Gravatar 加速

由于众所周知的原因,Gravatar 在国内访问速度不佳,并且经常被阻断。
加上昨天国内知名的 Gravatar 代理 Cravatar.cn 也宕机了,大量博客头像显示 502 错误,所以还是做一个后备

原始代码来自知名的 Cloudflare Workers 反代项目,避免被墙,暂无链接指向

gravatar.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
export const config = {
runtime: "experimental-edge",
};

export default function (req) {
return fetchAndApply(req);
}

const upstream = "www.gravatar.com";

async function fetchAndApply(request) {
let response = null;
let url = new URL(request.url);

url.host = upstream;
let method = request.method;
let request_headers = request.headers;
let new_request_headers = new Headers(request_headers);
new_request_headers.set("Host", upstream);
new_request_headers.set("Referer", url.href);
let original_response = await fetch(url.href, {
method: method,
headers: new_request_headers,
});

let original_response_clone = original_response.clone();
let original_text = null;
let response_headers = original_response.headers;
let new_response_headers = new Headers(response_headers);
let status = original_response.status;

new_response_headers.set("access-control-allow-origin", "https://cuojue.org");
new_response_headers.set(
"Cache-Control",
"max-age=600, s-maxage=2592000, stale-while-revalidate"
);
new_response_headers.delete("link");

original_text = original_response_clone.body;

response = new Response(original_text, {
status,
headers: new_response_headers,
});

return response;
}

Vercel 上要怎么部署

以上两个文件丢进项目的 /api 目录,会自动识别为 Edge Functions

然后在项目的 vercel.json 配置一下重定向

1
2
3
4
5
6
{
"rewrites": [
{ "source": "/analytics", "destination": "api/analytics" },
{ "source": "/avatar/(.*)", "destination": "api/gravatar" }
]
}

如果担心 Vercel 的免费 100G 流量用完,可以在前面再套一层 Cloudflare 作为缓存,这样就拥有了无限流量+无限调用次数的双层 Buff

题外话

如果用了 cloudflare ,最好关闭 Argo Tiered Cache 功能,不知道它默认在哪里的数据中心回源,但是在 Vercel 的 ATA 网络显示,回源节点在 澳大利亚 悉尼……


使用 Vercel 加速 Gravatar 和 G Analytics
https://cuojue.org/read/deploy-gravatar-ga-in-vercel.html
作者
WeiCN
发布于
2022年11月2日
许可协议