惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Azure Blog
Microsoft Azure Blog
博客园_首页
Forbes - Security
Forbes - Security
WordPress大学
WordPress大学
P
Proofpoint News Feed
T
Threat Research - Cisco Blogs
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
Spread Privacy
Spread Privacy
D
Darknet – Hacking Tools, Hacker News & Cyber Security
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
P
Privacy International News Feed
A
About on SuperTechFans
T
Tailwind CSS Blog
I
InfoQ
S
Securelist
云风的 BLOG
云风的 BLOG
罗磊的独立博客
Recent Announcements
Recent Announcements
T
The Exploit Database - CXSecurity.com
B
Blog RSS Feed
V
Visual Studio Blog
Know Your Adversary
Know Your Adversary
The GitHub Blog
The GitHub Blog
Jina AI
Jina AI
腾讯CDC
Cyberwarzone
Cyberwarzone
有赞技术团队
有赞技术团队
AWS News Blog
AWS News Blog
博客园 - 【当耐特】
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
F
Full Disclosure
S
Secure Thoughts
博客园 - 司徒正美
J
Java Code Geeks
Y
Y Combinator Blog
Google Online Security Blog
Google Online Security Blog
GbyAI
GbyAI
N
News and Events Feed by Topic
Help Net Security
Help Net Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Project Zero
Project Zero
T
Tenable Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Tor Project blog
MyScale Blog
MyScale Blog
Scott Helme
Scott Helme
小众软件
小众软件
K
Kaspersky official blog

jdjwzx233的博客

jdjwzx233的博客 - jdjwzx233 | jdjwzx233-Blog = jdjwzx233的博客 网页自动翻译功能全攻略 - 网络 - jdjwzx233 使用Netlify搭建反向代理 - Netlify - 网络 - jdjwzx233 Edge在保留数据的情况下退出微软账户登录 - Edge - jdjwzx233 - 奇怪的知识 优秀作文分享——人间烟雨,感谢有你 - 作文 - 语文 - jdjwzx233 优秀作文分享——从未走远 - 作文 - 语文 - jdjwzx233 优秀作文分享——我,在浙里 - 作文 - 语文 - jdjwzx233 将ChatGPT部署到网站上 - Web - Flask - python - ChatGpt - 编程 - jdjwzx233 - 奇怪的知识 远程桌面控制器-用c++实现 - 远程桌面 - 编程 - c++ - jdjwzx233 - 奇怪的知识 用c++写图像界面软件(QT) - QT - 编程 - c++ - jdjwzx233 - 奇怪的知识 C++中常用的排序算法 - 算法 - 编程 - c++ - jdjwzx233 - 奇怪的知识 《简爱》每章概括+赏析 - 语文 - jdjwzx233 - 奇怪的知识 初中历史纪年表(超级完整) - 历史 - 社会 - 奇怪的知识 初中历史纪年表(超级完整) - 历史 - 社会 - 奇怪的知识 初中世界历史年代尺(大事年表) - 历史 - 社会 - jdjwzx233 - 奇怪的知识 初中世界历史年代尺(大事年表) - 历史 - 社会 - jdjwzx233 - 奇怪的知识 《聊斋志异》译文与赏析 - 语文 - jdjwzxAPI - 奇怪的知识 《聊斋志异》简介及读后感 - 语文 - jdjwzxAPI - 奇怪的知识 《水浒传》每章概括 - 语文 - jdjwzxAPI - 奇怪的知识 Git 常用命令 - jdjwzxAPI - Git - 奇怪的知识 联想小新为硬件保留2G内存的解决方法 - Windows - jdjwzx233 - 奇怪的知识 博客扩展功能 - jdjwzxAPI - 奇怪的知识 VS2022 VC++程序如何生成安装程序 | jdjwzx233-Blog = jdjwzx233的博客 Markdown超全教程 - 奇怪的知识 - Markdown jdjwzx233的博客 - jdjwzxAPI - jdjwzx233 Hello World - jdjwzxAPI - 奇怪的知识
搭建jsdelivr镜像(又拍云/nginx/cfwokers/Deno) - jsdelivr - 网络 - jdjwzx233
本文作者: jdjwzx233 @jdjwzx233的博客 · 2023-12-24 · via jdjwzx233的博客

