从无到有实现 WordPress 全站 CDN

WordPress 的设计有很多莫名其妙的地方,其中之一就是它的域名设置。WordPress 的每一个站点要设置一个独立的主页(Home)地址,而且只能设置一个。也就是说,即使有两个域名指向同一个博客(比如 a.com 和 b.com),如果博客把主页设置成了 a.com,用户在访问 b.com 的时候,也会被重定向到 a.com。这样造成了 b.com 实际上完全无法访问到。

对于使用 WordPress 的博客主而言,通常一个域名就够了,可能最初 WordPress 的开发人员也是这么想的。但现在的互联网,单一域名已经不足以支撑一个网站了。简单来说,WordPress 配置 CDN 相当麻烦。

CDN,又名内容分发网络,一个把网站内容快速传递给用户的工具。它的工作原理是,预先把内容加载(缓存)到离用户较近的数据中心(通常称为 Edge Server),当用户访问网站时,直接从数据中心读取内容,而不是从源网站(Orginal Server),这样就减少了数据传输的时间。如下图:

一个 CDN 包含多个 Edge Server,如果每个用户都访问离自己较近的 Edge Server,则会比直接访问源网站要快很多。而现在的技术又可以做到当用户访问某一个域名的时候,被自动解析到较近的 Edge Server,用户则完全感受不到差别。

现在做得比较好的 CDN 服务如 CloudFlare。它家提供了一条龙服务,从域名解析到内容分发,一个帐号全搞定。这样做的好处是“傻瓜式”,不需要太多的专业知识就可以配置 CDN;而坏处则是一损俱损,CloudFlare 的服务器经常被墙,一旦被墙了,想脱离 CloudFlare 就重新设置一堆东西,很麻烦。

如果你像我一样不喜欢一键搞定的服务,那我们就来一步一步自己配置全站 CDN 吧。

初始化 CDN 服务

首先,你得有一个 CDN 服务,我用的是 KeyCDN,介绍看这里

初始化(KeyCDN 的术语叫“Zone”)完成之后,一般会得到一个二级域名,比如我的是 leonax-1800.kxcdn.com。然后在自己的域名解析中创建一个 CNAME,指向之前的域名,比如我用的是 www.leonax.net。解析刷新之后,初始化就完成了。

修改链接和资源地址

如上文所说,WordPress 限定了网站的地址,比如本博客设置为“leonax.net”,则所有的资源(JS、CSS 等)和链接都会以“//leonax.net/”开头。即使用户访问了主页“www.leonax.net”,其中的各种资源也会从“leonax.net”加载,这样就达不到 CDN 的效果。于是我们要把页面中所有的“leonax.net”全部替换成“www.leonax.net”。下面有一些方法,可选择用其中的一种或多种组合来达到效果:

修改资源地址

WordPress 提供了 style_loader_src 和 script_loader_src 分别来过滤 JS 和 CSS 的地址,我们要做的是把其中的域名删掉,从而使用相对路径。代码如下:

替换正文中的链接

使用 WordPress 的 the_content 过滤器,可以修改正文内容,具体要改什么,视具体情况而定。注意 WordPress 的短代码(shortcode)解析的优先级为 11,所以下面的代码使用了优先级“8”来避免短代码带来的改动。

Apache 输出全文替换

Apache 提供了 mod_substitute 来对输出内容进行修改。如果你的站点中提供了这项功能,则可以轻松改动网页内容:

mod_pagespeed 域名替换

mod_pagespeed 也提供了类似的域名替换功能,比 mod_substitute 智能的是,mod_pagespeed 可以识别出链接字段,比如 <img src=""> 中的内容,而不像 mod_substitute 那样全局替换:

CDN Pull

有些时候,我们可能需要在 CDN 模式下输出 www.leonax.net,而在访问源站的时候则依然得到 leonax.net。这时就需要对 CDN 发来的请求进行定向输出。一般 CDN 方面会给出一些特殊的信息,比如 KeyCDN 会设置一个特殊的 X-Pull 头,标记这个请求是从 KeyCDN 发来的。于是我们可以对这一类请求做特殊处理(Apache):

CORS

Cross-origin resource sharing,跨来源资源共享,是一个浏览器的特性。简单来说,leonax.net 和 www.leonax.net 是两个域,如果 leonax.net 引用了来自 www.leonax.net 的资源,而 www.leonax.net 没有允许这么做的话,浏览器会主动拒绝加载相应资源的。所以,如果两个域都用了 CDN 的资源,则在 www.leonax.net 中,需要允许 CORS。这一项在 KeyCDN 中默认是开启的,我也就没有多关心。

CDN Purge

CDN 的主要原理是缓存,也就是把网页内容缓存在 Edge Server 中,每次响应用户请求的时候,不必再向源站请求数据。这样就产生了缓存过期的问题。比如博客中有人留言,博客页面更新了之后,CDN 却不知道这件事,依然输出不包含留言的页面,这样就不对了。为了解决这个问题,CDN 通常都会提供一种清除缓存的方式,称为 CDN Purge。 KeyCDN 的做法是发出一个如下的 HTTPS 请求,用于清除一个特定页面的缓存:

