解决 jsdelivr CDN不可用问题
文章目录
- 问题
- 替代 CDN
- 对已有项目的 jsdelivr CDN 进行批量替换
- 查看已有项目的 jsdelivr CDN 使用情况并记录
- 替换规律总结
- 首先尝试 `cdnjs.cloudflare.com`
- 兜底使用 `unpkg.com`
- 执行替换
- 使用脚本统一完成替换
- 附记
问题
这两天搞 IPv6 地址相关的东东,才发现:jsdelivr CDN 在IPv4下无法访问,仅在IPv6(或双线)下才可以访问!
是的,这个最常被使用的 CDN 被墙了。查了下,它申请的备案好像在前些年就过期了,估计是因为它自动缓存了GitHub好些内容吧,也是不容易。。
PS,对比才发现 IPv6 还真是个好东西~关于IPv6的更多概念和详细情况参见 IPv6简记,现在运营商已经在入户宽带中普及IPv6了,测试网站:IP地址 | 域名 | MAC地址工具集,如果一切正常应该看到双栈IP显示。家庭网络没生效IPv6的直接参照这篇文章进行设置:家庭宽带正确设置IPv6的方法。
替代 CDN
jsdelivr CDN 在中国也有不少私建镜像,但是,考虑到稳定性,均不建议使用。
经测试,无论在IPv4或IPv6下,均可以访问的国际知名 CDN 有:
- Cloudflare:全球领先的CDN与网络安全服务商,通过覆盖200多个国家的节点网络提供网站加速、DDoS防护、免费SSL证书和边缘计算等服务,兼顾性能与安全。
- 缺点:不会默认覆盖全网 npm 资源,需开发者主动接入域名。(很多不出名npm都没有Cloudflare CDN)
- 选择:更稳定,推荐优选。
- unpkg:专为npm生态设计的公共CDN服务,可直接通过URL自动托管并分发npm包中的静态资源(如JS/CSS),底层依赖Cloudflare网络实现全球加速,适合快速开发调试。
- 缺点:底层依托Cloudflare网络实现,未来被墙的概率更大。
- 选择:自动托管npm包,可以无缝替代jsdelivr CDN,在 Cloudflare CDN 没资源情况下作为备选替代。
对已有项目的 jsdelivr CDN 进行批量替换
查看已有项目的 jsdelivr CDN 使用情况并记录
查看已有项目的 jsdelivr CDN 使用情况并记录(记录是为了便于追踪,在出错时进行恢复,毕竟搞错了,该网页就大概率要挂了):
grep 'cdn.jsdelivr.net/npm' /var/www/html -R | tee /tmp/"`date +%Y-%m-%d-%H-%m-%S`.log"
替换规律总结
首先尝试 cdnjs.cloudflare.com
-
域名和路径头替换:
https://cdn.jsdelivr.net/npm/
->https://cdnjs.cloudflare.com/ajax/libs/
-
包名和版本号路径化:
- 将
@
符号替换为/
。 - 原始格式:
package@version/...
- 目标格式:
package/version/...
- 例如:
bootstrap@4.5.3
->bootstrap/4.5.3
- 将
-
移除
/dist/
目录:cdnjs
的路径中通常不包含/dist/
目录,需要将其删除。- 例如:
/dist/js/bootstrap.min.js
->/js/bootstrap.min.js
- 例如:
/dist/css/bootstrap.min.css
->/css/bootstrap.min.css
-
版本号标准化(特殊情况):
- 例如对于
tsparticles
系列包,原版本2
并不支持,故将所有版本统一指定为了可用的具体版本2.12.0
。
- 例如对于
总结公式:
https://cdn.jsdelivr.net/npm/{package}@{version}/path/to/file
-> https://cdnjs.cloudflare.com/ajax/libs/{package}/{version}/path/to/file
兜底使用 unpkg.com
-
域名和路径头替换:
https://cdn.jsdelivr.net/npm/
->https://unpkg.com/
-
保留完整路径:
unpkg
的路径结构与 npm 包原始结构完全一致,因此包名后的所有路径(包括/dist/
,/build/
等目录)都原样保留,无需更改。
总结公式:
https://cdn.jsdelivr.net/npm/{package}@{version}/path/to/file
-> https://unpkg.com/{package}@{version}/path/to/file
执行替换
先确定替换后的 cloudflare CDN 地址是否存在!! 如果不存在则使用 unpkg 进行替换。
命令如下:
find . -type f -exec sed -i 's|https://cdn\.jsdelivr\.net/npm/font-awesome@4\.7\.0/css/font-awesome\.min\.css|https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css|g' {} +find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/hi-base32@0.5.1/build/base32.min.js|https://unpkg.com/hi-base32@0.5.1/build/base32.min.js|g' {} +
使用脚本统一完成替换
不推荐:无法确定被替换后的目标的有效性,且匹配路径不太通用。。(剩余的查找后可以再手动处理)
# 替换为 cdnjs.cloudflare.com
find . -type f -exec sed -i 's|https://cdn\.jsdelivr\.net/npm/\([^@]*\)@\([^/]*\)/dist/|https://cdnjs.cloudflare.com/ajax/libs/\1/\2/|g' {} +
推荐:可完美替代,就是未来的稳定性不如 cloudflare
# 替换为 unpkg.com
find . -type f -exec sed -i 's|https://cdn\.jsdelivr\.net/npm/|https://unpkg.com/|g' {} +
附记
处理记录参考(适用于 阿里云服务器 系列文章中的之前项目统一处理):
find . -type f -exec sed -i 's|https://cdn\.jsdelivr\.net/npm/font-awesome@4\.7\.0/css/font-awesome\.min\.css|https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js|https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.5.3/js/bootstrap.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js|https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.1/umd/popper.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css|https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.5.3/css/bootstrap.min.css|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/tsparticles@2/tsparticles.bundle.min.js|https://cdnjs.cloudflare.com/ajax/libs/tsparticles/2.12.0/tsparticles.bundle.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/tsparticles-path-polygon@2/tsparticles.path.polygon.min.js|https://cdnjs.cloudflare.com/ajax/libs/tsparticles-path-polygon/2.12.0/tsparticles.path.polygon.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js|https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/js/bootstrap.bundle.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css|https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/css/bootstrap.min.css|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/tsparticles-preset-fireworks@2/tsparticles.preset.fireworks.bundle.min.js|https://cdnjs.cloudflare.com/ajax/libs/tsparticles-preset-fireworks/2.12.0/tsparticles.preset.fireworks.bundle.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/tsparticles-preset-fire@2/tsparticles.preset.fire.bundle.min.js|https://cdnjs.cloudflare.com/ajax/libs/tsparticles-preset-fire/2.12.0/tsparticles.preset.fire.bundle.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/tsparticles-preset-fireworks@2/tsparticles.preset.fireworks.bundle.min.js|https://cdnjs.cloudflare.com/ajax/libs/tsparticles-preset-fireworks/2.12.0/tsparticles.preset.fireworks.bundle.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/tsparticles-slim@2/tsparticles.slim.bundle.min.js|https://cdnjs.cloudflare.com/ajax/libs/tsparticles-slim/2.12.0/tsparticles.slim.bundle.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/tsparticles-preset-fountain@2/tsparticles.preset.fountain.bundle.min.js|https://cdnjs.cloudflare.com/ajax/libs/tsparticles-preset-fountain/2.12.0/tsparticles.preset.fountain.bundle.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/tsparticles-preset-confetti@2/tsparticles.preset.confetti.bundle.min.js|https://cdnjs.cloudflare.com/ajax/libs/tsparticles-preset-confetti/2.12.0/tsparticles.preset.confetti.bundle.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/vanilla-jsoneditor/standalone.js|https://unpkg.com/vanilla-jsoneditor@3.8.0/standalone.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/js-crc@0.3.1/build/crc.min.js|https://unpkg.com/js-crc@0.3.1/build/crc.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/crypto-js.min.js|https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/diff/dist/diff.min.js|https://unpkg.com/diff@8.0.2/dist/diff.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js|https://unpkg.com/diff2html@3.4.52/bundles/js/diff2html-ui.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css|https://unpkg.com/diff2html/bundles/css/diff2html.min.css|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js|https://unpkg.com/jszip@3.10.1/dist/jszip.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako.min.js|https://cdnjs.cloudflare.com/ajax/libs/pako/2.1.0/pako.min.js|g' {} +
find . -type f -exec sed -i 's|https://cdn.jsdelivr.net/npm/hi-base32@0.5.1/build/base32.min.js|https://unpkg.com/hi-base32@0.5.1/build/base32.min.js|g' {} +sed -i 's|https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css|https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css|g' ../php/public-share/index.php
sed -i 's|https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css|https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css|g' ../php/public-share/admin.php.dist