jsDelivr —— 一个免费、快速、可靠的为 JS 和开源项目服务的 CDN。但是由于国内访问速度慢,加之 jsDelivr 备案被吊销和 DNS 污染常出现,国内使用了 jsDelivr 的站点常常出现访问速度慢的问题。
你可能会问,为什么不去使用别人的镜像站呢?俗话说 "自己动手,丰衣足食",自建镜像站可以让你更好地掌握自己的数据和服务。使用第三方镜像站存在一些潜在的风险和不确定性。首先,你无法确定第三方镜像站的可靠性和稳定性,它们可能在任何时候关闭或遭遇故障。这将对你的站点造成严重影响,可能导致服务中断。其次,第三方镜像站可能无法及时更新和同步你的站点内容,导致访问者无法获取最新的信息。最后,自建镜像站可以让你更加灵活地定制和管理自己的站点,满足特定需求和提供更好的用户体验。因此,建议在可行的情况下,考虑自建镜像站来确保数据的安全和可靠性。

2. 加速效果明显 (如图)

进入控制台的 CDN 管理界面 (https://console.upyun.com/services/cdn/), 并创建服务,然后配置服务 (具体参数如下)。

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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
'use strict'




const ASSET_URL = 'https://cdn.jsdelivr.net'

const JS_VER = 10
const MAX_RETRY = 1


const PREFLIGHT_INIT = {
status: 204,
headers: new Headers({
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS',
'access-control-max-age': '1728000',
}),
}






function makeRes(body, status = 200, headers = {}) {
headers['--ver'] = JS_VER
headers['access-control-allow-origin'] = '*'
return new Response(body, {status, headers})
}





function newUrl(urlStr) {
try {
return new URL(urlStr)
} catch (err) {
return null
}
}


addEventListener('fetch', e => {
const ret = fetchHandler(e)
.catch(err => makeRes('cfworker error:\n' + err.stack, 502))
e.respondWith(ret)
})





async function fetchHandler(e) {
const req = e.request
const urlStr = req.url
const urlObj = new URL(urlStr)
const path = urlObj.href.substr(urlObj.origin.length)

if (urlObj.protocol === 'http:') {
urlObj.protocol = 'https:'
return makeRes('', 301, {
'strict-transport-security': 'max-age=99999999; includeSubDomains; preload',
'location': urlObj.href,
})
}

if (path.startsWith('/http/')) {
return httpHandler(req, path.substr(6))
}

switch (path) {
case '/http':
return makeRes('请更新 cfworker 到最新版本!')
case '/ws':
return makeRes('not support', 400)
case '/works':
return makeRes('it works')
default:

return fetch(ASSET_URL + path)
}
}






function httpHandler(req, pathname) {
const reqHdrRaw = req.headers
if (reqHdrRaw.has('x-jsproxy')) {
return Response.error()
}


if (req.method === 'OPTIONS' &&
reqHdrRaw.has('access-control-request-headers')
) {
return new Response(null, PREFLIGHT_INIT)
}

let acehOld = false
let rawSvr = ''
let rawLen = ''
let rawEtag = ''

const reqHdrNew = new Headers(reqHdrRaw)
reqHdrNew.set('x-jsproxy', '1')



const refer = reqHdrNew.get('referer')
const query = refer.substr(refer.indexOf('?') + 1)
if (!query) {
return makeRes('missing params', 403)
}
const param = new URLSearchParams(query)

for (const [k, v] of Object.entries(param)) {
if (k.substr(0, 2) === '--') {

switch (k.substr(2)) {
case 'aceh':
acehOld = true
break
case 'raw-info':
[rawSvr, rawLen, rawEtag] = v.split('|')
break
}
} else {

if (v) {
reqHdrNew.set(k, v)
} else {
reqHdrNew.delete(k)
}
}
}
if (!param.has('referer')) {
reqHdrNew.delete('referer')
}


const urlStr = pathname.replace(/^(https?):\/+/, '$1://')
const urlObj = newUrl(urlStr)
if (!urlObj) {
return makeRes('invalid proxy url: ' + urlStr, 403)
}


const reqInit = {
method: req.method,
headers: reqHdrNew,
redirect: 'manual',
}
if (req.method === 'POST') {
reqInit.body = req.body
}
return proxy(urlObj, reqInit, acehOld, rawLen, 0)
}








async function proxy(urlObj, reqInit, acehOld, rawLen, retryTimes) {
const res = await fetch(urlObj.href, reqInit)
const resHdrOld = res.headers
const resHdrNew = new Headers(resHdrOld)

let expose = '*'

for (const [k, v] of resHdrOld.entries()) {
if (k === 'access-control-allow-origin' ||
k === 'access-control-expose-headers' ||
k === 'location' ||
k === 'set-cookie'
) {
const x = '--' + k
resHdrNew.set(x, v)
if (acehOld) {
expose = expose + ',' + x
}
resHdrNew.delete(k)
}
else if (acehOld &&
k !== 'cache-control' &&
k !== 'content-language' &&
k !== 'content-type' &&
k !== 'expires' &&
k !== 'last-modified' &&
k !== 'pragma'
) {
expose = expose + ',' + k
}
}

if (acehOld) {
expose = expose + ',--s'
resHdrNew.set('--t', '1')
}


if (rawLen) {
const newLen = resHdrOld.get('content-length') || ''
const badLen = (rawLen !== newLen)

if (badLen) {
if (retryTimes < MAX_RETRY) {
urlObj = await parseYtVideoRedir(urlObj, newLen, res)
if (urlObj) {
return proxy(urlObj, reqInit, acehOld, rawLen, retryTimes + 1)
}
}
return makeRes(res.body, 400, {
'--error': `bad len: ${newLen}, except: ${rawLen}`,
'access-control-expose-headers': '--error',
})
}

if (retryTimes > 1) {
resHdrNew.set('--retry', retryTimes)
}
}

let status = res.status

resHdrNew.set('access-control-expose-headers', expose)
resHdrNew.set('access-control-allow-origin', '*')
resHdrNew.set('--s', status)
resHdrNew.set('--ver', JS_VER)

resHdrNew.delete('content-security-policy')
resHdrNew.delete('content-security-policy-report-only')
resHdrNew.delete('clear-site-data')

if (status === 301 ||
status === 302 ||
status === 303 ||
status === 307 ||
status === 308
) {
status = status + 10
}

return new Response(res.body, {
status,
headers: resHdrNew,
})
}





function isYtUrl(urlObj) {
return (
urlObj.host.endsWith('.googlevideo.com') &&
urlObj.pathname.startsWith('/videoplayback')
)
}






async function parseYtVideoRedir(urlObj, newLen, res) {
if (newLen > 2000) {
return null
}
if (!isYtUrl(urlObj)) {
return null
}
try {
const data = await res.text()
urlObj = new URL(data)
} catch (err) {
return null
}
if (!isYtUrl(urlObj)) {
return null
}
return urlObj
}

3. 点击 [保存并部署]。

3. 点击 [Save & Deploy]。

回到你的项目管理页面,点击 [Settings],点击 [Domains],再点击 [Add Domain],填入一个你的域名或其子域名,点击 [Setup], 按照指示完成 DNS 解析后,点击 [Validate], 并点击自动配置证书,完成搭建。(下方有示例图)

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
server {

listen 80;

listen 443 ssl http2;

# 请更改为你的证书路径
ssl_certificate fullchain.pem;
ssl_certificate_key privkey.pem;

# 请更改为你的镜像域名
server_name jsd.ucbk.cn;

location / {
proxy_pass https://cdn.jsdelivr.net;
proxy_set_header Host $proxy_host;
proxy_set_header Accept-Encoding '';
proxy_ssl_server_name on;
proxy_ssl_name $proxy_host;
proxy_redirect / /;
# Proxy Cache
proxy_cache jsdelivr;
proxy_cache_lock on;
proxy_cache_lock_timeout 15s;
proxy_cache_use_stale updating;
proxy_cache_background_update on;
proxy_cache_key $host$request_uri;
proxy_cache_valid 200 301 302 30d;
proxy_cache_valid 500 501 502 503 15s;
proxy_cache_valid any 5m;
# Replace Domain
sub_filter_once off;
sub_filter_types application/javascript application/json text/xml text/css;
sub_filter '$proxy_host' '$host';
}

}
# 缓存路径请根据需要更改
proxy_cache_path /var/tmp/nginx/jsdelivr levels=1:2 use_temp_path=off keys_zone=jsdelivr:300m inactive=30d max_size=30g;
1
2
3
sub_filter_once  off;
sub_filter_types application/javascript application/json text/xml text/css;
sub_filter 'cdn.jsdelivr.net' 'jsd.ucbk.cn';

除了上述的服务之外,还有其他同样优秀的云服务商可以用于搭建镜像,如腾讯云、阿里云等等,但都存在一个致命的问题:需要备案。希望本文提供的方法可以帮到你,你同样也可以将这个方法用在其他网站的镜像上 (只需要修改一下代码就行)

这个镜像站目前还没被墙,如有需要的,可以拿去使用,这个镜像站每个月只有 100 万次请求数和 100G 的流量,如果可以的话请自行搭建,不会或懒得搭建的可以使用本站的 (๑・ᴗ・๑)♡