仙石浩明の日記

2010年1月28日

本ブログを livedoorブログから WordPress へ引越しました (URL は変更なし) hatena_b

このブログ 「仙石浩明の日記」 は、 今まで livedoorブログの有料プラン (月額 315円) を利用していた。 比較的自由にデザインをカスタマイズできること、 URL のドメインを自由に設定できることから、 2006年3月9日にこのブログを開設して以来、 今まで 4年近く使い続けてきた。 しかし自前サーバでない不便さ、隔靴掻痒感はいかんともしがたく、 乗り換えを検討したことは何度もあって、 そのたびに不便さと乗り換えの面倒くささを天秤にかけて踏み切れずにいた。

例えば、 HTML文法エラーを修正できない問題点を見つけたときや、 ブログのサイドバーにある 「livedoor Reader」 「RSS」 「livedoor Blog」などのバナーを非表示にしていたら、 ライブドアから警告メールが来て、 やむなくバナー表示を復活させたときなど。 なかでも、 昨年ブログ管理画面 (有料プラン) がリニューアルされたときは、 とても大きなフラストレーションを感じた。 ライブドアはよかれと思ってリニューアルしているのだろうが、 JavaScript を多用した新管理画面は、 私にとっては使いにくいことこのうえないし、 生ログ取得スクリプトも使えなくなった。

そして、 今年に入って 「ブログ共通ヘッダー」 (最上端に表示される livedoor Blogのロゴ, 所属カテゴリ, テキスト広告) が勝手に表示されるようになったのが最後の背中のひと押しとなった。 これを非表示にしたければ PRO プラン (月額 315円) ではなく ADVANCE プラン (月額 840円) に変更しろという趣旨 (要は値上げ) なのだと思うが、 何の連絡もなく画面の一番目立つ最上端に、 醜悪なヘッダー (ADVANCE プランへの移行を促すためにわざと醜悪にしている? *_*) を勝手に挿入するデリカシーの無さに驚き呆れ、 自前サーバへの引越を決めた。

common header

ブラウザ画面最上端 ↑ の 「ブログ共通ヘッダー」 (所属カテゴリ 「IT > プログラミング 」 と 「受験にまつわるエピソードを教えてください」 という広告? 表示) は、 ライブドアのサーバが HTML 中に自動挿入する以下の JavaScript (settings/header.js) によって body 要素へ追加 (appendChild) されている。

