【Blogger】特定の投稿・ページにパスワードをかけるカスタマイズ方法
Bloggerの標準機能では、個別の投稿やページにパスワードで閲覧制限をかけることはできません。
以前の記事でBloggerの内部機能のみでサイト全体にパスワードをかける方法を紹介しました。
今回はそこから派生し、特定のラベルがついた投稿や特定の固定ページにパスワードをかけ閲覧制限をするカスタマイズ方法を紹介します。外部サービスを使わずBloggerテンプレート内の条件分岐とJavaScriptのみで動作します。
特徴
特定のラベルがついた投稿/特定のページを保護対象とし、パスワードで本文の表示を制限します。
保護対象の投稿/ページにアクセスすると、本来の本文表示部分にパスワード入力フォーム・メッセージ・スニペットを表示します。正しいパスワードを入力すると、本文全体を表示します。
Bloggerサーバー側でページ内容を出し分けるため、パスワードが一致しない限りページソースに本文内容やパスワードが露出することはありません。
ブログ内の一部コンテンツのみに閲覧制限をかけられるため、サイト全体としては通常方法で読者を集客しつつ同一サイト内で以下のような限定コンテンツを配信することができます。
- 会員・ファン限定コンテンツ
- 合言葉がわかる、よりコアな読者向けコンテンツ
- イベント参加者・購入者限定コンテンツ
- SNSの鍵アカフォロワー向けコンテンツ
- 記事の続きを限定コンテンツにする
以下はサンプルページ(デモ)です。パスワードは「Test-pass-post」です。
デモページ導入前に行う設定
カスタムロボットヘッダータグの設定
パスワード入力画面やパスワード付きURL(後述)がインデックス登録されてしまうのを防ぐため、以下の設定を必ず行ってください。※サイト全体を検索避け(ブログ設定で「検索エンジンに表示されるようにする」をオフに)している場合は、以下の設定は不要です。
- Blogger管理画面内の「設定」を開く
- 「カスタムロボットヘッダータグを有効にする」を「オン」に
以上を設定しておくことで、投稿・ページ作成時に個別に検索避けを設定できるようになります(後述)。
サイトフィードの制限(または停止)
Bloggerは標準でサイトの「フィード(RSS/Atom)」を配信しています。これを制限しておかないとサイトURLさえわかればフィードリーダー経由で内容を読まれてしまいます[1]。
Blogger管理画面より「設定」→サイトフィード→「ブログフィードの許可」を「追記の区切りまで」または「なし」にします。
「追記の区切りまで」に設定
ブログの本文外の情報はフィードで取得できますが、各投稿・ページの本文で取得できる内容は以下に制限されます。
-
「追記の区切りまで」:先頭からページ内の
<!--more-->タグまでの本文 - 各投稿・ページ本文内最初の画像(OGP画像として)
- タイトル・ラベル・投稿日など本文以外の情報
公開ブログではフィード取得可の方が関連記事表示などできて便利だと思うので「追記の区切りまで」が良いと思います[2]。パスワード保護対象の投稿/ページ編集時に本文の適切な位置に<!--more-->タグを入れたうえで画像を使わない/先頭にダミー画像を入れるなどの対応が必要です。
「なし」に設定
- フィードによる情報の取得を完全に遮断。
- ブログ内の一部機能(関連記事表示・ページネーションなど)が使えなくなる場合あり。(フィードを用いる仕組みの場合。テーマによる。)
※「なし」の場合でもスニペット表示を制限するために<!--more-->タグの挿入が必要です。
テーマへの導入方法
Blogger管理画面「テーマ」→「HTMLを編集」よりテーマテンプレート編集画面にて以下のコードを導入し保存します。
テーマファイルの編集を行うため、事前バックアップおよびテストブログ等で試すことを推奨します。筆者環境で動作確認はしていますが、コードに間違いや非効率的な部分などあるかもしれませんのでご了承ください。ご利用は自己責任でお願いします。
headタグ内にメタタグ設置
テーマ上方の<head>タグ内に以下のコードを設置します。</head>閉じタグの直前に設置で良いと思います。
<b:comment>---パスワード認証時リファラを制限---</b:comment>
<b:if cond='data:blog.view'>
<meta content='same-origin' name='referrer'/>
</b:if>
パスワード付きURLがリファラとして外部サーバーに記録されるのを防ぎます。[3]
基本コード
HTML編集画面にて、<data:post.body/>で検索(「ctrl+F」)します。多くのテーマではこれが一カ所しかないので、この<data:post.body/>を以下のコードでまるまる置き換えます。
<b:comment>---パスワード保護ここから---</b:comment>
<b:comment>---パスワード設定---</b:comment>
<b:with value='"post-pass"' var='pwPost'> <b:comment>投稿用1</b:comment>
<b:with value='"vip-pass"' var='pwVip'> <b:comment>投稿用2</b:comment>
<b:with value='"page-pass"' var='pwPage'> <b:comment>ページ用</b:comment>
<b:comment>---保護対象---</b:comment>
<b:with value='data:view.isPost and data:post.labels any (l => l.name in ["限定公開", "パスワード付き"])' var='isGroupPost'> <b:comment>ラベルグループ1</b:comment>
<b:with value='data:view.isPost and data:post.labels any (l => l.name in ["VIP限定"])' var='isGroupVip'> <b:comment>ラベルグループ2</b:comment>
<b:with value='data:view.isPage and (data:view.pageId in [0123456789, 9876543210])' var='isGroupPage'> <b:comment>ページID</b:comment>
<b:with value='data:isGroupPage ? data:pwPage : (data:isGroupVip ? data:pwVip : (data:isGroupPost ? data:pwPost : ""))' var='requiredPassword'>
<b:with value='data:requiredPassword != ""' var='isProtected'>
<b:comment>---本文表示---</b:comment>
<b:if cond='!data:isProtected or data:view.isLayoutMode or data:view.isPreview or (data:blog.view and data:blog.view == data:requiredPassword)'>
<data:post.body/>
<b:if cond='data:isProtected'>
<script>//<![CDATA[
(function(){
let rawHref = window.location.href;
if (rawHref.indexOf('?') === -1 && rawHref.indexOf('&view=') !== -1) {
rawHref = rawHref.replace('&view=', '?view=');
}
const currentUrl = new URL(rawHref);
const viewParam = currentUrl.searchParams.get('view');
if (viewParam) {
// ページごとセッションにパスワード保存
const sessionKey = 'blog_auth_' + currentUrl.pathname;
sessionStorage.setItem(sessionKey, viewParam);
// URLからパラメータ(パスワード)消去
currentUrl.searchParams.delete('view');
history.replaceState(null, '', currentUrl.toString());
}
})();
//]]></script>
</b:if>
<b:comment>---パスワード入力フォーム表示---</b:comment>
<b:else/>
<div id='authorization-page'>
<h2>このコンテンツは保護されています</h2>
<p>
<data:post.snippets.short/> <b:comment>---スニペット表示---</b:comment>
</p>
<p>続きを読むにはパスワードを入力してください。</p>
<form id='authForm'>
<input autocapitalize='off' autocomplete='off' autocorrect='off' id='pwInput' inputmode='latin' placeholder='password' spellcheck='false' type='text'/>
<button type='submit'>Enter</button>
</form>
<b:if cond='data:blog.view != ""'>
<p style='color: #FF4B00; font-weight: bold;'>※パスワードが違います。</p>
</b:if>
</div>
<script>//<![CDATA[
(function(){
const sessionKey = 'blog_auth_' + window.location.pathname;
const savedPw = sessionStorage.getItem(sessionKey);
const currentViewParam = new URL(window.location.href).searchParams.get('view');
if (savedPw) {
if (currentViewParam === savedPw) {
// 保存されたパスワードを試してこの画面に来た=パスワードが間違っていた場合
// 無限ループを防ぐためセッションをクリアする
sessionStorage.removeItem(sessionKey);
} else {
// セッションからの自動復帰
let rawHref = window.location.href;
if (rawHref.indexOf('?') === -1 && rawHref.indexOf('&view=') !== -1) {
rawHref = rawHref.replace('&view=', '?view=');
}
const currentUrl = new URL(rawHref);
currentUrl.searchParams.set('view', savedPw);
location.replace(currentUrl.toString());
return;
}
}
// パスワード間違い時(無効なパラメータ付きでアクセスした際)のURLクリーンアップ
if (currentViewParam) {
let rawHref = window.location.href;
if (rawHref.indexOf('?') === -1 && rawHref.indexOf('&view=') !== -1) {
rawHref = rawHref.replace('&view=', '?view=');
}
const cleanUrl = new URL(rawHref);
cleanUrl.searchParams.delete('view');
history.replaceState(null, '', cleanUrl.toString());
}
// フォーム送信時
const authForm = document.getElementById('authForm');
if (authForm) {
authForm.addEventListener('submit', function(e) {
e.preventDefault();
let pw = document.getElementById('pwInput').value || '';
pw = pw.trim();
if(!pw) { alert('パスワードを入力してください'); return; }
let rawHref = window.location.href;
if (rawHref.indexOf('?') === -1 && rawHref.indexOf('&view=') !== -1) {
rawHref = rawHref.replace('&view=', '?view=');
}
const targetUrl = new URL(rawHref);
targetUrl.searchParams.set('view', pw);
// 「戻る」ボタンの履歴に認証ページが重複して挟まるのを防止
location.replace(targetUrl.toString());
});
}
})();
//]]></script>
</b:if>
</b:with>
</b:with>
</b:with>
</b:with>
</b:with>
</b:with>
</b:with>
</b:with>
<b:comment>---パスワード保護ここまで---</b:comment>
コード上部でパスワードや対象のページID・投稿ラベルを設定します(後述)。
なお、本文スニペットを出力するタグ<data:post.snippets.short/>はテーマバージョン3(ウィジェットバージョン2)でのみ機能する記法です。QooQなど古めのテーマでは機能しないので代わりに<data:post.snippet/>と記述してください。
投稿用とページ用で<data:post.body/>が2か所存在するテーマ用
テーマによっては<data:post.body/>が投稿用と固定ページ用の2か所記述されている場合があります(JetThemeなど)。その場合は以下の1~3のコードをそれぞれの箇所に設置してください。
1, 投稿用
条件タグcond='data:view.isPost'などの下部にある<data:post.body/>を以下で置き換えてください。※JetThemeの場合は<b:includable id='JetPost'
var='post'>...</b:includable>の中にある<data:post.body/>を置換。
<b:comment>--- 投稿用 パスワード保護ここから ---</b:comment>
<b:comment>パスワード設定</b:comment>
<b:with value='"post-pass"' var='pwPost'>
<b:with value='"vip-pass"' var='pwVip'>
<b:comment>対象ラベル設定</b:comment>
<b:with value='data:view.isPost and data:post.labels any (l => l.name in ["限定公開", "パスワード付き"])' var='isGroupPost'> <b:comment>ラベルグループ1</b:comment>
<b:with value='data:view.isPost and data:post.labels any (l => l.name in ["VIP限定"])' var='isGroupVip'> <b:comment>ラベルグループ2 </b:comment>
<b:with value='data:isGroupVip ? data:pwVip : (data:isGroupPost ? data:pwPost : "")' var='requiredPassword'>
<b:with value='data:requiredPassword != ""' var='isProtected'>
<b:if cond='!data:isProtected or data:view.isLayoutMode or data:view.isPreview or (data:blog.view and data:blog.view == data:requiredPassword)'>
<data:post.body/>
<b:if cond='data:isProtected'>
<b:include name='passwordAuthScript'/>
</b:if>
<b:else/>
<div id='authorization-page'>
<h2>このコンテンツは保護されています</h2>
<p>
<data:post.snippets.short/> <b:comment>---スニペット表示---</b:comment>
</p>
<p>続きを読むにはパスワードを入力してください。</p>
<form id='authForm'>
<input autocapitalize='off' autocomplete='off' autocorrect='off' id='pwInput' inputmode='latin' placeholder='password' spellcheck='false' type='text'/>
<button type='submit'>Enter</button>
</form>
<b:if cond='data:blog.view != ""'>
<p style='color: #FF4B00; font-weight: bold;'>※パスワードが違います。</p>
</b:if>
</div>
<b:include name='passwordAuthScript'/>
</b:if>
</b:with>
</b:with>
</b:with>
</b:with>
</b:with>
</b:with>
<b:comment>--- 投稿用 パスワード保護ここまで ---</b:comment>
2, 固定ページ用
条件タグcond='data:view.isPage'などの下部にある<data:post.body/>を以下で置き換えてください。
※JetThemeの場合は<b:includable id='JetPage'
var='post'>...</b:includable>の中にある<data:post.body/>を置換。
<b:comment>--- ページ用 パスワード保護ここから ---</b:comment>
<b:with value='"page-pass"' var='pwPage'><b:comment>ページ保護用パスワード</b:comment>
<b:with value='data:view.isPage and (data:view.pageId in [0123456789, 9876543210])' var='isGroupPage'><b:comment>対象ページID</b:comment>
<b:with value='data:isGroupPage ? data:pwPage : ""' var='requiredPassword'>
<b:with value='data:requiredPassword != ""' var='isProtected'>
<b:if cond='!data:isProtected or data:view.isLayoutMode or data:view.isPreview or (data:blog.view and data:blog.view == data:requiredPassword)'>
<data:post.body/>
<b:if cond='data:isProtected'>
<b:include name='passwordAuthScript'/>
</b:if>
<b:else/>
<div id='authorization-page'>
<h2>このコンテンツは保護されています</h2>
<p>
<data:post.snippets.short/> <b:comment>---スニペット表示---</b:comment>
</p>
<p>続きを読むにはパスワードを入力してください。</p>
<form id='authForm'>
<input autocapitalize='off' autocomplete='off' autocorrect='off' id='pwInput' inputmode='latin' placeholder='password' spellcheck='false' type='text'/>
<button type='submit'>Enter</button>
</form>
<b:if cond='data:blog.view != ""'>
<p style='color: #FF4B00; font-weight: bold;'>※パスワードが違います。</p>
</b:if>
</div>
<b:include name='passwordAuthScript'/>
</b:if>
</b:with>
</b:with>
</b:with>
</b:with>
<b:comment>--- ページ用 パスワード保護ここまで ---</b:comment>
3, JavaScript
以下のコードを次のいずれかの中に設置します。
-
<b:widget type='Blog'>直下の<b:includable />の並びの中 -
<b:defaultmarkups>内の<b:defaultmarkup type='Common'>配下
※他の<b:includable >...</b:includable>の間に入り込まないように注意。
<b:includable id='passwordAuthScript'>
<script>//<![CDATA[
(function(){
let rawHref = window.location.href;
if (rawHref.indexOf('?') === -1 && rawHref.indexOf('&view=') !== -1) {
rawHref = rawHref.replace('&view=', '?view=');
}
const currentUrl = new URL(rawHref);
const viewParam = currentUrl.searchParams.get('view');
const sessionKey = 'blog_auth_' + currentUrl.pathname;
const authForm = document.getElementById('authForm');
if (authForm) {
// 認証失敗・フォーム表示時の処理 ---
const savedPw = sessionStorage.getItem(sessionKey);
if (savedPw) {
if (viewParam === savedPw) {
// 保存されたパスワードが間違っていた場合(無限ループ防止)
sessionStorage.removeItem(sessionKey);
} else {
// セッションからの自動復帰
currentUrl.searchParams.set('view', savedPw);
location.replace(currentUrl.toString());
return;
}
}
// パスワード間違い時(フォーム表示時)のURLクリーンアップ
if (viewParam) {
currentUrl.searchParams.delete('view');
history.replaceState(null, '', currentUrl.toString());
}
// フォーム送信時の処理
authForm.addEventListener('submit', function(e) {
e.preventDefault();
let pw = document.getElementById('pwInput').value || '';
pw = pw.trim();
if(!pw) { alert('パスワードを入力してください'); return; }
currentUrl.searchParams.set('view', pw);
location.replace(currentUrl.toString());
});
} else {
// 認証成功・通常表示時の処理 ---
if (viewParam) {
// セッションへ保存し、URLのパラメータを消去
sessionStorage.setItem(sessionKey, viewParam);
currentUrl.searchParams.delete('view');
history.replaceState(null, '', currentUrl.toString());
}
}
})();
//]]></script>
</b:includable>
投稿と固定ページで共通のスクリプトを呼び出せるよう、<b:includable>として定義しています。
使い方
デフォルトでは以下の三種類のグループ用にパスワードをそれぞれ設定できます。
- ラベルグループ1(
isGroupPost) - ラベルグループ2(
isGroupVip) - ページ(
isGroupPage)
パスワードの設定
設置したコード上方のpwPost(ラベルグループ1用)、pwVip(ラベルグループ2用)、pwPage(ページ用)のvalueにそれぞれのパスワードを設定します。
パスワードに使える文字種は以下です。
- 半角英字(大文字・小文字を区別)
- 半角ハイフン(-)
上記以外の文字(数字や日本語文字、その他の記号など)は使えません。
テーマ内の記述は以下のようにシングルクォーテーションとダブルクオーテーションでパスワードを囲みます(ダブルクォーテーション"は保存時に"に変換されます)。
<b:with value='"UKofjeasF-DlA"' var='pwPost'>
保護対象のラベル・ページIDの設定
パスワードをかける投稿はラベル、固定ページはページIDで管理します。
ラベルはダブルクォーテーション"で囲みます。複数指定したい場合はカンマで区切ります(どれか一つでも含まれればパスワード保護の対象)。
["ラベル名1", "ラベル名2"]
ページIDは引用符等で囲む必要はありません。複数指定する場合はカンマで区切ります。※ページIDはページ編集画面のURL末尾の数字列です(https://draft.blogger.com/blog/page/edit/1234567890123456789/ここの数字列)。
[0123456789123456789, 9876543210123456789]
それぞれ配布コード内の記述を参考に書き換えてください。
投稿・ページ作成時
パスワードをかける投稿・ページを公開する際には、以下の設定をしてください。
-
HTMLビューで
<!--more-->タグを挿入(または作成ビューで「追記を挿入」) - 保護用のラベルを投稿に設定 / ページIDを確認しテーマ内に記述
- 検索避け(カスタムロボットタグ)を設定
- 編集画面内右側メニュー(歯車アイコン「投稿の設定」)
- 「カスタムロボットタグ」を開く
- 「デフォルト」のチェックを外し「noindex」「noarchive」の2つにチェック
先頭から<!--more-->タグを入れた部分までがスニペットとしてパスワード入力画面に表示されます(投稿一覧やフィードで表示される部分も同様)。全く表示したくない場合は本文の先頭に挿入してください。
利用上の注意
パスワード付きURLの取り扱いに注意
本カスタマイズはURLの末尾に?view=パスワードというパラメータを付与し、Bloggerの表示を制御する仕組みです。正しいパスワードのパラメータ付きURLから直接アクセスすると、パスワード入力なしで内容を閲覧できます。パラメータ付きURLが検索エンジンに登録されるのを防ぐためにも上述の検索避け設定を必ず行ってください。
このパラメータはJavaScriptでURLから削除されるため、通常はアドレスバーやブックマークのURLには残りません。ただし、ブラウザ履歴には残る場合があるためサイト閲覧者がパスワード付きURLを不意に共有してしまうリスクを完全には排除できません。[4]
また、画像そのものにはパスワードがかかりません。画像URLに直接アクセスすればパスワードなしで閲覧できます(livedoorブログ等と同じ仕様)。
個人情報、機密情報など、真に秘匿性の高い情報の保護には用いないでください。
※ブログの掲載内容はBloggerコンテンツポリシーに従う必要があり、Bloggerの審査を受ける可能性があります。パスワードをかけていても機密情報や違反コンテンツをBloggerに載せることは非推奨です。
Blogger標準機能によるブログ内検索
Bloggerの仕様上、投稿本文はブログ内検索(検索ガジェットやURLでの検索)の対象にはなるため、検索した「特定の単語やフレーズ」がその非公開記事に含まれるか否かは分かってしまいます。「本文全体」が読まれることはありませんが、上記の仕様については事前にご理解ください。
単語・フレーズによる内容の推測も防ぎたい場合は「投稿」ではなく「ページ」で作成しパスワードをかけることを推奨します。Bloggerの「ページ」は仕様上ブログ内検索の対象にはなりません。[5]
表示要素のカスタマイズについて
デザインや表示メッセージ
上述したコードにはパスワード入力フォームのCSSは含まれません。適宜追加・調整してください。例としてサンプルページでは以下のCSSを適用しています。
/*パスワード入力フォーム*/
#authorization-page {
margin-bottom: 3rem;
}
#pwInput {
background-color: inherit;
color: inherit;
border: 1px solid #ccc;
border-radius: 2px;
transition-duration: .1s;
}
#pwInput:focus-visible {
outline: 0;
border-color: #1bb1cf;
box-shadow: 0 0 2px 2px #1bb1cf70;
}
#authForm button {
display: block;
margin-top: 5px;
color: #333;
font-size: small;
background-color: #eee;
cursor: pointer;
border: 1px solid #ccc;
border-radius: 2px;
transition-duration: .1s;
}
#authForm button:hover {
background-color: #ccc;
}
また、HTMLや文言はサイトの雰囲気に合わせて自由に編集していただいて構いませんが、フォームの送信に関わる以下のidやタグ構成は削除せずご使用ください。
<form id='authForm'>:送信イベントの監視に必要。<input id='pwInput'>:入力文字列の取得に必要。
※プレビューモードではパスワード保護表示になりません。デザイン編集の確認はテストブログ等の本番表示で行ってください。なお、一度正しいパスワードを入力すると、同じ投稿/ページでの認証は自動スキップされます。これを解除するには当該タブを一度閉じ、ページを開きなおしてください。
スニペットの長さ
スニペットは本文の先頭から<!--more-->タグまでか140字まで表示されます。もう少し長く表示したい場合は、<data:post.snippets.short/>を以下で置き換えてください。
<data:post.snippets.long/>
※上記コードが機能しないテーマ(QooQなど)では<data:post.longSnippet/>に書き換えてください。
これらコードだと最大1000文字まで表示されます。
Dynamic Viewsテーマでは使えません
Dynamic
Views(動的ビュー)系テーマではこの方法は使えません。ビューの切り替えにURLパラメータ?view=が使われており競合するためです。
他の一般的な構造のBloggerテーマであれば動作するはずです。筆者環境で公式テーマのContempo、EssentialやカスタムテーマJetTheme、F-light、QooQでの動作を簡単にですが確認済みです。
おわりに
Bloggerのデータタグdata:blog.viewとURLパラメータ?view=パスワードを使い、特定の投稿・ページをパスワード保護するカスタマイズ方法を紹介しました。Bloggerサーバー側でページ内容を出し分ける仕組みにより、強固な保護を実現しています。
投稿ごと・ページごとにパスワードを完全個別にすることはできませんが、グループ分けにより最大で3種類のパスワードを設定できるようにしました。「全体で1パスワード」よりは少しコードが煩雑になりましたが、まあ許容範囲内かと思います。
お使いのブログやサイトで利用していただけると嬉しいです。もし不具合などあればコメントにてご報告いただければ幸いです。



