Blog

AWSとprerender.ioでSEO対策

概要

最近流行りのreactやvueなどSPAでWebアプリを作ると、サーバサイドレンダリング(SSR)をすべきか悩みます。

Googleのクローラはjavascriptを割ときちんとレンダリングしてくれるため、あまり意識なくてもいいようになりました。

一方、titleやogpなどのmetaタグをjavascriptで書き換えている場合、twitterやslackで共有した際にカードが正しく表示されない問題は依然残されています。

metaタグだけをサーバサイドレンダリングすることも可能ですが、今回は prerender.io を使い、クローラに向けてだけレンダリング済みのHTMLを返すようにしたいと思います。

prerender.io とは

javascriptをレンダリングし、javascriptタグを除いた素のHTMLをキャッシュしてくれるサービスです。

googleやtwitter等のクローラをオリジナルではなく prerender.io の方に転送することで、レンダリング済みのHTMLをクローラに渡すことができます。

例えば、このサイトのトップページをprerenderした結果は以下のようになります。

proxyの設定がされていないので、相対パスで設定されている画像やstyleが表示されていませんが、実際に使用する際は設定するので画像等も正しくクローラに渡せます。

クローラを判別して転送する方法は、以下のようにNginxやApacheで行う方法に、ExpressJS、Rails、Spring等にミドルウェアを導入する方法などがあります。

  • Nginx設定例
server {
    listen 80;
    server_name example.com;
 
    root   /path/to/your/root;
    index  index.html;

    location / {
        try_files $uri @prerender;
    }
 
    location @prerender {
        #proxy_set_header X-Prerender-Token YOUR_TOKEN;
        
        set $prerender 0;
        if ($http_user_agent ~* "googlebot|bingbot|yandex|baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
            set $prerender 1;
        }
        if ($args ~ "_escaped_fragment_") {
            set $prerender 1;
        }
        if ($http_user_agent ~ "Prerender") {
            set $prerender 0;
        }
        if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") {
            set $prerender 0;
        }
        
        #resolve using Google's DNS server to force DNS resolution and prevent caching of IPs
        resolver 8.8.8.8;
 
        if ($prerender = 1) {
            
            #setting prerender as a variable forces DNS resolution since nginx caches IPs and doesnt play well with load balancing
            set $prerender "service.prerender.io";
            rewrite .* /$scheme://$host$request_uri? break;
            proxy_pass http://$prerender;
        }
        if ($prerender = 0) {
            rewrite .* /index.html break;
        }
    }

今回はAWSのS3に置いたjavascriptをCloudFrontで配信しているケースを想定して行います。(このサイトはそのように構築されています。)

prerender.io に登録

レンダリングはprerender.ioに行ってもらうため、ここ から登録を行ってください。

そしたら後で使うので アカウント情報 からtokenをメモっておきます。

AWSで構築

全体像は以下のようになります。

prerender.io + AWS

S3に配置したhtml、javascriptをCloudFrontを経由して配信しており、2つのLambdaでクローラを判別、 prerender.io に転送しています。

少し複雑な仕組みですが、簡単に構築できるスクリプトが公開されています。

Cloudformation を開き、スタックの作成を開始します。

このときに、日本regionではなく、バージニア北部を選択するように気をつけてください。(Edgeが起動できなくてエラーが出ます。)

上のリポジトリから prerender-cloudfront.yaml をダウンロードしてきて、アップロードします。

そしたら、次の画面に表示される PrerenderToken に先程prerender.ioで発行したtokenを入力します。

あとは作成を押すだけで、CloudFront、S3、Edge全てが作成されます。

S3に自分のindex.htmlと、js、css、画像等をアップロードすれば、すぐにWebページが公開されます。

実際にprerenderされているか、コマンドで確かめることができます。

curl -H 'User-Agent: Facebot' https://${CLOUDFRONT_DOMAIN}

また、prerender.io でもどのページがキャッシュされているのか確認することができます。

まとめ

AWSでも簡単にprerender.ioを設定して、botにレンダリング済みのHTMLを渡すことができました。

SPAに関してはいろいろな意見がありますが、僕個人としてはクローラーがjavascriptエンジンを持ち、今後SSRしなくてもよくなると思っています。

それまでの間、このような応急処置もありなのではないでしょうか。

人気の記事

Jetpack ComposeとKotlin Coroutinesを連携させる

kotlin coroutinesのFlow, SharedFlow, StateFlowを整理する

Jetpack ComposeのRippleエフェクトを深堀り、カスタマイズも

Layout Composableを使って複雑なレイアウトを組む【Jetpack Compose】

Flow.combineの内部実装がすごい話

Jetpack ComposeとViewModelについて考える