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

推荐订阅源

Vercel News
Vercel News
Recorded Future
Recorded Future
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The GitHub Blog
The GitHub Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Microsoft Azure Blog
Microsoft Azure Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
Y
Y Combinator Blog
N
News | PayPal Newsroom
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Help Net Security
Help Net Security
博客园 - Franky
SecWiki News
SecWiki News
Recent Announcements
Recent Announcements
T
Troy Hunt's Blog
The Register - Security
The Register - Security
The Last Watchdog
The Last Watchdog
Webroot Blog
Webroot Blog
S
Security Affairs
博客园 - 司徒正美
S
Schneier on Security
I
InfoQ
博客园_首页
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Threat Research - Cisco Blogs
Forbes - Security
Forbes - Security
腾讯CDC
N
Netflix TechBlog - Medium
N
News and Events Feed by Topic
Cloudbric
Cloudbric
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
A
About on SuperTechFans
Engineering at Meta
Engineering at Meta
Recent Commits to openclaw:main
Recent Commits to openclaw:main
B
Blog
V
Vulnerabilities – Threatpost
C
Check Point Blog
Google DeepMind News
Google DeepMind News
Google Online Security Blog
Google Online Security Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
Cisco Blogs
Schneier on Security
Schneier on Security
O
OpenAI News
K
Kaspersky official blog

Mythsman

Chromium + Mitmproxy 组合使用踩坑 记一次家庭科学网络架构调整 OpenWrt+R66s 软路由入门尝鲜 爬虫浏览器的Cloudflare五秒盾处理 PDF字体乱码问题分析 记一次SSH下无法umount磁盘的问题 Linux主机性能测试方法 利用 Redsocks 解决透明代理的远程抓包问题 利用 FFmpeg 将视频转换为 GIF 操作指北 纯Docker部署Https服务——以Nextcloud为例 快手抓包问题分析 一次采耳引发的屎案 树莓派与1024x600分辨率屏幕的适配问题 树莓派4B使用arm64系统踩坑
Ghost 解决 jsdelivr 资源加载慢的问题
mythsman · 2023-01-27 · via Mythsman

背景

用了很久的自建 Ghost 博客系统不知道从哪个版本开始,页面加载速度忽然变慢了很多。看了下加载的资源,发现多了很多走 jsdelivr cdn 的资源,加载速度竟然长达半分钟。。。

本来选择自建博客系统的重要目的之一就是为了页面加载速度可控,尽量避免加载不可靠、容易被墙的第三方资源。结果没想到 Ghost 官方又在核心模块里引用了第三方的 CDN。

不过还好 Ghost 项目本身的配置化做的还是不错的,大年初六上班摸个鱼的时间解决了一下。

解决

仔细看了下,新加入的走 CDN 的资源主要是 会员系统(portal)+评论系统(comments)+页面搜索 (sodo-search),因此在某次支持这些系统的更新前都是没问题的。不过考虑到目前的主题已经集成了这些系统,所以这些功能也不能禁用掉。

参考 Ghost Forum 的这篇讨论,可以通过在 config.[env].json 中修改配置,将 url 等替换成 self-hosted 的版本。不过这里的讨论中提到的配置来源并不清晰,在源码中搜索了一番发现了端倪(这里的 /var/lib/ghost 目录是我这 docker 里的 ghost 安装目录):

root@1f7b379a87f4:/var/lib/ghost/current# grep -r 'cdn.jsdelivr.net/ghost' *
core/shared/config/defaults.json:        "url": "https://cdn.jsdelivr.net/ghost/portal@~{version}/umd/portal.min.js",
core/shared/config/defaults.json:        "url": "https://cdn.jsdelivr.net/ghost/sodo-search@~{version}/umd/sodo-search.min.js",
core/shared/config/defaults.json:        "styles": "https://cdn.jsdelivr.net/ghost/sodo-search@~{version}/umd/main.css",
core/shared/config/defaults.json:        "url": "https://cdn.jsdelivr.net/ghost/comments-ui@~{version}/umd/comments-ui.min.js",
core/shared/config/defaults.json:        "styles": "https://cdn.jsdelivr.net/ghost/comments-ui@~{version}/umd/main.css",

可见这些配置都来源于 core/shared/config/defaults.json 这个文件,涉及到 CDN 的相关配置如下:

{

    ...

    "portal": {
        "url": "https://cdn.jsdelivr.net/ghost/portal@~{version}/umd/portal.min.js",
        "version": "2.23"
    },
    "sodoSearch": {
        "url": "https://cdn.jsdelivr.net/ghost/sodo-search@~{version}/umd/sodo-search.min.js",
        "styles": "https://cdn.jsdelivr.net/ghost/sodo-search@~{version}/umd/main.css",
        "version": "1.1"
    },
    "comments": {
        "url": "https://cdn.jsdelivr.net/ghost/comments-ui@~{version}/umd/comments-ui.min.js",
        "styles": "https://cdn.jsdelivr.net/ghost/comments-ui@~{version}/umd/main.css",
        "version": "0.12"
    },
    
    ...

 }

