域名重定向 hexo博客更换域名

在很长的一段时间里,使用的是多域名,主要有 weikeqin.cn weikeqin.com,但是两个域名解析到的是同一个博客,想把 weikeqin.cn 的流量全部转到 weikeqin.com,因为有些时候 .cn 不如 .com 域名,在某个特殊的时候感觉特别明显。然后就有了这篇文章。
在具体更换域名前的测试中,发现github.com还是比coding.net好用。

(1) 博客域名更换

域名的重定向的方法有 1.域名转发 2.301重定向 3.JS跳转
方法一,域名注册商支持域名转发功能才行!国内的绝大部分域名注册商不支持。
方法二,是Web 服务器给访问老域名的请求返回一个 301 或者 302,然后跳转到新域名上。 如果转发前的域名和转发后的域名访问的都是中国大陆的服务器而且已经备案,可以直接通过域名解析配置 显性URL 301重定向。 由于使用的是 github.com coding.net 的page服务,解析到的不是中国大陆的服务器,这个方法也不可行。
方法三,使用js跳转。具体方法如下。

网站重定向

(1.1) index.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>新域名 https://weikeqin.com</title>
</head>

<body>
    <p> 跳转中,访问新域名站点 <a href="https://weikeqin.com" target="_blank"> https://weikeqin.com </a> </p>
</body>
<script type="text/javascript">console.log("index.html")</script>
<script type="text/javascript">window.location.href = "https://weikeqin.com";</script>

</html>

(1.2) 404.html

<script>
    /*! purl v2.3.1 | MIT */ 
    /** http://cdn.bootcss.com/purl/2.3.1/purl.min.js */
    (function (factory) { if (typeof define === "function" && define.amd) { define(factory) } else { window.purl = factory() } })(function () { var tag2attr = { a: "href", img: "src", form: "action", base: "href", script: "src", iframe: "src", link: "href" }, key = ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "fragment"], aliases = { anchor: "fragment" }, parser = { strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ }, isint = /^[0-9]+$/; function parseUri(url, strictMode) { var str = decodeURI(url), res = parser[strictMode || false ? "strict" : "loose"].exec(str), uri = { attr: {}, param: {}, seg: {} }, i = 14; while (i--) { uri.attr[key[i]] = res[i] || "" } uri.param["query"] = parseString(uri.attr["query"]); uri.param["fragment"] = parseString(uri.attr["fragment"]); uri.seg["path"] = uri.attr.path.replace(/^\/+|\/+$/g, "").split("/"); uri.seg["fragment"] = uri.attr.fragment.replace(/^\/+|\/+$/g, "").split("/"); uri.attr["base"] = uri.attr.host ? (uri.attr.protocol ? uri.attr.protocol + "://" + uri.attr.host : uri.attr.host) + (uri.attr.port ? ":" + uri.attr.port : "") : ""; return uri } function getAttrName(elm) { var tn = elm.tagName; if (typeof tn !== "undefined") return tag2attr[tn.toLowerCase()]; return tn } function promote(parent, key) { if (parent[key].length === 0) return parent[key] = {}; var t = {}; for (var i in parent[key]) t[i] = parent[key][i]; parent[key] = t; return t } function parse(parts, parent, key, val) { var part = parts.shift(); if (!part) { if (isArray(parent[key])) { parent[key].push(val) } else if ("object" == typeof parent[key]) { parent[key] = val } else if ("undefined" == typeof parent[key]) { parent[key] = val } else { parent[key] = [parent[key], val] } } else { var obj = parent[key] = parent[key] || []; if ("]" == part) { if (isArray(obj)) { if ("" !== val) obj.push(val) } else if ("object" == typeof obj) { obj[keys(obj).length] = val } else { obj = parent[key] = [parent[key], val] } } else if (~part.indexOf("]")) { part = part.substr(0, part.length - 1); if (!isint.test(part) && isArray(obj)) obj = promote(parent, key); parse(parts, obj, part, val) } else { if (!isint.test(part) && isArray(obj)) obj = promote(parent, key); parse(parts, obj, part, val) } } } function merge(parent, key, val) { if (~key.indexOf("]")) { var parts = key.split("["); parse(parts, parent, "base", val) } else { if (!isint.test(key) && isArray(parent.base)) { var t = {}; for (var k in parent.base) t[k] = parent.base[k]; parent.base = t } if (key !== "") { set(parent.base, key, val) } } return parent } function parseString(str) { return reduce(String(str).split(/&|;/), function (ret, pair) { try { pair = decodeURIComponent(pair.replace(/\+/g, " ")) } catch (e) { } var eql = pair.indexOf("="), brace = lastBraceInKey(pair), key = pair.substr(0, brace || eql), val = pair.substr(brace || eql, pair.length); val = val.substr(val.indexOf("=") + 1, val.length); if (key === "") { key = pair; val = "" } return merge(ret, key, val) }, { base: {} }).base } function set(obj, key, val) { var v = obj[key]; if (typeof v === "undefined") { obj[key] = val } else if (isArray(v)) { v.push(val) } else { obj[key] = [v, val] } } function lastBraceInKey(str) { var len = str.length, brace, c; for (var i = 0; i < len; ++i) { c = str[i]; if ("]" == c) brace = false; if ("[" == c) brace = true; if ("=" == c && !brace) return i } } function reduce(obj, accumulator) { var i = 0, l = obj.length >> 0, curr = arguments[2]; while (i < l) { if (i in obj) curr = accumulator.call(undefined, curr, obj[i], i, obj); ++i } return curr } function isArray(vArg) { return Object.prototype.toString.call(vArg) === "[object Array]" } function keys(obj) { var key_array = []; for (var prop in obj) { if (obj.hasOwnProperty(prop)) key_array.push(prop) } return key_array } function purl(url, strictMode) { if (arguments.length === 1 && url === true) { strictMode = true; url = undefined } strictMode = strictMode || false; url = url || window.location.toString(); return { data: parseUri(url, strictMode), attr: function (attr) { attr = aliases[attr] || attr; return typeof attr !== "undefined" ? this.data.attr[attr] : this.data.attr }, param: function (param) { return typeof param !== "undefined" ? this.data.param.query[param] : this.data.param.query }, fparam: function (param) { return typeof param !== "undefined" ? this.data.param.fragment[param] : this.data.param.fragment }, segment: function (seg) { if (typeof seg === "undefined") { return this.data.seg.path } else { seg = seg < 0 ? this.data.seg.path.length + seg : seg - 1; return this.data.seg.path[seg] } }, fsegment: function (seg) { if (typeof seg === "undefined") { return this.data.seg.fragment } else { seg = seg < 0 ? this.data.seg.fragment.length + seg : seg - 1; return this.data.seg.fragment[seg] } } } } purl.jQuery = function ($) { if ($ != null) { $.fn.url = function (strictMode) { var url = ""; if (this.length) { url = $(this).attr(getAttrName(this[0])) || "" } return purl(url, strictMode) }; $.url = purl } }; purl.jQuery(window.jQuery); return purl });
