意外と見落としがち!モバイル切替に必要なVary HTTPヘッダー
モバイルサイトの実装方法として今はレスポンシブウェブデザインが主流ですが、他にもダイナミックサービングやセパレートURLを使うことがありますよね。そんな時に見落としがちなのが、Vary HTTPヘッダーです!
この記事ではVary HTTPヘッダーが必要な理由と設定方法、注意点をお伝えします!
Vary HTTPヘッダーが必要な理由
ダイナミックサービング(動的な配信)やセパレートURL(別々のURL)では、訪問者のデバイス(端末)のUserAgent(ユーザーエージェント)によって、パソコンならこのコンテンツ、スマホならこのコンテンツといった具合にコンテンツの切り替えを行っています。
※モバイルサイト実装の種類についてはこちら
bbnyuyu.hatenablog.com
そこでGoogleではページのキャッシュやモバイルページのクロールをスムーズに行うために、Vary HTTPヘッダーを使ってクローラー(Googlebot)に「このページはUserAgent(ユーザーエージェント)によって違うコンテンツを返すよ」と伝えることが強く推奨されています。
Vary HTTPヘッダー(Vary: User-Agent)の設定方法
それではVary HTTPヘッダーの設定方法ですが、HTTPレスポンスヘッダーで「Vary: User-Agent」を指定します!
WebサーバーがApacheなのかNginxなのかによって実装方法が違いますので、2パターンお伝えします!
Apacheの場合(.htaccess)
.htaccessに以下を追記
#mod_headersモジュール <IfModule mod_headers.c> #GooglebotにUserAgentによって異なるリソースを返すことを伝える Header append Vary "User-Agent" </IfModule>
Nginxの場合(nginx.conf)
nginx.confに以下を追記
#GooglebotにUserAgentによって異なるリソースを返すことを伝える add_header Vary 'User-Agent';
CGI版PHPのWebサーバーの場合は注意が必要
ちなみに上記の.htaccessの設定では、CGI版PHPのWebサーバー(ロリポップなど)環境だとphpファイルに対して設定できないことがあります。。
少しややこしい説明をすると、ApacheのバージョンなどによってCGI版では、.htaccessのmod_headersモジュールがHTTPレスポンスヘッダー生成前に実行されてしまうので、Headerディレクティブ(Header ○○)自体が効かないことが原因です。
その際は、php以外のファイルのために上記.htaccessの設定はそのままにしておいて、それとは別にphpファイルに対してheader関数で設定します!
header関数の設定方法
header.phpなどのヘッダー共通phpファイルや個別のphpファイルの最上部に以下を追記。
/*************************************************************
以下 header関数
CGI版PHPで「.htsccess」内のmod_headersが効かないため
*/
//GooglebotにUserAgentによって異なるリソースを返すことを伝える
header('Vary: User-Agent');
/*
以上 header関数
*************************************************************/
WordPressサイトの場合
functions.phpから全ページに適応されるように以下を追記。
/*************************************************************
以下 header関数
CGI版PHPで「.htsccess」内のmod_headersが効かないため
*/
function add_mod_headers() {
//GooglebotにUserAgentによって異なるリソースを返すことを伝える
header('Vary: User-Agent');
}
add_filter('send_headers','add_mod_headers');
/*
以上 header関数
*************************************************************/
さて、この記事ではVary HTTPヘッダーが必要な理由と設定方法、注意点をお伝えしてきました。
実際にはクローラーはページのキャッシュやモバイルページのクロールなどを様々な要素から判断しますので、Vary HTTPヘッダーを設定しないとサイトがどうにかなってしまう!というわけではないですが、スムーズに正しく認識してもらうためにも、ぜひご活用ください!
【SEOが不安】display:noneにならないアコーディオンの実装

