WordPress から Jekyll への移行で一番問題になったのは、記事の URL を維持することでした。
フィードやタグのページなどはまあリダイレクトでもいいのですが、記事の URL が変わってしまうと、既存のはてなブックマークや Twitter での言及が追えなくなってしまうので、避けたいところです。
この記事では、このブログでの記事 URL の維持のために行った作業について説明します。かなり dirty な方法なので、よりよい方法があれば教えてください。
目標と概要
Jekyll のデフォルトでは記事の URL は /2014/02/10/1234.html
のようになります。これを /1234
に変更していきます。
今回は下記の手順で行いました。
- permalink の設定
- 生成する HTML のパス変更
- Apache の MultiViews の利用
- 開発用サーバの書き換え
permalink の設定
_config.yml
で permalink: :title
とすると {{ page.url }}
などで目的の URL (/1234
) を返すようになります。
この場合、/1234/index.html
にファイルを作成するようになります。これにより、標準的な設定の Web サーバなら /1234/
でアクセスできるようになるわけです。
これはなかなかうまい手ではあるのですが、最後のスラッシュが余計です。Apache なら /1234
を /1234/
へリダイレクトしてくれはするのですが、URL は変わってしまいます。
生成する HTML のパス変更
次の項で説明する MultiViews のために、permalink
の設定はそのままで、/1234/index.html
でなく /1234.html
にファイルを作るよう Jekyll のコードを上書きします。
_plugins
下に下記のコードを配置します。
# _plugins/dont_create_directory_per_post.rb
require "jekyll"
module Jekyll
class Post
def destination(dest)
path = File.join(dest, File.expand_path(CGI.unescape(self.url), "/"))
# path = File.join(path, "index.html") if path[/\.html$/].nil?
path = "#{path}.html" if path[/\.html$/].nil?
path
end
end
end
これで /1234.html
でアクセスできるようになりました。
Apache の MultiViews の利用
.htaccess
に次の記述を追加します。
# Enable content negotiation
Options +MultiViews
MultiViews を有効にすると、要求したパスにファイルがない場合、Accept ヘッダに基づいて、拡張子を付与したファイルを探してくれるようになります。
/1234
へアクセスした場合に、1234 というファイルがなく、1234.html があれば、それを返してくれるわけです。
これで、/1234
でアクセスできるようになりました。
開発用サーバの書き換え
運用サーバはこれでいいのですが、jekyll serve
だと (上記の .htaccess
は当然無視されるので) /1234
ではアクセスできません。
_plugins
下に下記のコードを配置することで、WEBrick のコードを一部書き換えます。
# _plugins/webrick.rb
# jekyll serve で .html 省略可能に
require "webrick"
WEBrick::HTTPServlet::FileHandler.class_eval do
def search_file(req, res, basename)
langs = @options[:AcceptableLanguages]
path = res.filename + basename
if File.file?(path)
return basename
elsif langs.size > 0
req.accept_language.each{|lang|
path_with_lang = path + ".#{lang}"
if langs.member?(lang) && File.file?(path_with_lang)
return basename + ".#{lang}"
end
}
(langs - req.accept_language).each{|lang|
path_with_lang = path + ".#{lang}"
if File.file?(path_with_lang)
return basename + ".#{lang}"
end
}
# ここから追加
elsif File.file?("#{path}.html")
return "#{basename}.html"
# ここまで追加
end
return nil
end
end
これで jekyll serve
でも /1234
でアクセスできるようになりました。