Tech Notes

WordPress×サブディレクトリインストール×サブディレクトリ型マルチサイト×H2O

の、やり方。正直これで正しいという保証は出来ないが、とりあえず動いているのでご紹介。

背景知識

まず、WordPressのサブディレクトリインストールをする者は「WordPressアドレス(URL)」と「サイトアドレス(URL)」の違いについて認識しなければならない。もっとも、サブディレクトリインストールをしようとしている時点でそれは当然理解しているかもしれないが、一応改めて説明する。

2種類のURLに関する復習

例えばURLhttp://example.comをトップページとするサイトを作りたいとする。サイトのデータはサーバ上で/var/www/my-site以下にあるものとする。例えば/var/www/my-site/test.htmlというファイルが存在すればhttp://example.com/test.htmlというURLで見れる状態だ。

この時、通常のwordpressインストールをするならば、/var/www/my-site/の直下にwp-adminやwp-contentなどのフォルダ、wp-config.phpやindex.phpなどが設置されることになる。このぶちまけ状態がキモくて耐えられないですという人のためにあるのがサブディレクトリインストールだ。

この場合、例えばwordpressのデータを/var/www/my-site下ではなく/var/www/my-site/wpdat下に置くといったことができる。便宜的にwpdatとしたが任意のフォルダ名が付けられる。このように変更したとき、サイトのトップページは依然としてhttp://example.comだが、テーマのcssなどがあるwp-contentフォルダにアクセスするURLはhttp://example.com/wp-contentではなくhttp://example.com/wpdat/wp-contentになる。この「サイトを表すURL」と「サイトのwp-contentやwp-adminなどのファイルにアクセスするためのURL」がそれぞれ「サイトアドレス(URL)」と「WordPressアドレス(URL)」だ。

この2つはダッシュボードの設定>一般から設定できる。wp-config.phpでも指定できるが、それをした場合ダッシュボードからは変更できなくなる。ここで注意して欲しいのだが、「サイトアドレス(URL)」=「WP_HOME」、「WordPressアドレス(URL)」=「WP_SITEURL」で指定する。

もう一度言う。「サイトアドレス(URL)」=「WP_HOME」、「WordPressアドレス(URL)」=「WP_SITEURL」で指定する。

「サイトアドレス(URL)」≠「WP_SITEURL」だ。絶対に間違えてはならない。

このネーミングは間違いなく混乱を招くと思うのだが、なぜ放置されているのだろうか。

サブディレクトリインストール×マルチサイト

さてマルチサイトの場合、両URLは設定>一般ではなく参加サイト>サイトネットワーク管理>サイト>(各サイト)>編集>設定から変更する。ちなみにここでの表示はwp-config.php準拠なのか、「WordPressアドレス」「サイトアドレス」ではなく「Siteurl」「Home」になっている。混乱しないように。

マルチサイトには当然複数のサイトがある訳だが、各サイトごとに「WordPressアドレス」(=Siteurl)と「サイトアドレス」(=Home)が存在する。のでいじれるようになっている。問題はここから。

「各サイトの」WordPressアドレス・サイトアドレスとは別に、「サイトネットワークの」WordPressアドレス、とでも呼称すべきものが存在している。存在しているんだからしょうがない。これはwp-config.phpのDOMAIN_CURRENT_SITEおよびPATH_CURRENT_SITEで設定できる。"https://" + (DOMAIN_CURRENT_SITE) + (PATH_CURRENT_SITE)が「サイトネットワークの」WordPressアドレスになるのだと認識してくれればいい。

サイトネットワークのWordPressアドレスと、サイトネットワークの中のメインサイトのサイトアドレスは一致させる必要がある。ここがとても性質の悪いところだ。メインサイトのWordPressアドレスとサイトネットワークのWordPressアドレスを合わせても意味が無いのだ。

なお、サイトネットワークの中のメインサイトのサイトアドレス・WordPressアドレスは変更することが出来ない。データベースを直接いじれば可能だが、まあそういうことはしたくないと思うので、サブディレクトリインストールを済ませてからマルチサイト化を行うことをオススメする。

実践

サブディレクトリインストール

まずは設定>一般からWordPressアドレスを変更する。

「http://example.com」→「http://example.com/wpdat」

パスは適宜読み替えること。一度これをやったらサーバの設定の方も済むまでサイトはダッシュボードも含めて表示できないので気を付けてね。設定を間違えてしまった場合はwp-config.phpを使って設定すること。

さてH2Oの設定ファイルだが、このようにする。

