# CSR Dynamic Rendering
CSR Dynamic Renderingとは、クローラが来たときだけサーバレンダリングを実行するアーキテクチャです。CSR Dynamic Renderingの目的は、SEOを最適化することです。CSRのデメリットの章でも見た通り、CSRはSEOで不利になる可能性があります。CSRは、クライアント側でコンテンツを生成するため、クローラがコンテンツを解析するまでタイムラグが発生してしまうからです。
その課題を解決するためには、SSRやSSGに切り替えるのが有効的です。しかし、アプリケーションの規模や開発リソースの関係で、SSRやSSGへの移行が難しいこともあるでしょう。
CSR Dynamic Renderingは、既存のCSRのアーキテクチャを変えずに、クローラが来たときだけサーバサイドレンダリングを実行することができます。そのため、導入コストが低く、SEOの最適化がしやすいというメリットがあります。Googleでは、サーバサイドレンダリングへの移行を推奨 (opens new window)していますが、とりあえずSEOだけ最適化したいとニーズには有効な選択肢になります。
# CSR Dynamic Renderingの仕組み
CSR Dynamic Renderingは、サーバ側でクローラが来たときだけコンテンツをレンダリングするパターンです。通常のアクセスの場合は、CSR同様に静的ファイルを配信します。レンダリング方法は、自前でサーバにレンダリングする処理を実装するか、Googleのrendertron (opens new window)やprerender.io (opens new window)などのサービスを使うことができます。今回は、ローカル環境でrendertronを試したいと思います。
TIP
Googleのrendertronは、現在、Dynamic Renderingを推奨していませんが、レンダリングの仕組みを理解するため使用します。
Please note that this project is deprecated. Dynamic rendering is not a recommended approach and there are better approaches to rendering on the web.
# rendertron
はじめに、rendertronの仕組みを見てみましょう。
rendertronは、Googleで開発されたDynamic Renderingをするためのツールです。内部でPuppeteerを使用して、該当のサーバへアクセスします。そして、取得したHTMLをクローラ用にシリアライズして返却します。既存のアプリケーションの構造を変えずにCSRをレンダリングできるため、コストをかけずにSEO対策したい場合は、有効な選択肢です。
rendertronサーバに対して、GET /render/<該当のURL>
の形式でアクセスすると、該当のURLのHTMLを取得してレンダリングを実行します。例えば、google.comにアクセスする場合は、次のように指定します。
http://localhost:3000/render/https://google.com
# 実装
では、rendertronを使って、CSR Dynamic Renderingの実装をしましょう。今回は、Vite + ReactのSPAアプリケーションを構築します。このアプリケーションは、Home
、About
、Blog
、Contact
ページで構成されます。
Blogページでは、サンプルのAPIからデータを取得して、リストを表示しています。非同期処理を伴うコンテンツも、rendertronでサーバレンダリングして、SEOの最適化をしたいと思います。
サーバ構成は次の通りです。
静的サーバがhttp://localhost:3001
からSPAの静的ファイルを配信します。静的サーバの前段で、Proxyサーバを設置します。Proxyサーバでクローラの判定をし、クローラが来たときは、rendertronサーバへ遷移し、サーバレンダリングを実行します。rendertronは、静的サーバにアクセスし、コンテンツを取得します。取得したコンテンツの中身をクローラ用に書き換えてから、Proxyサーバへ戻します。
上記をまとめると、クローラが来たときの流れは以下の通りです。
- Proxyサーバでクローラを判定
- Proxyサーバからrendertronサーバへ遷移
- rendertronサーバが静的サーバへコンテンツを取得
- 取得したコンテンツの中身をクローラ様に書き換え、Proxyサーバへ返す
- Proxyサーバからクライアントへ返す
まずは、rendertronをローカル環境で動かしてみましょう。GitHub (opens new window)からソースをダウンロードして、ローカルで実行します。
$ git clone https://github.com/GoogleChrome/rendertron.git
$ cd rendertron
$ yarn
$ yarn build
TIP
すでにメンテナンスがされていないためか、TypeScriptで型エラーになります。自身の環境で修正してから起動してください。
サーバを立ち上げると、http://localhost:3000
でrendertronが起動します。
$ yarn start
$ node build/rendertron.js
Listening on port 3000
続いて、Vite + ReactのSPAアプリケーションを実装します。フォルダ構成は次の通りで、http://localhost:3001
で動作するように設定しています。
spa/
├── index.html
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── App.tsx
│ ├── assets
│ │ └── react.svg
│ ├── components
│ │ └── Navigation
│ ├── index.css
│ ├── main.tsx
│ ├── pages
│ │ ├── About
│ │ ├── Blog
│ │ ├── Contact
│ │ └── Home
│ ├── routes.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── yarn.lock
yarn dev
で開発サーバを立ち上げておきましょう。
$ yarn dev
$ vite --port 3001
VITE v4.3.3 ready in 319 ms
➜ Local: http://127.0.0.1:3001/
➜ Network: use --host to expose
➜ press h to show help
最後に、Proxyサーバを実装します。Proxyサーバは、DockerのNginxを使用します。フォルダ構成は次の通りです。
proxy/
├── default.conf
└── docker-compose.yml
docker-compose.yml
を作成して、イメージにnginxを指定します。
version: '3'
services:
nginx:
image: nginx:latest
container_name: nginx
ports:
- "7000:7000"
volumes:
- ./default.conf:/etc/nginx/conf.d/default.conf
次に、default.conf
を作成し、Nginxの設定をします。
upstream rendertron {
server host.docker.internal:3000;
}
upstream static {
server host.docker.internal:3001;
}
map $http_user_agent $is_bot {
# テストのため、常にクローラをONにする
default 1;
# '~*googlebot' 1;
}
server {
listen 7000;
# クローラがきたときは、 http://rendertron/ へ遷移する
if ($is_bot = 1) {
rewrite ^(.*)$ /rendertron/$1;
}
location /rendertron/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://rendertron/render/$scheme://localhost:3001$request_uri;
}
# 通常のユーザの場合は、静的ファイルを配信する
location / {
proxy_pass http://static/;
}
}
クローラが来たときと通常のユーザが来たときで、リバースプロキシの設定をしています。クローラが来たときは、http://localhost:3000/
へ遷移し、通常のユーザの場合は、http://localhsot:3001
へ遷移します。今回はテストのために、常にクローラが来るように設定しています。
それでは、Dockerコンテナを立ち上げてみましょう。
$ docker compose up
Proxyサーバは、http://localhost:7000
でアクセスできます。試しに、http://localhost:7000/blog
にアクセスしてみましょう。
ページのリソースを見てみると、サンプルAPIのデータが初回のHTMLに含まれているのが分かります。
このようにrendertronを挟むことで、CSRのサイトでも、SSRのようにレンダリングすることができました。クローラのときだけサーバレンダリングを実行するので、ユーザーへの影響がほとんどなく実装することができます。ただ、rendertronは現在メンテナンスされていない状況です。他の選択肢として、prerender.ioなどのサービスを使うと、同じようなアーキテクチャを実現できます。
今回はローカル環境で実装しましたが、本番運用したい場合も同じような構成で実現可能です。例えば、AWSでCloudfrontを使っている場合は、Lamda@edge (opens new window)を挟むことでクローラの判定とrendertronの実装が可能でしょう。同様に、Cloudflare Workerを使って、クローラの判定とprerender.ioのサービスを繋げるような構成でも可能 (opens new window)でしょう。
間にレンダリングを挟むため、レイテンシーが発生するデメリットはありますが、導入コストが低く、アプリケーションレイヤーを変えることなく実装できるのが大きなメリットと言えるでしょう。