(function () {
    var hd = document.createElement('div');
    var str = '';
    str += '<div id="header2" style="z-index:10001"><table cellspacing="0" class="blog-common-header" id="header">';
        ...(中略)...
    hd.innerHTML = str;
    document.body.appendChild(hd);
        ...(後略)...

ユーザがカスタマイズ可能な範囲の外であるため、 この JavaScript の実行そのものを止めることは無理っぽいが、 追加された子要素を削除する JavaScript を実行して消すことは可能。 例えば以下のようなコードを HTML ファイルのどこかに入れておけばよい。

<script type="text/javascript" src="https://www.gcd.org/sengoku/docs/livedoor.js"></script>

livedoor.js は、 body 要素の子要素 「header2」 を取り除く (removeChild) だけの JavaScript:

(function () {
    var element = document.getElementById("header2");
    element.removeChild(element.childNodes.item(0));
})();

正月早々 「ブログ共通ヘッダー」 が勝手に表示されるようになったことに気付き、 直ちにこの対策を行なって非表示にしたのだが、 以前バナーを非表示にしたときライブドアから警告メールが来たことを考えれば、 今回も警告メールが来るのは時間の問題だろうと、 引越を急いだ次第。

私はブログを書くときも HTML で書いていて、 あまり CMS 的な機能は必要としないので、 トラックバックとコメントを受付けるだけの簡単な Web アプリを書こうと思っていたのだが、 試しにインストールしてみた WordPress が思いの外シンプルな作りで、 ちょっといじっているうちに簡単にカスタマイズできてしまったので、 これを使うことにした。

livedoorブログでエクスポートしたデータを (Movable Type / TypePad のデータとして) WordPress でインポートしたら、 (pre 要素なのに) 行頭の空白文字が削除されてしまって往生したが、 大した量でもなかったので手作業で修正してしまった。 もう少し量が多かったら真面目に PHP コードを追ったのだが...

こんなに簡単にできるなら、 もっと早く WordPress へ移行すればよかった。 既存のテーマを見よう見まねでいじっただけなのだが、 以前のデザインをほとんどそのまま踏襲することができた (むしろ余計な div 要素を排除できたので、よりシンプルになった)。

livedoorブログと WordPress では URL (パーマリンク, permalink) の形式が異なる。 例えば、 あるエントリの URL は、 livedoorブログ (旧URL) と WordPress (新URL) で次のようになる:

旧: livedoorブログ新: WordPress
ベースURL http://blog.gcd.org https://www.gcd.org/blog
エントリ /archives/51168556.html /2008/04/154/
月別表示 /archives/2008-04.html /2008/04/
カテゴリ /archives/cat_50026701.html /category/enlighten/
RSS /index.rdf /feed/rdf/

一見して、 WordPress のパーマリンクの方が分かりやすい。 正確に言えば、 WordPress のパーマリンクの形式は任意に設定できて、 livedoorブログ (Movable Type ベース) の形式に合わせることも可能だが、 エントリID (上記の例だと 51168556 と 154) が異なるので形式を合わせること自体にはあまり意味はない。

livedoorブログを利用していた時の URL がそのまま使えるように、 旧URL を新URL へ変換する以下の PHP スクリプトを書いた (switch 文で URL ごとにずらずら case を並べただけ)。

<?php
$new = NULL;
if ($_SERVER['REQUEST_URI'] == "/") {
    $new = "/blog/";
} elseif ($_SERVER['REQUEST_URI'] == "/atom.xml") {
    $new = "/blog/feed/atom/";
} elseif ($_SERVER['REQUEST_URI'] == "/index.rdf") {
    $new = "/blog/feed/rdf/";
} elseif (preg_match('/^\/archives\/(20\d\d)-(\d\d)\.html$/',
                     $_SERVER['REQUEST_URI'], $matches)) {
    $year = $matches[1];
    $mon = $matches[2];
    $new = "/blog/$year/$mon/";
} elseif (preg_match('/^\/archives\/cat_(\d+)\.html$/',
                     $_SERVER['REQUEST_URI'], $matches)) {
    $id = $matches[1];
    $new = "/blog/category/";
    switch ($id) {
    case 50026701: $new .= "enlighten/";   break;
    case 50026704: $new .= "business/";    break;
    case 50026703: $new .= "engineer/";    break;
    case 50026699: $new .= "system/";      break;
    case 50035671: $new .= "hardware/";    break;
    case 50026700: $new .= "programming/"; break;
    case 50021362: $new .= "stone/";       break;
    case 50035209: $new .= "la-fonera/";   break;
    case 50041534: $new .= "hawaii/";      break;
    case 50045637: $new .= "hongkong/";    break;
    case 50026702: $new .= "others/";      break;
    default: $new = NULL;
    }
} elseif (preg_match('/^\/archives\/(\d+)\.html$/',
                     $_SERVER['REQUEST_URI'], $matches)) {
    $id = $matches[1];
    $new = "/blog/";
    switch ($id) {
    case 50071073: $new .= "2006/03/8/"; break;
        ...(中略)...
    case 51168556: $new .= "2008/04/154/"; break;
        ...(中略)...
    case 51552583: $new .= "2009/12/184/"; break;
    case 51555097: $new .= "2010/01/185/"; break;
    default: $new = NULL;
    }
}
if ($new) {
    $host = "www.gcd.org";
    $_SERVER['SERVER_NAME'] = $host;
    $_SERVER['REQUEST_URI'] = $new;
    $_SERVER['SCRIPT_NAME'] = $new;
    $_SERVER['PHP_SELF'] = $new;
    define('WP_USE_THEMES', true);
    require('/usr/local/www/wordpress/wp-blog-header.php');
} else {
    header("HTTP/1.1 301 Moved Permanently");
    header("Location: https://www.gcd.org/blog/");
    exit();
}
?>

http://blog.gcd.org/* にアクセスすると、 ↑ この PHP スクリプトが実行される。

これに加え、 逆方向の変換、 つまり新URL から旧URL への変換も必要。 例えば 「はてなブックマーク」 へ頂いたコメント (例えば前述したエントリに対するコメント一覧) は旧URL に紐づいているので、 各エントリに旧URL を登録しておく必要がある。 WordPress には カスタムフィールド という機能があって、ひとつのエントリに複数の 「キー」 と、 各キーにその値を登録することができる。 そこで沢山の 「ブックマーク」 を頂いたエントリには、 「hatena_b」 というキーで旧URL を登録しておくことにした。 すると、以下のような PHP スクリプトでブックマーク数 を表示できる。

<?php
  $hatena_b = get_post_meta($post->ID, "hatena_b", true);
  if ($hatena_b)
    echo ' <a href="http://b.hatena.ne.jp/entry/' . $hatena_b
       . '"><img style="border:0;" src="http://b.hatena.ne.jp/entry/image/'
       . $hatena_b . '" alt="" /></a>';
?>

最後に、 ネームサーバの設定変更を行なって、 blog.gcd.org レコード (TTL = 3時間) の値を 「CNAME blog-01.livedoor.jp」 から 「CNAME www.gcd.org」 へ変更した。 1/26 19:35 に変更を行なったところ、 19:46 には最初のアクセスが www.gcd.org へ届き、 20時台には大半のアクセスが www.gcd.org へ届くようになった。 20時台にライブドア側へ届いた blog.gcd.org へのアクセスはわずかに 7件、 その後は 1 時間〜数時間ごとに、ぱらぱらアクセスがあるという状況が続いている。 旧レコードを保持しているキャッシュネームサーバが残っていて、 そのネームサーバを使ってるブラウザからのアクセスのみが、 ライブドア側へ届いているといった感じ。

同日 20:20 追記:

そろそろ帰ろうかと思っていたら、 ひろせ31さんからチャットを頂いた:

(19:05:46) ひろせ: curl -I https://www.gcd.org/blog/ | grep X-Pingback
到達不可なヘッダついてますよー
(19:06:46) 仙石: ども、ご無沙汰してます。
(19:06:53) ひろせ: どもどもー
(19:08:21) 仙石: そっか、ヘッダに残ってましたか~
(19:08:34) 仙石: チェックするのを忘れていました。ありがとうございます
(19:08:40) ひろせ: いえいえー

確かにヘッダに 「X-Pingback:」 フィールドがついている。↓

% curl -I https://www.gcd.org/blog/
HTTP/1.1 200 OK
Date: Thu, 28 Jan 2010 10:08:54 GMT
Server: Apache
X-Pingback: http://gt.gcd.org/wordpress/xmlrpc.php
Content-Type: text/html; charset=UTF-8

Pingback って何だろう (^^;) と思いつつ、 「gt.gcd.org」 は GCD (私の個人サイト) 内部でのみ有効なホスト名 (IPアドレスは 192.168.1.1) なので、 このフィールドの目的が何であれ取り除いた方がいい。 実は、 HTTP レスポンス中にも 「gt.gcd.org」 は出力されていて、 ↓

<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://gt.gcd.org/wordpress/xmlrpc.php?rsd" />
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://gt.gcd.org/wordpress/wp-includes/wlwmanifest.xml" /> 

これは公開前に気付いて取り除いたのだが、 HTTP ヘッダの方はチェックするのを忘れていた。orz

上記二つの link 要素は、 それぞれ rsd_link と wlwmanifest_link というフィルタが出力しているので、 このフィルタを wp_head フックから取り除いてやればよい。 具体的には、 現在のテーマの functions.php ファイルに、 以下のようなコードを追加する:

remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');

ひろせ31さんから指摘を受けて、 条件反射的に WordPress の PHP コードを検索してみた:

% find /usr/local/www/wordpress -type f | xargs grep X-Pingback
/usr/local/www/wordpress/wp-includes/classes.php:         * Sets the X-Pingback header, 404 status (if 404), Content-type. If showing
/usr/local/www/wordpress/wp-includes/classes.php:                $headers = array('X-Pingback' => get_bloginfo('pingback_url'));

classes.php の該当箇所を見てみると、


        function send_headers() {
                $headers = array('X-Pingback' => get_bloginfo('pingback_url'));
                ...

ありゃ、ハードコードされてる...

前述した link 要素のように、 フィルタで出力するようになっていれば、 そのフィルタを無効化するだけで済むのに... (*_*)

さて、どうしたものか。 この send_headers() 関数は、 $headers 配列にフィールドを加えていって、 最後に $headers を HTTP ヘッダとして送信する、 という処理の流れのようだ。

さらに読み進めていくと、

                ...
                $headers = apply_filters('wp_headers', $headers, $this);
                ...

ありがたいことにフィルタ・フックが定義されていた。 この wp_headers フックに、 X-Pingback: フィールドを取り除くフィルタを登録すれば、 うまくいきそうだ。

(19:12:57) 仙石: X-Pingback はハードコードされてますねぇ... > WordPress
(19:13:12) ひろせ: うひー
(19:13:22) 仙石: フィルタで削除するしかないのかな... あるいは WordPress のコードを直接書き換えるか...
(19:13:22) ひろせ: gt.gcd.orgって、内部ホストなんですかね?
(19:13:26) 仙石: です
(19:13:43) 仙石: 管理用なので、外部からはアクセスできないようにしたいです
(19:16:46) ひろせ: Apacheで、 Header unset X-Pingback で消えるかもすね。
(19:17:58) 仙石: なるほど

現在のテーマの functions.php ファイルに、 以下の PHP コードを追加してみた:

function remove_X_Pingback($headers) {
        unset($headers['X-Pingback']);
        return $headers;
}
add_filter('wp_headers', 'remove_X_Pingback');

初めて書く WordPress のフィルタだったが (実をいうと PHP も書き慣れているわけではないので、 配列の要素を削除する方法もマニュアルを調べながら書いた ^^;)、 うまく動いているようだ:

% curl -I https://www.gcd.org/blog/
HTTP/1.1 200 OK
Date: Thu, 28 Jan 2010 10:29:33 GMT
Server: Apache
Content-Type: text/html; charset=UTF-8

めでたく、X-Pingback: フィールドが出力されなくなった。

(19:30:23) 仙石: 取り除きました。> X-Pingback:
(19:30:33) ひろせ: oo
(19:30:38) 仙石: functions.php に、
function remove_X_Pingback($headers) {
        unset($headers['X-Pingback']);
        return $headers;
}
add_filter('wp_headers', 'remove_X_Pingback');
(19:30:51) 仙石: を加えました。

ありがとうございました。> ひろせ31さん

Filed under: システム構築・運用 — hiroaki_sengoku @ 08:26

1 Comment

  1. はじめまして。
    私もlivedoorBlogからwordpressへ移行したくて
    こちらの記事を参考にさせていただいております。

    ひとつ、質問がございまして、
    「旧URLを新URLへ変換するPHPスクリプト」
    はどのファイルに記述すればよろしいでしょうか?

    お手数ですが、教えていただけますと幸いです。
    よろしくお願い致します。

    Comment by やまだ — 2013年4月25日 @ 22:16

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.