使用七牛 CDN 为 Ghost 静态资源加速

此前折腾 WordPress 时,利用 WP Super Cache + 七牛镜像存储 完美实现了 CDN 加速(教程)。迁移到 Ghost 后,也想这么做。研究一番后,也确实做到了,这里简单介绍一下研究过程,以 Ghost v0.5.3 及其默认主题 Casper v1.1 为例。

Asset Helper

修改主题时发现很多静态资源是通过 {{asset "css/screen.css"}} 加载的(文档)。如果将 asset 的返回改为 CDN 的地址,就可以实现 CDN 加速了。由于 Ghost 尚未发布插件功能,所以只能直接修改源码了。

几经搜索,终于在 core/server/helpers/asset.js 找到了这个函数。简单改改就可以返回 CDN 地址啦。

修改前:

asset = function (context, options) {
    var output = '',

修改后:

asset = function (context, options) {
    var output = 'http://netputer.qiniudn.com',
    // 将「netputer」替换为你的七牛 CDN 域名,结尾不要加「/」

访问首页观察效果,这种方法确实可以实现 CDN 加速静态资源,但仅限被 asset 处理过的静态资源文件,并不完美。于是继续研究。

Base Tag

观察网页源码,发现几乎所有静态资源都是相对路径。通过 <base> 给这些相对路径的静态资源指定基准域名为 CDN 的域名,从而实现所有静态资源都能被 CDN 加速。

修改方法也不难,对于默认主题来说,只需修改 content/themes/casper/default.hbs<head> 部分:

修改前:

<meta name="HandheldFriendly" content="True" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<link rel="shortcut icon" href="{{asset "favicon.ico"}}">

修改后:

<meta name="HandheldFriendly" content="True" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<base href="http://netputer.qiniudn.com/">
<!-- 将「netputer」替换为你的七牛 CDN 域名 -->

<link rel="shortcut icon" href="{{asset "favicon.ico"}}">

访问首页观察效果,发现静态资源确实全都被 CDN 加速了,上传的图片也不例外。但是很多链接是相对路径的,都被指向 CDN 域名了,这并不是我们所期望的。

不过只要把这些相对路径都替换为绝对路径,就可以避免这个问题了。由于涉及的改动比较零散,可以参考我的 Diff 进行修改。

再次访问首页观察效果,现在的效果已经接近完美了。

Advanced

既然是进阶,不需要太过详细,简单表达一下思路。

CDN Cache

Ghost 是通过 URL 加参数避免缓存( screen.css?v=123456 ),但这对七牛 CDN 来说就没有作用了。如果将来我们修改了静态资源文件,该如何更新呢?

其实可以将 asset 返回的路径的文件名修改为唯一不重复的(类似 screen.123456.css ),再配合 Nginx 规则将其指向正确位置。但是挺麻烦的,还不如每次修改就到后台删掉此前已被缓存的资源。所以就没有继续研究下去了。

HTTPS CDN

七牛 CDN 也是支持 HTTPS 的,但不是 *.qiniudn.com 。你需要到后台绑定 dn-*.qbox.me 的域名才可以使用 HTTPS CDN 。

Development Environment

以上修改的方法是对所有运行环境生效的,可能会对本地调试会造成影响。好在 Ghost 源码中可以通过 isProduction 判断当前运行环境是否为生产环境。

对于第一种方法,可以在 core/server/helpers/asset.js 中加上这么一段逻辑:

if (utils.isProduction) {
    output = 'http://netputer.qiniudn.com';
}

对于第二种方法,可以将 <base> 加到 core/server/helpers/ghost_head.js 中:

if (require('./utils').isProduction) {
    head.push('<base href="http://netputer.qiniudn.com/">');
}

其实还有第三种方法,也是最一种彻底的方法:找到最终输出页面内容的函数,对静态资源路径进行替换。但是感觉那样容易误杀,就没有那么去做。

P.s. 将研究过程整理成文后发现,这些方法都很蛋疼。只能期待 Ghost 的插件功能早日发布了。