CDN 方面搞定了,WordPress 依然成问题,因为我们也不知道页面什么时候更新。研究了一下发现,我可以利用 WP Super Cache,在 Super Cache 的缓存页面被删除的同时,也发送一个请求给 KeyCDN,同时删除那边的缓存,一举两得:

上述的代码会在博文更新、或是有新留言的时候触发,会稍微减慢响应速度,更好的办法应该利用 WordPress 的 wp_schedule_single_event 方法,不过暂时还没有研究。

总结

把上述所有内容串起来,就有了现在的全站 CDN


43 条评论 添加

      1. 并不麻烦,我的站点就是动静分离的,绑定一个子域名到/wp-content/uploads目录做cdn,然后在functions中加入一丢丢代码开启wp的隐藏功能即可,动静分离后,加速效果还是很明显的,缓存起来也很方便
        具体如下http://www.v7v3.com/wpjiaocheng/2014041111.html

  1. 原来你的站已CDN。刚才已使用了KeyCDN,请教一下,KeyCDN做CNAME解析后,那原来的A解析到主机IP是否可以删了?我现在Ping域名显示的是主机的IP,而你的显示的却是KeyCDN节点IP。
    同时想问一下KeyCDN上的Secure Token和SSL认证如何显示?
    如果把KeyCDN默认的域名自定义为一个自已的域名,如:cdn.jinbo123.com,那我该如何设置?

    1. 应该不能删,要不然 KeyCDN 找不到你的源站,就抓不到内容。我的 http://www.leonax.net 是 CNAME,Ping 出来是 KeyCDN 的 IP;但 leonax.net 应该还是自己的 IP。

      Secure Token 我还没有仔细看,不知道有什么用。SSL 的话你需要上传你的 CSR 文件到 KeyCDN 那里,它才可以显示正确的 SSL。在 Zone 设置里有提示。

      你需要创建一个 Zone alias,把你的 cdn.jinbo123.com 填进去。

          1. 那我就用www.jinbo123.com做CNAME吧,这样就可以使用签发给https://www.jinbo123.com的证书了。但我想问一下,如果用www.jinbo123.com做CNAME后,那如何区分那些文件是已CDN了的?现在用KEYCDN提供的网址可以看得出来那些文件已CDN。

                  1. 我试过一次,然后把www做CNAME 之后,同时还保留WWW的A解析到博客主机的IP上。但ping博客www.jinbo123.com返回的IP是全KEYCDN的,造成博客403错误。

                    至今还有一些地区由于DNS还没有刷新造成无法访本我的博客。

                  2. 是的,博客网站为https://www.jinbo123.com,然后做了301定向。输入任何网站都会跳到https://www.........

                    包括http://jinbo123.com和https://jinbo123.com都会自动定向到https://www.jinbo123.com

  2. 还有一个问题,刚才发现由于我的网站本身已SSL了的,但使用了KeyCDN后出现403错误,也就是证书错误,造成网站无法访问。
    请问这个如何解决?谢谢。

  3. 我还在折腾SSL,可以回答一下以下几个问题吗?
    1、先是WordPress那里里,你的博客网址是什么(详细一些),有没有做301定向
    2、你申请SSL时,是带WWW二级域名还只是根域名(也就是不带www的域名)?
    谢谢。

    1. 1. leonax.net,我自己没有设置过 301,但如果我没理解错的话,WordPress 会自动转到 leonax.net
      2. 都有,DigiCert 在申请 www 的时候,顺便会送裸域。

      1. 额 有人说A记录都可以删除,不过我还是保留了一个。这样要多久才能生效。还有就是wordpress使用了wp-supper这个缓存插件 使用它的cdn里面是填写keycdn给的地址还是填写其他的?用了后网站页面会变得的很乱 why? 感谢

  4. 谢博主分享,KeyCDN真的很好用。

    不过有一点还是有点疑问,我直接用的KeyCDN家的WP插件设置的CDN,然后在插件选项里面设置了CDN作用目录(wp_includes, wp_uploads等),最终的效果是主站的域名访问不变,博客中的图片等等资源路径都变成了CDN域名(www.razrlele.com),看起来似乎和你的全站优化效果一样。。。

    还是说有什么不同?

  5. 我觉得你这里不是全站CDN缓存,你的HTML页面就不是,每次加载都是新下载的,而不是使用本地缓存——但JS文件等就是——即不强制刷新就使用浏览器里的缓存版本。

    事实上,HTML缓存不好,有了留言更新,半天都看不到。

  6. 用博主的方法轻松搞定了JS等静态资源走CDN,很好用,这样就够了,访问量还没到那个量级,就没启用全站走CDN了

发表评论

电子邮件地址不会被公开。 必填项已用*标注