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 的地址,我们要做的是把其中的域名删掉,从而使用相对路径。代码如下:
1 2 3 4 5 6 7 8 |
function leonax_remove_host( $url ) { $url = str_replace('http://leonax.net', '', $url); $url = str_replace('https://leonax.net', '', $url); return $url; } add_filter( 'style_loader_src', 'leonax_remove_host', 99, 1 ); add_filter( 'script_loader_src', 'leonax_remove_host', 99, 1 ); |
替换正文中的链接
使用 WordPress 的 the_content 过滤器,可以修改正文内容,具体要改什么,视具体情况而定。注意 WordPress 的短代码(shortcode)解析的优先级为 11,所以下面的代码使用了优先级“8”来避免短代码带来的改动。
1 2 3 4 |
add_filter( 'the_content', function($content) { $content = str_replace('//leonax.net', '', $content); return $content; }, 8 ); |
Apache 输出全文替换
Apache 提供了 mod_substitute 来对输出内容进行修改。如果你的站点中提供了这项功能,则可以轻松改动网页内容:
1 2 |
AddOutputFilterByType SUBSTITUTE text/html Substitute "s|//leonax.net/|//www.leonax.net|i" |
mod_pagespeed 域名替换
mod_pagespeed 也提供了类似的域名替换功能,比 mod_substitute 智能的是,mod_pagespeed 可以识别出链接字段,比如 <img src=""> 中的内容,而不像 mod_substitute 那样全局替换:
1 2 |
ModPagespeedMapRewriteDomain www.leonax.net leonax.net ModPagespeedMapRewriteDomain https://www.leonax.net https://leonax.net |
CDN Pull
有些时候,我们可能需要在 CDN 模式下输出 www.leonax.net,而在访问源站的时候则依然得到 leonax.net。这时就需要对 CDN 发来的请求进行定向输出。一般 CDN 方面会给出一些特殊的信息,比如 KeyCDN 会设置一个特殊的 X-Pull 头,标记这个请求是从 KeyCDN 发来的。于是我们可以对这一类请求做特殊处理(Apache):
1 2 3 |
<If "%{HTTP:X-Pull} == 'KeyCDN'"> # 特殊处理,方式见上面一项 </If> |
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 请求,用于清除一个特定页面的缓存:
1 |
curl "https://www.keycdn.com/zones/purgeurl/{zone_id}.json" -u [apikey]: -X DELETE -d path={path} |
CDN 方面搞定了,WordPress 依然成问题,因为我们也不知道页面什么时候更新。研究了一下发现,我可以利用 WP Super Cache,在 Super Cache 的缓存页面被删除的同时,也发送一个请求给 KeyCDN,同时删除那边的缓存,一举两得:
1 2 3 4 5 6 7 8 9 10 11 |
add_action('gc_cache', function($action, $permalink) { $url = 'https://{apikey}:@www.keycdn.com/zones/purgeurl/{zone_id}.json'; if ($action === 'prune') { wp_remote_request($url, array( 'method' => 'DELETE', 'body' => array('path' => 'leonax-1800.kxcdn.com' . $permalink), 'httpversion' => '1.1', 'blocking' => false, )); } }, 50, 2); |
上述的代码会在博文更新、或是有新留言的时候触发,会稍微减慢响应速度,更好的办法应该利用 WordPress 的 wp_schedule_single_event 方法,不过暂时还没有研究。
总结
把上述所有内容串起来,就有了现在的全站 CDN。
又结识了一位coding大神!
有看没有懂……常常在你这里可以捡到我碎掉的玻璃心……
其实动静分离后对静态资源进行单独加速会很方便
WordPress 的设计导致这么做很麻烦,索性就做了全站。
并不麻烦,我的站点就是动静分离的,绑定一个子域名到/wp-content/uploads目录做cdn,然后在functions中加入一丢丢代码开启wp的隐藏功能即可,动静分离后,加速效果还是很明显的,缓存起来也很方便
具体如下http://www.v7v3.com/wpjiaocheng/2014041111.html
嗯,我现在图片也是分离出去的,但是脚本之类的资源分离出去不方便。
不晓得点击下去什么感觉~
嗯,再点一次真的就发表了
原来你的站已CDN。刚才已使用了KeyCDN,请教一下,KeyCDN做CNAME解析后,那原来的A解析到主机IP是否可以删了?我现在Ping域名显示的是主机的IP,而你的显示的却是KeyCDN节点IP。
同时想问一下KeyCDN上的Secure Token和SSL认证如何显示?
如果把KeyCDN默认的域名自定义为一个自已的域名,如:cdn.jinbo123.com,那我该如何设置?
应该不能删,要不然 KeyCDN 找不到你的源站,就抓不到内容。我的 http://www.leonax.net 是 CNAME,Ping 出来是 KeyCDN 的 IP;但 leonax.net 应该还是自己的 IP。
Secure Token 我还没有仔细看,不知道有什么用。SSL 的话你需要上传你的 CSR 文件到 KeyCDN 那里,它才可以显示正确的 SSL。在 Zone 设置里有提示。
你需要创建一个 Zone alias,把你的 cdn.jinbo123.com 填进去。
CSR文件是我原来申请Jinbo123.com SSL的CSR文件吗?
填cdn.Jinbo123.com进去后还需要进行域名解析吗?
第一步SSL已完成,cdn已CNAME,过了一个多小时,还没有看见解析成功和可以使用。
你的 SSL 是签发给 http://www.jinbo123.com 和 jinbo123.com 的,没有 cdn.jinbo123.com,所以会有错误。你需要额外购买一个 cdn.jinbo123.com 的证书,或者使用 http://www.jinbo123.com 做 CNAME。
那我就用www.jinbo123.com做CNAME吧,这样就可以使用签发给https://www.jinbo123.com的证书了。但我想问一下,如果用www.jinbo123.com做CNAME后,那如何区分那些文件是已CDN了的?现在用KEYCDN提供的网址可以看得出来那些文件已CDN。
你用 www 做 CNAME 之后,所有从 http://www.jinbo123.com 访问的文件都是 CDN 的。
那会出现错误,因为没有执行文件了,如PHP文件等。
不会啊,CDN 只是一个缓存,内容还是从你的主站 jinbo123.com 输出的,当 CDN 找不到文件的时候,会从主站抓取的。
我试过一次,然后把www做CNAME 之后,同时还保留WWW的A解析到博客主机的IP上。但ping博客www.jinbo123.com返回的IP是全KEYCDN的,造成博客403错误。
至今还有一些地区由于DNS还没有刷新造成无法访本我的博客。
www 不可以同时做 CNAME 和 A 的,你要把 @ 解析到主机 IP,然后在 KeyCDN 那里把 Zone 指向 jinbo123.com
按你所说的方法去做了,不行,错误提示是:此网页包含重定向循环。
莫非你的博客中设置了主机地址是包含 www 的?
是的,博客网站为https://www.jinbo123.com,然后做了301定向。输入任何网站都会跳到https://www.........
包括http://jinbo123.com和https://jinbo123.com都会自动定向到https://www.jinbo123.com
嗯,要把主站设成不带 www 的,然后 www 用过 CNAME 指向 CDN,这样就不会死循环。
还有一个问题,刚才发现由于我的网站本身已SSL了的,但使用了KeyCDN后出现403错误,也就是证书错误,造成网站无法访问。
请问这个如何解决?谢谢。
这个CDN支持SSl?
SSI 是什么?如果你指的是 SNI 的话,是的。
看了下 我绑定的域名和你在一个边缘服务器上诶 哈哈
赞一个。写的很到位,学习了。
这种技术贴绝对需要mark下来慢慢看。
我还在折腾SSL,可以回答一下以下几个问题吗?
1、先是WordPress那里里,你的博客网址是什么(详细一些),有没有做301定向
2、你申请SSL时,是带WWW二级域名还只是根域名(也就是不带www的域名)?
谢谢。
1. leonax.net,我自己没有设置过 301,但如果我没理解错的话,WordPress 会自动转到 leonax.net
2. 都有,DigiCert 在申请 www 的时候,顺便会送裸域。
请问我在keycdn使用pull ,然后获得一个cdn地址;就在域名下做一个CNAME,那么A记录应该全部删除还是说留着@的那个?
要留着,要不然 KeyCDN 就找不到源站了
额 有人说A记录都可以删除,不过我还是保留了一个。这样要多久才能生效。还有就是wordpress使用了wp-supper这个缓存插件 使用它的cdn里面是填写keycdn给的地址还是填写其他的?用了后网站页面会变得的很乱 why? 感谢
大神,Nginx應該怎么操作呢?
如果使用Keycdn官方的wordpress插件CDN Enabler,和您设置的有什么区别呢?
基本没有区别,只是我不太喜欢装插件,能不用插件解决就不装插件。
明白了,谢谢。
谢博主分享,KeyCDN真的很好用。
不过有一点还是有点疑问,我直接用的KeyCDN家的WP插件设置的CDN,然后在插件选项里面设置了CDN作用目录(wp_includes, wp_uploads等),最终的效果是主站的域名访问不变,博客中的图片等等资源路径都变成了CDN域名(www.razrlele.com),看起来似乎和你的全站优化效果一样。。。
还是说有什么不同?
昏了= = 没有看到楼上的评论。。。了解了。。。
我觉得你这里不是全站CDN缓存,你的HTML页面就不是,每次加载都是新下载的,而不是使用本地缓存——但JS文件等就是——即不强制刷新就使用浏览器里的缓存版本。
事实上,HTML缓存不好,有了留言更新,半天都看不到。
如果你访问的是 http://www.leonax.net 的话,那就是全站 CDN。HTML 页面不会被缓存在本地,也没有必要,CDN 的作用是把 HTML 页面缓存在 CDN 上,这样不用每次都去源站抓取。
用博主的方法轻松搞定了JS等静态资源走CDN,很好用,这样就够了,访问量还没到那个量级,就没启用全站走CDN了