WebページのUI/UXの一つとして展開式コンテンツ(アコーディオン/ドロップダウンなど)がよく使われますよね。特にモバイルページでは比較的サイズの小さい画面で情報を効果的に見せることができて重宝されます!
さて、その展開式コンテンツ(アコーディオン)ですが、SEO的に問題があるんじゃないかとよく議論されます。
どういうことかというと展開式コンテンツ(アコーディオン)は、初期状態では中身のコンテンツは隠れているので、その隠れたコンテンツ部分はクローラーに正しく認識されずインデックスされない(評価されない)のではないかということです。
そこでこの記事では展開式コンテンツ(アコーディオン)に関するGoogleの見解や懸念点、display:noneにならない実装方法についてお伝えします!
Googleの見解
まずは、2014年にGoogleのジョン・ミューラー氏が
初期状態で表示されないコンテンツも認識できるが、重要なコンテンツなら見えるようにしておくべき。
ただし、PCで初期状態で見えるようにしていればモバイルサイトでは隠れたコンテンツとなっていても問題はない。
という趣旨のアナウンスをされています。
当時はMFI(モバイルファーストインデックス)適応前なので、PCページがインデックスされ評価されていました。
その後、2016年のMFI導入発表以降、Googleのゲイリー・イリェーシュ氏やGoogle公式Twitterなどにより、MFIではモバイルページがインデックスされ評価されるので、初期状態で隠れたコンテンツでも問題はないとアナウンスされました。
結論としては、現在は展開式コンテンツ(アコーディオン)でも評価が下がることがないので問題はありません!
展開式コンテンツ(アコーディオン)の懸念点
Googleが明言しているとおり、実際にもGoogleヘルプページなどでdisplay:noneで隠れたコンテンツ部分をテキスト完全一致(site:ドメイン "テキスト")で検索してみると、ちゃんとヒットしますしスニペットでも表示されるので、しっかりクローラーに認識されインデックスされていることが確認できます。
じゃあ問題ないじゃん!という話なのですが、いくつか懸念があります。
まず、展開式コンテンツ(アコーディオン)にはいくつか実装方法があり、
例えば、隠したいコンテンツ部分をCSSでdisplay:noneとする方法や、初期状態では読み込まず展開時にAjaxで非同期でコンテンツを読み込む方法など。
Googleは初期状態で隠れたコンテンツ全般のことを指してはいますが、特定の実装方法に対する言及はしていませんので、全般的に問題はないのだろうとは思いますが、少なくとも非同期で後からコンテンツを読み込む方法だとさすがのクローラーもスムーズに認識できないのではないかと思うわけです。
また、CSSのdisplay:noneは本来要素の非表示を意味するので、通常はコンテンツ評価の観点でクローラーは中身を(認識できてはいるものの)無視しやすいものと思われます。
ちなみに展開式コンテンツ(アコーディオン)の実装でよく利用されるjQueryのslideToggleやslideUp/slideDownだと隠れたコンテンツ部分はdisplay:noneです。
他にも、こちらは実装方法の違いではありませんが、隠れたコンテンツが有るページと無いページではランキングに差が出たという海外事例があり、その海外事例で使用されたとされているプラグインも隠れたコンテンツはdisplay:noneで実装されています。
そういったことから、同じ展開式コンテンツ(アコーディオン)でも実装方法によって認識のしやすさが異なるのではないかと予想できます。
そこでGoogle公式コンテンツはどうしているのかなと思い確認してみると、先ほど例にあげたGoogleヘルプページのようにdisplay:noneで隠れているもの以外にも、
隠したいコンテンツを別のhtml要素で囲み、そのhtml要素の高さを0にすることで、見た目は隠しつつdisplay:noneとなっていないものがありました。
これならモヤモヤが解消できる!ということで、同じような展開式コンテンツ(アコーディオン)の実装方法をご共有します!
display:noneにならないアコーディオンの実装
jQuery(CDN)の読み込み
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
jQueryの実装
隠したいコンテンツをdiv要素で囲み、はみ出て表示されないようにしつつ高さを0にして隠します。
表示する時は、隠れたコンテンツの高さを取得して、囲んでいるdiv要素の高さを取得した高さを同じにします。
<script>
$(function(){
$('.js-dropdown-box').wrap('<div class="js-dropdown-box-wrap">');
$('.js-dropdown-box-wrap').css({
'overflow': 'hidden',
'height': 0,
'transition': 'height .3s linear'
});
$('.js-dropdown-btn').css('cursor','pointer');
$('.js-dropdown-btn').on('click',function(){
if($(this).hasClass('js-dropdown-btn-on')){
$(this).next('.js-dropdown-box-wrap').css('height','0');
$(this).removeClass('js-dropdown-btn-on');
}else{
var target_height = $(this).next('.js-dropdown-box-wrap').children('.js-dropdown-box').outerHeight();
$(this).next('.js-dropdown-box-wrap').css('height',target_height);
$(this).addClass('js-dropdown-btn-on');
}
});
});
</script>
htmlの記述
<h2 class="js-dropdown-btn">ボタン</h2> <p class="js-dropdown-box">コンテンツ</p>
さて、この記事では展開式コンテンツ(アコーディオン)に関するGoogleの見解や懸念点、そしてdisplay:noneにならない実装方法をお伝えしてきました。
実際にはクローラーは非常に優秀で「隠しテキストと隠しリンク - Search Console ヘルプ」といったガイドライン違反も判別できるように、display:noneでも全然判別可能なはずですので、Googleの見解通りあまり気にせず展開式コンテンツ(アコーディオン)を実装しても問題ないかと思います。
ここまで書いておいてなんですが、現状は僕もあまり気にしません!
実は過去にせっかく実装したので、備忘録的に記事にしておきたいなと思ったのが本音です。。笑
それでもどうしても不安を拭い去っておきたいというような、いざというコンテンツがあれば、僕もこちらの方法で実装します。
あくまで、念のためのおまじない的な感じでご活用いただければ幸いです^^
※参照記事
[続報] 重要なコンテンツならタブや展開ボタンで隠さないほうがいい | 海外SEO情報ブログ
Googleの説明とは異なり、タブで隠されたコンテンツはMFI後であっても評価が下がるのか? | 海外SEO情報ブログ
WordPressサイト制作で知っておくべきメインループとカスタムループ
WordPressでサイト制作をする時に必ずお世話になる「ループ」。WordPress初心者の多くがこのループにハマリますよね。
このループにはメインループとカスタムループ(サブループ)というものがあるのですが、「メインループがどうだこうだ」「この仕様だとカスタムループ(サブループ)が必要だ」などなどループがよくわからないとまったく意味がわかりません。
そんな方のためにループとクエリーの関係やメインループとカスタムループ(サブループ)、そして実装例をお伝えします!
メインクエリーとループの関係とは
WordPressでは各ページにメインクエリーなるものが与えられています。そこでWordPressで用意されているループを使用すると、クエリーに基づいてページに必要な情報をデータベースから取得でき、ダッシュボード上で投稿したページの一覧やページタイトル、リンク、本文、画像などが表示できます。これがWordPressサイトの基本の仕組みです!
そんなループですが、ページによってはメインクエリーの内容だけだと十分じゃない場合が多々あります。
例えば、投稿ページで関連記事として同じカテゴリーの投稿一覧を表示する場合など。。
上記の例だと、メインクエリーは投稿ページに関する情報は保持しているのですが、同一カテゴリーの投稿一覧に関する情報は保持されていません。
そこであらかじめ用意されているメインクエリー以外にも欲しい情報を取得できるクエリーが必要!ということになるんです!
メインループとカスタムループ(サブループ)
では、欲しい情報を取得できるクエリーが必要な時にどうすればいいか。。そこでカスタムクエリー(サブクエリー)の登場です!
カスタムクエリーはあらかじめ用意されているメインクエリーとは別で条件を指定したクエリーを使うことができます。例えば先ほどの例だと、〇〇カテゴリーの投稿を〇件というような条件を指定します。
このようにメインクエリー以外にも任意の条件でカスタムクエリーを使うことで、サイトに色々なコンテンツや機能を入れることができるんです!
そしてやっと本題のメインループとカスタムループ(サブループ)とはなんぞやということですが、
メインループはメインクエリーを扱うループ。
カスタムループ(サブループ)はカスタムクエリーを扱うループ。
ということです!
メインループとカスタムループ(サブループ)の実装例
最後のメインループとカスタムループ(サブループ)の実装例をご紹介します。
以下そのままコピー&ペーストなどしてお使いください!
メインループの例
<div class="c-post">
<?php
if(have_posts()){
while(have_posts()){
the_post();
?>
<div class="c-post__item">
<a href="<?php esc_url(the_permalink()); ?>" class="c-post__item__link">
<h3 class="c-post__item__link__tit"><?php the_title(); ?></h3>
</a>
</div>
<?php }else{ ?>
<p class="c-post__item--none">まだ投稿はありません</p>
<?php } ?>
</div>
カスタムループ(サブループ)の例
投稿タイプが投稿、タクソノミーがカテゴリーでスラッグがtestの投稿を4件取得するループ
<div class="c-post">
<?php
$args = array(
'post_type' => 'post',
'posts_per_page' => 4,
'tax_query' => array(
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => 'test',
),
),
);
$the_query = new WP_Query($args);
if($the_query->have_posts()){
while($the_query->have_posts()){
$the_query->the_post();
?>
<div class="c-post__item">
<a href="<?php esc_url(the_permalink()); ?>" class="c-post__item__link">
<h3 class="c-post__item__link__tit"><?php the_title(); ?></h3>
</a>
</div>
<?php
wp_reset_postdata();
}else{
?>
<p class="c-post__item--none">まだ投稿はありません</p>
<?php } ?>
</div>
さて、この記事ではメインクエリーとループの関係、メインループとカスタムループ(サブループ)、それから実装例をお伝えしてきました。
はじめは超絶わかりやすく説明しようと息巻いていたのですが、書いているうちにだんだん難しくなり、いつの間にか諦めてしまっていました(すみません)。。
全くわからない状態から読まれた読者さんには、もしかしたら小難しい内容だったかもしれませんが、この記事の内容を頭の片すみに置いた状態で、もうしばらくWordPressサイト制作の経験をすると、すんなり理解できるようになります!
ご参考になれば幸いです!
※参照記事
ループ - WordPress Codex 日本語版
関数リファレンス/WP Query - WordPress Codex 日本語版
サーバー上のPDFファイルを検索結果に表示させない(noindex)方法
サイトによっては、技術資料などのpdfファイルを特定のページからしか表示させたくない!というような場合がありますよね。そのような場合にpdfファイルがクローラーによってインデックス登録され、検索結果で表示されるなんてことがあっては困ります。。。
そんな時のためにWebサーバー上のpdfファイル全てにnoindexを指定する方法をお伝えします!
※ApacheベースのWebサーバーの場合。
.htaccessファイルで実装
ルートディレクトリ直下の.htaccessファイルでX-Robots-Tag HTTPヘッダーを使用します。
もし特定のディレクトリ以下のPDFファイルのみnoindexを指定したい場合(例えば「https://example.com/document/」以下のみ)は、任意のディレクトリ直下で.htaccessファイルを作成してください。
<FilesMatch "\.pdf$"> Header set X-Robots-Tag "noindex" </FilesMatch>
ちなみに他のファイル(例えばpngファイル)にnoindexを指定したい場合は以下の通りです。
<FilesMatch "\.png$"> Header set X-Robots-Tag "noindex" </FilesMatch>
また、複数のファイルタイプ(例えばpdfとpng)にnoindexを指定したい場合は以下のようにします。
<FilesMatch "\.(pdf|png)$"> Header set X-Robots-Tag "noindex" </FilesMatch>
お困りの際はぜひお試しください!
※参照記事
robots メタタグと X-Robots-Tag HTTP ヘッダーの仕様 | 検索 | Google Developers
コマンド実行!ファイル等のパーミッションを一括で変更する方法
サーバーの状況やツールの導入の際などにパーミッションを一括で変更したい時ってありますよね。
そんな時のために任意の階層以下のディレクトリやファイルのパーミッションを一括で変更する方法をお伝えします!
ターミナルソフト(通称黒い画面)を利用して以下のコマンドを実行すると一括変更可能です!
全てのphpファイル
find ./ -name \*.php -exec chmod 700 {} \;
全てのディレクトリ
find ./ -type d -exec chmod 755 {} \;
全てのファイル
find ./ -type f -exec chmod 755 {} \;お困りの際はぜひお試しください!
サーバー等にGooglebot(クローラー)がアクセス可能か確認する方法
Google AdSenseで推奨されているads.txt(サイトオーナーが広告販売者の認定を宣言するために必要)を正しく認識できているか確認する場合などに使用します。
コマンドで以下を実行して確認できます!
※Windowsでしたらコマンドプロンプトなど
アクセスできるか確認
curl -iL https://example.com/ads.txt
Googlebotで検出できるか確認
curl -iL -A "'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html).'" https://example.com/ads.txt
お困りの際はぜひお試しください!
※参照記事
ads.txt または app-ads.txt のクロールを確認する - Google アド マネージャー ヘルプ
WordPressのURLをサブからルートディレクトリに変更する方法
WordPressをWebサーバーに設置する時、ルートディレクトリ直下ではなく「wp」などのサブディレクトリに設置することがありますよね。
そうすると通常ではWordPressサイトは「https://ドメイン/wp/」のURLで表示されてしまいます。
サイトのURLは「https://ドメイン」に変更したい。
でも
Webサーバー上の設置階層は変えたくない。
といった場合の方法を3ステップでお伝えします!
(例)このような場合
「https://example.com/wp」から「https://example.com」にサイト階層を変更
WordPress側でサイトアドレスを変更
WordPressダッシュボードで「管理画面 > 設定 > 一般設定」の「サイトアドレス(URL)」を以下の通り変更して「変更を保存」。
※データベース上で変更する場合は「wp_optionsテーブル > siteurlレコード」を変更。
「https://example.com/wp」
↓
「https://example.com」