hosts:
  "example.com:80":
    listen:
      port: 80
    paths:
      "/":
        file.dir: /var/www/my-site
        redirect:
          url: /wpdat/index.php/
          internal: YES
          status: 307

HTTPS化とかする人も多いと思うのでそこはよしなに。

重要なのは、index.phpがwpdatの下にあるため、内部リダイレクトは/index.php/ではなく/wpdat/index.php/に届けるというところ。ここさえ押さえておけばちゃんと動く。

H2Oは各ハンドラを正常なレスポンスが出るまで順に回していくので、/wpdat/下のcssなどを指すURLがリクエストされた場合はfile.dirに引っかかり、ファイルとして存在しないURL(記事のパーマリンクなど)が来た場合はfile.dirを通り抜けて内部リダイレクトが起きindex.phpで処理される。そういう順番の問題があるのでfile.dirとredirectを逆順に書いてはいけない。

サイトネットワークを構築

H2Oの設定部分以外は大体wordpress公式通りにやればいい。

wp-config.phpにWP_ALLOW_MULTISITE云々を書きこむ。ツール>サイトネットワークの構築をやる。指示通りにwp-config.phpに書きこむ。ログインする。おわり。

サーバ(H2O)の設定をやるまでサイトネットワークのダッシュボードにアクセスできないので、サイトを増やす前にそっちをやる必要がある。

で、H2Oの設定。

hosts:
  "example.com:80":
    listen:
      port: 80
    paths:
      "/":
        file.dir: /var/www/my-site
        file.dir: /var/www/my-site/wpdat
        redirect:
          url: /wpdat/index.php/
          internal: YES
          status: 307

file.dirが1つ増えている。これは何かというと、サイトネットワークのダッシュボードにアクセスするためのものだ。サイトネットワークのWordPressアドレスはexample.com/になるため、サイトネットワークのダッシュボードのURLはexample.com/wp-admin/になる。つまりexample.com/でwpdatディレクトリ下にアクセスできる必要があるため必然的にこうなる。

サイトを追加する場合はこうなる。

hosts:
  "example.com:80":
    listen:
      port: 80
    paths:
      "/":
        file.dir: /var/www/my-site
        file.dir: /var/www/my-site/wpdat
        redirect:
          url: /wpdat/index.php/
          internal: YES
          status: 307
      "/sub-site1":
        redirect:
          url: /
          internal: YES
          status: 307
      "/sub-site2":
        redirect:
          url: /
          internal: YES
          status: 307

sub-site1,sub-site2は適宜読み替えて欲しい。

サイトネットワークにsub-site1というサイトを追加したとき、WordPressアドレスとサイトアドレスはそれぞれ「example.com/sub-site1」になっている筈である。

sub-site1の記事はexample.com/sub-site1/~~でアクセスしたい。

sub-site1のCSSなどはexample.com/sub-site1/wp-content/~~でアクセスしたい。

sub-site1のダッシュボードはexample.com/sub-site1/wp-admin/~~でアクセスしたい。

で、これらはぶっちゃけ"/"に飛ばせば全部叶う。良かったですね。一応追ってみると、example.com/sub-site1/wp-content/~~はexample.com/wp-content/~~に書き換わるのでfile.dirで処理されるし、それらでどうにかならないパスはちゃんとindex.phpに呑み込まれていく。便利。

H2Oは正規表現での内部リダイレクトとか使えない都合上、サブサイトが増えるごとに設定を増やす必要はある。リバプロ構成にしてmrubyでhttp_proxy()を使うといった手はありかもしれんが、それだけのためにリバプロ組むのもなあという気がする(個人の感想です)のでこういう感じにした。

サブサイトが増えたら、どうせ全部同じ設定なのでこういう感じにするのが良し。

hosts:
  "example.com:80":
    listen:
      port: 80
    paths:
      "/":
        file.dir: /var/www/my-site
        file.dir: /var/www/my-site/wpdat
        redirect:
          url: /wpdat/index.php/
          internal: YES
          status: 307
      "/sub-site1": &wp_config
        redirect:
          url: /
          internal: YES
          status: 307
      "/sub-site2": *wp_config
      "/sub-site3": *wp_config
      "/sub-site4": *wp_config
      "/sub-site5": *wp_config

&で名前を付けて*で同じ設定を指定できる。

余談だが、この設定ファイルならばsub-site1のWordPressアドレスを「example.com/sub-site1/wpdat」に変えたとしても動く。良く分かんないという人は何で動くのか考えて見てください。

感想

