facebookやtwitterなどで画像を表示させたいけど、SPAでは表示されないということがあり、ずっと悩んでいました。
ただOGPを設定するだけならVue headなどを使って設定はできるのですが、twitter,facebookではogpが読み込まれませんでした。
SSRなどをして対処している記事もありましたが、複雑になるのであまりやりたくはありませんでした。
そこでふと思いつきました。「特定のクローラーの時だけ、静的サイトを返せばいいのでは?」と。
とっても当たり前のことでしたが、なかなか気づきませんでした。
ちなみにクローラーとは、ウェブページ間のリンクをたどることによってウェブサイトを自動的に検出してスキャンするプログラム(ロボットやスパイダーなど)の総称のことです。
SPAではOGPの設定をしようにもクローラーがjavascriptを読み込まないものがありfacebookやtwitterで画像が表示できなかったりします。主要なものだと、GoogleやPinterestは読み込んでくれるのですが、twitter,facebookは読み込みません。
全てのクローラーに対応できるわけではないのですが、facebookやtwitterで画像を表示させるためにOGPを設定してるようなものなので、ピンポイントで対処したいと思います。
今回はLaravel + vue.jsで作られたSPAを想定しています。
実装
route
web.php
Route::get('/{any?}', 'IndexController@index')->where('any', '^(?!api\/)[\/\w\.-]*')->name('index');
web.phpでindex.blade.phpを受けるとして、そこで設定をしていきます。
controller
IndexController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class IndexController extends Controller
{
// Twitter
// Twitterbot/1.0
private $twitterBot;
// Facebook
// facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)
private $facebookBot;
public function __construct()
{
$this->facebookBot = "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)";;
$this->twitterBot = 'Twitterbot/1.0';
}
public function index()
{
if (isset($_SERVER["HTTP_USER_AGENT"])) {
$user_agent = $_SERVER['HTTP_USER_AGENT'];
if ($user_agent == $this->twitterBot || $user_agent == $this->facebookBot) {
// 正規表現でurlを判定判定
preg_match('/(ja|en|cn|es)\/sample\/(.*?)\/(\d*)/', $_SERVER["REQUEST_URI"], $matches, PREG_OFFSET_CAPTURE);
if ($matches) {
return view('ogp')->with('title', $title)
->with('image_url', $image_url)
->with('description', $description)
->with('url', $url)
->with('title', $title);
}
}
}
return view('index');
}
}
user_agentで条件分岐させています。順を追ってみていけばわかると思います。tiwtterとfacebookのbotを判定してその時だけ、ogp.blade.phpを返しています。
resources/viewsに特定のクローラーに返却する用のviewを作成します。
ogp.blade.php
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<meta name="csrf-token" content="{{ csrf_token() }}">
<link rel="shortcut icon" href="/images/favicon.ico"/>
<title>{{ config('app.name', 'Laravel') }}</title>
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
<title>{{$title}}</title>
<meta property="og:title" content="{{$title}}">
<meta property="og:image" content="{{$image_url}}">
<meta property="og:description" content="{{$description}}">
<meta property="og:url" content="{{$url}}">
<meta property="og:type" content="website">
<meta property="og:site_name" content="{{$site_name}}">
<meta name="twitter:site" content="{{$url}}">
<meta name="twitter:card" content="photo">
<meta name="twitter:title" content="{{$title}}">
<meta name="twitter:image" content="{{$image_url}}">
<meta name="twitter:description" content="{{$description}}">
<meta property="fb:app_id" content="{{$fb_app_id}}">
</head>
<body>
</body>
</html>
ogpのみを設定しています。これでtwitterでもfacebookでも画像などがちゃんと表示されると思います。
まとめ
少々強引ですが、表示されるのでよしとします。