</script>

<script>
    console.log("404.html")
    var url = purl();
    if (url.attr('host') == 'weikeqin.cn') {
        var old_url = url.attr('source');
        var new_url = old_url.replace('weikeqin.cn', "weikeqin.com");
        console.log("old_url = " + old_url);
        console.log("new_url = " + new_url);
        window.location.replace(new_url);
    } else if (url.attr('host') == 'www.weikeqin.cn') {
        var old_url = url.attr('source');
        var new_url = old_url.replace('www.weikeqin.cn', "weikeqin.com");
        console.log("old_url = " + old_url);
        console.log("new_url = " + new_url);
        window.location.replace(new_url);
    } else if (url.attr('host') == 'weikeqin.github.io') {
        var old_url = url.attr('source');
        var new_url = old_url.replace('weikeqin.github.io/weikeqin.cn.github.io', "weikeqin.com");
        console.log("old_url = " + old_url);
        console.log("new_url = " + new_url);
        window.location.replace(new_url);
    } else if (url.attr('host') == 'www.weikeqin.github.io') {
        var old_url = url.attr('source');
        var new_url = old_url.replace('www.weikeqin.github.io/weikeqin.cn.github.io', "weikeqin.com");
        console.log("old_url = " + old_url);
        console.log("new_url = " + new_url);
        window.location.replace(new_url);
    } else {
        window.location.href = "http://weikeqin.com";
    }

</script>

(1.3) 一些思考

为什么要分成两个文件 index.html 和 404.html,放一个文件不是更好吗?
其实可以放一个文件里。 但是当脚本有问题的时候或者非正常流程时,分成两个文件会比一个文件更好。
如果脚本有问题,index.html出错的时候(测试的时候遇到了),有404.html兜底。 (这种情况在开发的时候,或者部署前就可以发现。)
遇到一些域外的情况,404.html文件可以兜底。
分成两个文件是从架构和模块化的角度考虑。

js跳转脚本里为什么要加console.log日志
为了排查问题方便 (一般情况下用不到)

(2) 具体实施

用的是万网的域名,weikeqin.cn和weikeqin.com用的都是coding.net的page服务。(实际更复杂)
修改后,weikeqin.cn会用github.com的pages服务,weikeqin.com会用coding.net的pages服务。
准备重定向脚本,在github pages服务准备好。
修改域名解析,在 域名控制台->解析设置 里 把weikeqin.cn 解析到coding.net改成github.com

回滚方案,如果有问题,修改域名解析,把 weikeqin.cn域名解析重新修改成coding.net对应的配置即可。

其实在实施前准备了很多测试,具体步骤忽略。

网站网络设计

(3) 测试

http://weikeqin.cn/
https://weikeqin.cn/

Navigated to http://weikeqin.cn/
(index):12 index.html
Navigated to https://weikeqin.com/

http://weikeqin.cn/2017/03/16/git-notes/
https://weikeqin.cn/2017/03/16/git-notes/

GET http://weikeqin.cn/2017/03/16/git-notes/ 404 (Not Found)
/2017/03/16/git-notes/:1 A cookie associated with a cross-site resource at http://hm.baidu.com/ was set without the `SameSite` attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with `SameSite=None` and `Secure`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.
/2017/03/16/git-notes/:1 A cookie associated with a cross-site resource at http://baidu.com/ was set without the `SameSite` attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with `SameSite=None` and `Secure`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.
Navigated to http://weikeqin.cn/2017/03/16/git-notes/
VM62:7 404.html
VM62:12 old_url = http://weikeqin.cn/2017/03/16/git-notes/
VM62:13 new_url = http://weikeqin.com/2017/03/16/git-notes/
Navigated to http://weikeqin.com/2017/03/16/git-notes/

可以看到 weikeqin.cn 重定向到 weikeqin.com 了

References

[1] Github Pages页面重定向到新网址,实现域名跳转
[2] HeTianCong.github.io
[3] 在更换 hexo 博客的域名后需要做的配置工作
[4] weikeqin.cn.github.io
[5] 301-redirect-for-site-hosted-at-github
[6] Permanent redirect from Github gh-pages
[7] 网站域名301重定向的五大别样方法
[8] 云解析 DNS > 操作指南 > 解析记录管理 > 添加解析记录 > 添加解析记录
[9] 云解析 DNS > 操作指南 > 解析生效测试方法