WordPressのサーバ周りのサポートはhtaccessに的が絞られている。そのため、htaccessをサポートしないWebサーバ上でWordPressを動かすに当たっては、htaccessに込められたお気持ちを読解して再翻訳する必要がある。

しかし今回やって思ったのは、ぶっちゃけ再翻訳したH2Oの設定ファイルの方がよっぽど分かりやすくねえ?ということ。本当いい加減rewritemodの黒魔術滅びねーかな。いや、レンタルサーバ上でのhtaccessは管理者権限なしでそれなりの自由度が担保出来て本当に便利なのは知ってますよ。知ってますけど…ねえ…

正規表現くらい覚えろって言われたらぐうの音も出ないが、いや出るわ。RewriteCondとか結局ただのif文なんですよとか、見てわかるかぼけ。プログラミングらしいレベルのことができるならちゃんとそういう顔してくれよ。

あとWordPressのソースコードの中、オブジェクトの命名が色々混乱を招くのが多くて消耗する。Siteという単語がサイトネットワーク内の各サイトを指してるかと思えばサイトネットワークを指してたりする。current_siteとcurrent_blogてめぇーだよてめぇー。唐突にblogとかいう単語出してきてんじゃねーよ。一生なぞなぞでもやってろ。

参考資料

http://www.cattlemute.com/2015/11/04/335/

https://wordpress.stackexchange.com/questions/175728/redirect-loop-only-for-multisite-network-admin

https://nskw-style.com/2012/wordpress/ms/wpmu-global-variables.html

https://wordpress.stackexchange.com/questions/20294/whats-the-difference-between-home-url-and-site-url

補足

  • サブディレクトリインストールを行う理由として「ログインページのパスなどが分かりにくいようにしてセキュリティ向上」といった点を挙げる記事があるが、この記事では単純に「ファイルが整理される」の一点だけであるとした。理由は2つある。 1つ目はサブディレクトリインストールを行ったところで、ログインページのURLは全く簡単に推測できるという点だ。 テーマのCSSやJSのパスはWordPressアドレスで示される。つまりユーザーが普通にサイトにアクセスした時点で、ログインページのあるWordPressアドレスはユーザーに伝えられている。あとはそこにwp-login.phpと付け足せば良いだけだ。 たったそれだけの手間も惜しむ雑なハッキングはしのげるかもしれないが、それ以上の意味は無い。 またそもそもセキュリティの向上は適切に長く複雑なパスワードがあれば十分なはずである。そして本当にログインページを隠匿したいならばLogin Page Rebuilderなどのプラグインを用いればいい。 2つ目の理由は、この記事で紹介した方法を行った場合wp-login.phpのパスは変わらないという点だ。 サブディレクトリインストールを行ったのになぜ?と思うかもしれないが、とても単純な話である。「メインサイトのWordPressアドレス」の下にあるものとしてexample.com/wpdat/wp-login.phpにアクセスできるほかに、「サイトネットワークのWordPressアドレス」の下にあるものとしてexample.com/wp-login.phpにアクセスできるのである。メインサイトのWordPressアドレスとサイトネットワークのWordPressアドレスはそれぞれ別にあり、そしてそれぞれにダッシュボードがあり、ログインページが存在する。サイトネットワークのWordPressアドレスとメインサイトのサイトアドレスは一致させなければならない以上これは動かしようがないのだ。
  • PATH_CURRENT_SITEを「サイトネットワークのWordPressアドレスである」と定義したのは実はこの記事の独自だ。というか公式ドキュメントでPATH_CURRENT_SITEとは何かという定義が見つけられなかった。 PATH_CURRENT_SITEはWordPressのソースコード内において、後ろに直接wp-adminなどのパスが付けられるものとして運用されているようなので、サイトアドレスかWordPressアドレスかで言ったらWordPressアドレスの筈、という推論である。しかし実態としては、wp-admin/network/admin.phpでメインサイトの「サイトアドレス」と一致の検証が行われる。なのでここを見る限りではサイトアドレスとして使われている。開発陣の中でも有耶無耶になっているんじゃ?という気がしている。 なお、PATH_CURRENT_SITEをメインサイトの「WordPressアドレス」に合わせてしまうとリダイレクトループになってしまうのだが、redirect_network_admin_requestというフィルターフックでリダイレクトを防げば「サイトネットワークのWordPressアドレス」と「サイトネットワークのメインサイトのサイトアドレス」を合わせる必要は無くなる。WordPress公式がサポートする方法では無さそうだが、個人的にはこっちのほうが設計運用として正しい気がしている。