【Next.js】WordPressをヘッドレスCMS化させる方法

Blog Wordress

こんにちは。あっきーです。
シェアハウスだと、同居人からご飯とかもらえたりするけど結局お返し等で出費が増えて、むしろマイナスになることに気付いた、フリーランスエンジニアです。

今回は、Next.jsを使ってWordpressをヘッドレスCMS化させてみよう!という話です。
Wordpressをそのまま使うんじゃなくて、ちょっといじることでパフォーマンスを良くしていくことが目的です。

環境

  • WordPress: v5.8
  • Vercel(サーバー)

ヘッドレスCMSとは、「記事管理だけを行うCMS」

そもそも、ヘッドレスCMSとは何ぞや?という話から入ります。

CMSは「Contents Management System」の略です。ざっくり言うと「記事とか画像とかを楽々管理しちゃうよ!」っていうものです。
ここでいう管理っていうのは、「追加、更新、表示」すべて表しています。

代表的なのはやっぱりWordpressですね。このサイトでも使っていますし、多くのWebサイトでも導入されているんですが、記事を更新して表示させることができています。

ヘッドレスとは「headless(ヘッドがない)」。ここでいうヘッドは表示部分という意味で、それがないことを言ってます。
なのでヘッドレスCMSはコンテンツの管理だけをするCMSということです。

  • 普通のWordpress: バックとフロントが一体化している
  • ヘッドレスCMS: バックとフロントが分離している

WordPressをヘッドレス化するメリット

「わざわざ表示部分を分離して何の意味があるの?」という疑問が当然ありますが、ヘッドレスCMSにはいくつかメリットがあります。

  • パフォーマンスが良くなる
  • 管理がしやすい
  • セキュリティが強くなる

フロントにNext.jsなどの言語を使えて表示速度などが上がる

WordPressにはショートコードやプラグイン、フックなどによって様々な機能を追加できますし、テーマを作りこむことできれいなwebサイトも制作できます。また、記事の管理から表示までを扱えるので何でも屋なんですね。

が、それが逆にパフォーマンスを下げてしまうことも多いです。
一番影響しているのが表示速度ですね。

その表示部分をフロントエンドの言語に任せてしまえば、表示速度を上げることが可能です。
例えば、Wordpressでフロントまで作るのと、Next.jsでフロント部分を作るのではこんな違いがあります。

  • WordPress: URLでアクセス->データベースから記事を取得->表示させる
  • Next.js: サイト上にあらかじめHTMLファイルを作成->アクセスがあったら用意してあるHTMLファイルを見せる

WordPressではアクセス毎に「データ取得->表示」が行われますが、Next.jsでは「あらかじめ用意したHTMLを表示」ができるので、「データを取得する」という手間が省けます。
これによってページ速度が爆速に上がるんですね。

サーバー側、フロント側で修正などがしやすい

WordPressは「Wordpressテーマ」によってフロント側が作られています。また、同時に記事管理などサーバーサイドの役割も担っています。
これは便利でもあるんですが、管理がしにくいというデメリットもあります。

ある役割に特化してもらった方が、それに集中できるので何かバグやパフォーマンス低下があったときに対処しやすくなります。

セキュリティを強化できる

WordPressはセキュリティが問題視されています。例えばメールアドレス漏洩などの問題が割と起こっています。
これも今まで見た通り、フロントとバックの両方を担っているからです。
逆に言えば、フロントからバックに侵入しやすいです。
実際にブラウザで「検証」からソースコードをみればwp-contentなどWordpress特有の文字列がたくさん見られますし、URLの後ろにwp-adminとつければ管理画面にアクセスできてしまいます。

バックには記事データなどサイトに関する情報が入っているのでそこに侵入されちゃたら大事な情報がダダ漏れですよね。

ただ、フロントとバックを分離させてしまえば解決できます。

WordPress用とフロント用に別々にドメインを与えてあげて、フロントではそのWordpressのURLを隠してあげれば、Wordpressへの侵入リスクはかなり減ります。

WordPressヘッドレス化のデメリット

メリットを見てきましたが、当然デメリットはあります。
それは扱いづらく、開発が面倒ということです。

  • 本来ならWordpressにテーマをぶち込んであげればできるのに、テーマの代わりにフロント側をNext.js等で作ってあげないといけない
  • となると、巷で売られているテーマはすべて使えない
  • WordPressで使えた機能(ショートコード)などはNext.js側で自作しなければいけない
  • SEO対策も自分で(WordpressならAll in One SEOプラグインで一発だったんだけど…)
  • そもそもヘッドレスCMSなら別にある

まあ、使いやすさとパフォーマンスはトレードオフなので、こればかりはしょうがないですね。

最後に関しては、ContetfulやmicroCMSといった初めからヘッドレスCMSとして作られたものがあるんですが、Wordpressは親しみがあるのでデメリットかどうかは人によりけりです。