为了自托管这些文件,我们首先将这些文件下载到静态文件夹下,然后在想办法将 config.[env].json 配置好即可。

不过问题来了,静态文件夹有哪些?如果想当然的放在 /var/lib/ghost/content/public 下,肯定是要吃瘪的。正解应该是要看下 /var/lib/ghost/current/core/frontend/web/site.js 这里启动 express 的地方:

module.exports = function setupSiteApp(routerConfig) {
    debug('Site setup start', routerConfig);

    const siteApp = express('site');

    //...
    
    // Serve sitemap.xsl file
    siteApp.use(mw.servePublicFile('static', 'sitemap.xsl', 'text/xsl', config.get('caching:sitemapXSL:maxAge')));

    // Serve stylesheets for default templates
    siteApp.use(mw.servePublicFile('static', 'public/ghost.css', 'text/css', config.get('caching:publicAssets:maxAge')));
    siteApp.use(mw.servePublicFile('static', 'public/ghost.min.css', 'text/css', config.get('caching:publicAssets:maxAge')));

    // Card assets
    siteApp.use(mw.servePublicFile('built', 'public/cards.min.css', 'text/css', config.get('caching:publicAssets:maxAge')));
    siteApp.use(mw.servePublicFile('built', 'public/cards.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));

    // Comment counts
    siteApp.use(mw.servePublicFile('built', 'public/comment-counts.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));

    // Member attribution
    siteApp.use(mw.servePublicFile('built', 'public/member-attribution.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));

    // Serve site images using the storage adapter
    siteApp.use(STATIC_IMAGE_URL_PREFIX, mw.handleImageSizes, storage.getStorage('images').serve());
    // Serve site media using the storage adapter
    siteApp.use(STATIC_MEDIA_URL_PREFIX, storage.getStorage('media').serve());
    // Serve site files using the storage adapter
    siteApp.use(STATIC_FILES_URL_PREFIX, storage.getStorage('files').serve());
    
    //...
};

显然,这里 public 文件夹下的文件都是单独配置绑定的,不是整个文件夹的绑定。额外添加文件的话是不会映射到外部路径上的。这样一来,我们只能绑定在 images , media , files 这些路径下。再 check 下这些具体的变量,就会得知这些路径映射到的外部路径:

root@1f7b379a87f4:/var/lib/ghost/current# grep -r 'STATIC_.*_URL_PREFIX' *
...
node_modules/@tryghost/constants/index.js:    STATIC_IMAGES_URL_PREFIX: 'content/images',
node_modules/@tryghost/constants/index.js:    STATIC_MEDIA_URL_PREFIX: 'content/media',
node_modules/@tryghost/constants/index.js:    STATIC_FILES_URL_PREFIX: 'content/files'

我这里就选择放在 files 文件夹下,这里新建一个 self-host 文件夹:

root@1f7b379a87f4:/var/lib/ghost/content/files/self-host# ls
comments-ui-0.12.css  comments-ui-0.12.min.js  portal-2.23.min.js  sodo-search-1.1.css  sodo-search-1.1.min.js

由于我是通过 docker-compose 部署,相比于修改 config 文件,直接通过环境变量配置更为方便,变量名跟 config 中的 json 格式一一对应,这里不得不夸奖下 Ghost 的配置自动映射做的挺方便:

version: "3.8"
services:
  ghost:
  image: ghost:5.27
  container_name: "ghost"
  environment:
    ...
    portal__url: /content/files/self-host/portal-2.23.min.js
    sodoSearch__url: /content/files/self-host/sodo-search-1.1.min.js
    sodoSearch__styles: /content/files/self-host/sodo-search-1.1.css
    comments__url: /content/files/self-host/comments-ui-0.12.min.js
    comments__styles: /content/files/self-host/comments-ui-0.12.css
  ...

效果

首次加载速度直接从 30s 优化到了 300ms ,优化效果十分感人。。。

更新(2024.4)

考虑到 Ghost 经常会更新,每次都复制文件怪麻烦的,于是搭建了一个简单的 jsdelivr 代理 :

https://cdn.mythsman.com/jsdelivr/{path_to_jsdelivr}

这样只要在 docker-compose 做如下配置就好:

ghost:
    image: ghost:5.69.3
    container_name: "ghost"
    restart: always
    depends_on:
        - "mariadb"
    environment:
        # ...
        portal__url: https://cdn.mythsman.com/jsdelivr/ghost/portal@~2.37/umd/portal.min.js
        sodoSearch__url: https://cdn.mythsman.com/jsdelivr/ghost/sodo-search@~1.1/umd/sodo-search.min.js
        sodoSearch__styles: https://cdn.mythsman.com/jsdelivr/ghost/sodo-search@~1.1/umd/main.css
        comments__url: https://cdn.mythsman.com/jsdelivr/ghost/comments-ui@~0.16/umd/comments-ui.min.js
        # ...
    volumes:
        - ./ghost/:/var/lib/ghost/content
    expose:
        - "2368"