WordPressをヘッドレス化させる

と、ヘッドレスCMSに関して前提知識を頭に入れてもらったところで開発に行きましょう。

WordPressとNext.jsが使える環境がすでにある想定で話していきます。

プロジェクトを立ち上げる

まずはプロジェクトの作成から。
Next.jsの公式ページに行くと、ヘッドレスWordpressのデモが用意されていて構築方法も書いてあります(天才!)。というわけでこちらを遠慮なく使わせていただきましょう。

とりあえず、デモリソースを取り込みます。(ソースはこちら

npx create-next-app --example cms-wordpress cms-wordpress-app

次にWordpress管理画面に行き、WPGraphQLプラグインをインストールしてください。

GraphQLの設定に行くと、一番上にGraphQL Endpointという項目があると思います。
ここにあるURLをNextJsプロジェクトの.env.localWORDPRESS_API_URLに入れてください。


// .env.local
WORDPRESS_API_URL=https://example.com/graphql //追加

# Only required if you want to enable preview mode
# WORDPRESS_AUTH_REFRESH_TOKEN=
# WORDPRESS_PREVIEW_SECRET=

最後にNextJSアプリ内で以下のコマンドを実行してください。


npm install
npm run dev

http://localhost:3000にアクセスしてデモサイトのように記事一覧が表示されれば成功です!

エラーが出た場合

人によってはアクセスしたときに以下のようなエラーが出るかもしれません。

Invalid src prop (https://secure.gravatar.com/avatar/ec2ed1ab4a9fde8e4b6d6773274b0139?s=96&d=mm&r=g) on `next/image`, hostname “example.com” is not configured under images in your `next.config.js`

これは、「next.config.jsファイルでドメイン設定がないと画像を挿入できないよ」っていうエラーです。
なのでnext.config.jsを作成して、以下のように追記してください。


module.exports = {
  images: {
    domains: ['example.com'] // ここにドメインを追加
  },
}

再度npm run devを実行してください。うまく表示されます。
エラー解消されない場合、まだ追加できてないドメインがあるかもです。
エラー文のhostname "example.com" is not configuredの”exmaple.com”をさっきの配列に追加して再度実行してみてください。

プレビューを表示させる

公開記事は表示できたのですが、プレビューはまだ表示できません。
プレビューはWordpress側で表示されるので都合が悪いですね。
NextJs側でプレビュー画面を開けるようにしましょう!

やるべきことは2つ

  • WordPress認証を行う
  • プレビューURLを変更

認証を行う

フロント側からとれる記事は公開されたものだけです。プレビューは公開前の記事なので、今の状態ではそのデータを取得できず、プレビューもできません。

公開前記事を取得するにはWordpressにログインする必要があります。それをやっていきましょう。

WPGraphQL JWTプラグインをインストールしていきます。
こちらのGitHubからzipファイルでリソースをダウンロードして、Wordpress管理画面のプラグインから新規アップロードをしてください。
(プラグイン検索でこちらのプラグインが出てこないのでこの方法を取ってます。)

このプラグインを入れると、設定が完了するまで記事を取得できなくなるので気を付けてください!

次にWordpressファイルの1つwp-config.phpに以下を追記します。
(サーバー上にWordpressファイルがある場合はFTPソフトなど使って取得&更新してください)


define( 'GRAPHQL_JWT_AUTH_SECRET_KEY', 'ランダム文字列' );
wp-config.phpファイルに「/* カスタム値は、この行と「編集が必要なのはここまでです」の行の間に追加してください。 */」的な箇所があると思うのでそこに書いた方が無難です。

ランダム文字列はジェネレータなどを使ってコピペしてください。
例) https://api.wordpress.org/secret-key/1.1/salt/

また、同じくWordpressファイルにある.htaccessに以下を追記しましょう。

SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1

WordPress管理画面でGraphiQL IDEを開いてください。
以下のコードを入れて実行してみましょう。


mutation Login {
  login(
    input: {
      clientMutationId: "適当な文字列"
      password: "wordpressログインパスワード"
      username: "wordpressログインユーザ名"
    }
  ) {
    refreshToken
  }
}

NextJsプロジェクトの.envファイルを編集します。


WORDPRESS_AUTH_REFRESH_TOKEN="さっきのrefreshToken"
WORDPRESS_PREVIEW_SECRET="適当な文字列"

設定は以上で完了です。http://localhost:3000/api/preview?secret=(WORDPRESS_PREVIEW_SECRET)&id=(投稿ID)でアクセスするとプレビューが見られます。

プレビューURLを変更

プレビューが表示されるようになりましたが、wordpressのプレビューボタンからは表示されないので、リダイレクトさせましょう。

wordpressテーマにあるfunctions.phpに以下のコードを書いてください.


/**
 * redirect to custom preview link
*/
function preview_link($link) {
	global $post;
	$secret = 'ここに.envファイルのWORDPRESS_PREVIEW_SECRET';
	$new_link = 'http://localhost:3000/api/preview?secret='.$secret.'&id='.$post->ID;
   return $new_link;
}
add_filter('preview_post_link', 'preview_link');

これで、プレビューボタンを押すとNext.js側で表示されます。

Vercelにデプロイする

ここはすでにデプロイしていればここは飛ばしてOKです。

Next.jsはVercelにホストすると相性がいいのでこちらを使いましょう。
個人利用なら無料ですし、ドメインはサブドメインが与えられるので完全無料です。

方法は超簡単です。

  • GitHubにデプロイ
  • Vercelで新しいプロジェクトを作成
  • デプロイしたリポジトリを選択する

流れに沿って設定すれば完了します。

なお、envファイルはVercel側で作成する必要があります。
作成したプロジェクトのsettings -> Environment Variablesで作成できます。
WORDPRESS_API_URLなどをこちらで入力してください。

Vercelの自動ビルドを実装する

Next.jsはSG(Static Generation)という機能があります。これはビルド時(Vercelに更新時)にデータを取得し必要なHTMLファイルを先に作っておくというものです。

最初にHTMLファイルを作るので表示速度が速い反面、再ビルドしないとデータが更新されません。
例えば記事更新をwordpress側でしても、next.js側では表示されないということになります。

ということで、記事更新したときにVercel側で自動ビルドしてもらえるように設定をしましょう。

Vercelでプロジェクト内でsettigns -> Git -> Deploy Hooksに行きましょう。

ここでフックを作成してURLをコピーしてください。
フック名をdeployToVercel, mainとかしておけばいいでしょう。

wordpressテーマ内のfunctions.phpに以下のコードを追記してください。


/**
 * deploy to vercel when a post is created or updated
 *
 * @return JSON
 */
function deploy_to_vercel() {
  $url = ''; // ここに先ほどのフックapiを貼る
	if($url) {
	  $context = array(
		'http' => array(
		  'method'  => 'POST',
		)
	  );

	  $json_res = file_get_contents($url, false, stream_context_create($context));
	  return $json_res;
	}
	return false;
}
add_action('save_post', 'deploy_to_vercel');

先ほど作成したAPIを呼び出すとVercelが再デプロイしてくれます。
Wordpress側では記事を投稿・編集したときにこのAPIを呼び出すようにしています。

これで、新しい記事も反映されるようになりました。実際に試してみてください。

なお、ビルド完了には少し時間がかかるのですぐにフロント側で反映されないです。

画像を別ドメインに格納する

wordpressにアップロードした画像などのファイルはwp-content/uploadsに保存されるんですが、サイト上で画像ソースを見ればwordpressだとすぐに分かってしまいます。

画像などはwordpressが入ったドメインとは別のドメインに格納しておけばセキュリティが強くなります。
なので、画像ファイルようのドメインを作ってそこにアップできるようにしましょう。

ドメインは新しく取得しても良いですが、サブドメインでも問題ないです。file.example.comなどとしておきましょう。

wordpressのドメインで末尾にwp-admin/options.phpにアクセスしましょう。
すると、wordpress全般の設定に行けます。

その中で以下の2つの項目を編集してください。

  • upload_path: 画像を格納する相対パス
  • upload_url_path: 画像を格納する絶対パス

例えばfile.example.comがファイル用のドメインで、このドメインをwordpressファイルの直下(public_htmlの直下など)に格納している場合は以下のようになります。

  • upload_path: file.example.com/files
  • upload_url_path: https://file.example.com/files

こうすると、サブドメインのfilesディレクトリに保存されます。
実際にwordpressから何か画像をアップしてみてください。指定した場所に保存されているかと思います。

こうすれば、フロントが画像を取得するときにこのファイル用ドメインを使うので、wordpressが入ったドメインを隠すことができます。

これにてヘッドレス化完了です。

おまけ:パフォーマンスはどうなる?

最後に、当サイトで現wordpressとヘッドレスwordpressでページ速度がどれだけ違うか調べてみました。

現wordpress

ヘッドレスwordpress

ヘッドレス化だとまさかの100点です。
僕のサイトは普通のwordpressで、パフォーマンスが良いテーマを使っているのであまり大きな変化が無いんですが、そんなハイパフォーマンスなテーマの上を行っています(やるねぇ)。

もちろん、スターターキットをそのまま使っていて、凝った機能を追加していないので100点なのかもしれませんがこれは大きなパフォーマンス改善につながりそうですね。

まとめ

というわけでWordpressヘッドレス化でした。
まあ、ユーザ目線で見ると仕組みがガラッと変わるのでこの方法がもっと浸透してから導入した方が良さそうですね。

開発者としてはヘッドレス化は知っておきたいところです。

それでは、また。

スポンサードサーチ

オススメ英語学習用SNS "Our Dictionary"

人気記事英語学習用SNSをLaravelで作ってみた【システム解説あり】