【Blogger】ブログ全体にパスワードをかけるカスタマイズ
Bloggerにはパスワードで閲覧制限をかける標準機能はなく、身内や特定界隈向けのクローズドなサイトをBloggerで運営したい場合には、検索避けで済ませるか「限定公開」設定で読者を逐一招待する等の方法しかありません。
今回は外部サービスを使わず、Bloggerテンプレート内の条件分岐とJavaScriptのみで動作する「簡易的なパスワード認証テンプレート」を作成しました。
以下で、コード一式や使い方、注意点などを紹介します。
特徴
ブログ全体にパスワードをかけ、パスワードを知っている人だけが閲覧できるように設定できます。
ブログURLにアクセスするとBloggerテーマ内の条件分岐により独立したパスワード入力ページを表示します。正しいパスワードを入力した場合のみ、通常のブログページ要素が表示されます。
Bloggerサーバー側でページ内容を出し分けるため、パスワードが一致しない限りソースコードを見ても通常ページの本文内容やパスワードが露出することはありません。
以下のような用途を想定しています。
- SNSの鍵アカフォロワーにのみ見せたい
- 合言葉がわかる特定界隈の人にのみ見せたい
- グループチャット内のメンバーのみに見せたい
- ファン限定コンテンツの配信
- 完全な非公開にはせず一時的な閲覧制限をかけたい時
以下はサンプルブログ(公式テーマのEssentialを利用)です。パスワードは「Test-password」です。
導入前に行う設定
このテンプレートはページの表示を制限するものですが、Bloggerの標準機能によってコンテンツが裏から漏洩するのを防ぐため導入時は以下の設定を行ってください。
検索避け
検索エンジンにブログが表示されるのを防ぎます。
Blogger管理画面より「設定」→プライバシー→「検索エンジンに表示されるようにする」をオフにする。
サイトフィードの停止(あるいは制限)
Bloggerは標準でサイトの「フィード(RSS/Atom)」を配信しています。これを制限しておかないとサイトURLさえわかればフィードリーダー経由で内容を読まれてしまいます[1]。
Blogger管理画面より「設定」→サイトフィード→「ブログフィードの許可」を「なし」または「追記の区切りまで」「先頭のみ」にします。
「なし」に設定
- フィードによる情報の取得を完全に遮断。
- ブログ内の一部機能(関連記事表示・ページネーションなど)が使えなくなる場合あり。(フィードを用いる仕組みの場合。テーマによる。)
「追記の区切りまで」「先頭のみ」に設定
ブログの本文外の情報はフィードで取得できますが、各投稿・ページの本文で取得できる内容は以下に制限されます。
- 「追記の区切りまで」:先頭からページ内の<!--more-->タグまでの本文
- 「先頭のみ」:本文の先頭から400文字まで
- 各投稿・ページ本文内最初の画像(OGP画像として)
- タイトル・ラベル・投稿日など本文以外の情報
閲覧制限したい内容が本文のみで、関連記事表示などを使いたい場合は「追記の区切りまで」が向きます。投稿・ページ更新のたびに適切な位置に<!--more-->タグを入れたうえで画像を使わない/先頭にダミー画像を入れるなどの対応が必要です。
テーマへの導入方法
Blogger管理画面「テーマ」→「HTMLを編集」よりテーマテンプレート編集画面にて以下の4か所のコードを導入し保存します。
テーマファイルの編集を行うため、事前バックアップおよびテストブログ等で試すことを推奨します。なお、筆者はJavaScriptやHTMLについては勉強中です。筆者環境で動作確認はしていますが、コードに間違いや非効率的な部分などあるかもしれませんのでご了承ください。ご利用は自己責任でお願いします。
<head>直前に変数タグと条件分岐タグを配置
テーマの最上部、<head>タグの直前に以下を配置します。your-passwordの部分を任意のパスワード(半角アルファベット大文字・小文字)に書き換えてください。
<b:comment>headタグ前authorizationここから</b:comment>
<b:with value='"your-password"' var='authorization'>
<b:if cond='!data:authorization or (data:view.isLayoutMode or data:view.isPreview) or (data:blog.view and data:blog.view == data:authorization)'>
<b:comment>headタグ前authorizationここまで</b:comment>
なお、ダブルクォーテーション"は保存時に"に書き換えられます。
</head>閉じタグ直前にメタタグ配置
</head>の閉じタグ直前に以下のコードを配置します(リファラー対策)。
<meta content='same-origin' name='referrer'/>
なお、テーマによっては</head>のまわりがコメントアウトや条件タグで囲まれている場合があります。もし<!-- Your Style and Script before </head> is here -->等のコメントがあればその位置に配置してください。なければ</head>タグまわりを囲んでいるコメントタグや条件タグを避けて、その少し上に配置してください。
</body>閉じタグ直前にスクリプト配置
</body>閉じタグ直前に以下のコードを配置します。
<b:comment>認証関連スクリプト.通常ページ内用</b:comment>
<b:if cond='data:authorization and !data:view.isPreview and !data:view.isLayoutMode'>
<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) return;
const sessionKey = `blog_view_auth_${currentUrl.hostname}`;
sessionStorage.setItem(sessionKey, viewParam);
currentUrl.searchParams.delete('view');
history.replaceState(null, '', currentUrl.toString());
document.addEventListener('click', function(e) {
const link = e.target.closest('a[href]');
if (!link) return;
try {
const targetUrl = new URL(link.href);
if (targetUrl.host === currentUrl.host) {
if (targetUrl.pathname === currentUrl.pathname && targetUrl.hash) return;
e.preventDefault();
targetUrl.searchParams.set('view', viewParam);
const targetAttr = link.getAttribute('target');
if (targetAttr && targetAttr.toLowerCase() === '_blank') {
window.open(targetUrl.toString(), '_blank');
} else {
location.href = targetUrl.toString();
}
}
} catch(err) { }
});
})();
//]]></script>
</b:if>
なお、テーマによっては</body>の周りがコメントアウトや条件タグで囲まれている場合があります。<!-- Your Style and Script before </body> is here -->等のコメントがあればその位置に、なければ</body>タグを囲むコメントや条件タグを避けてその少し上に配置してください。
</body>と</html>の間に認証ページ用コードを配置
</body>タグの下、</html>タグの上に以下のコードを配置します(実質的に</html>タグの直前に配置することになると思います)。CSSやメッセージ内容は適宜変更してください。
<b:comment>/body閉じタグ下 パスワード入力ページ</b:comment>
<b:else/>
<b:tag name='head'>
<title>パスワード認証</title>
<meta content='width=device-width,initial-scale=1' name='viewport'/>
<meta content='noindex,noarchive,nofollow' name='robots'/>
<link href='https://cdn.jsdelivr.net/npm/kiso.css@latest/kiso.css' rel='stylesheet'/>
<style>/*<![CDATA[*/
body {
background: #fdfdfd;
color: #444;
font-size: 16px;
font-family: "Helvetica Neue", Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", "Noto Sans JP", sans-serif;
font-weight: 400;
line-height: 1.5;
}
#authorization-page {
margin: 0 auto;
padding: 2rem;
}
#auth-message {
margin-bottom: 2rem;
}
h1 {
text-align: center;
border-bottom: 1px solid #ccc;
font-weight: 600;
margin: 0 0 1.6rem 0;
padding: 0 0 5px 0;
}
p {
margin-bottom: 1.2rem;
}
#auth-message a {
color:#1971FF;
text-decoration: underline #1971FF;
}
form{
max-width: 500px;
margin: 0 auto 1rem;
}
input{
border: 1px solid #ccc;
font-size: 1rem;
padding: 15px;
width: 100%;
margin-bottom: 1rem;
}
button {
background-color: #eee;
border: 1px solid #ccc;
border-radius: 2px;
color: #333;
cursor: pointer;
display: block;
font-size:1.1rem;
margin: 0 auto;
padding: 10px;
text-align: center;
width: 100%;
transition-duration: .2s;
}
button:hover {
background-color: #ccc;
color: #444;
}
/*]]>*/
</style>
</b:tag>
<b:tag name='body'>
<div id='authorization-page'>
<b:comment>ブログタイトル表示</b:comment>
<h1><data:blog.title/></h1>
<form id='authForm'>
<div id='auth-message'>
<p>サイトを閲覧するにはパスワードを入力してください。</p>
<p>ヒントや注意事項、<a href='#'>SNSアカウント</a>など</p>
</div>
<input autocapitalize='off' autocomplete='off' autocorrect='off' id='pwInput' inputmode='latin' placeholder='password' spellcheck='false' type='text'/>
<button type='submit'>サイトを見る</button>
</form>
</div>
<script type='text/javascript'>//<![CDATA[
window.addEventListener('DOMContentLoaded', () => {
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_view_auth_${currentUrl.hostname}`;
const savedPw = sessionStorage.getItem(sessionKey);
if (viewParam) {
sessionStorage.removeItem(sessionKey);
const msgElement = document.getElementById('auth-message');
if (msgElement) {
const errorText = document.createElement('p');
errorText.style.color = '#FF4B00';
errorText.style.fontWeight = 'bold';
errorText.style.marginTop = '1rem';
errorText.textContent = '※パスワードが違います。';
msgElement.appendChild(errorText);
}
currentUrl.searchParams.delete('view');
history.replaceState(null, '', currentUrl.toString());
} else if (savedPw) {
currentUrl.searchParams.set('view', savedPw);
location.replace(currentUrl.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; }
currentUrl.searchParams.set('view', pw);
location.replace(currentUrl.toString());
});
}
});
//]]></script>
</b:tag>
</b:if>
<b:comment>パスワード入力ページここまで</b:comment>
</b:with>
<b:comment>簡易認証ここまで</b:comment>
全体の概要
全体は以下のようなコード配置になります。
<html ... >
...
<b:comment>headタグ前authorizationここから</b:comment>
<b:with value='"your-password"' var='authorization'>
<b:if cond='!data:authorization or (data:view.isLayoutMode or data:view.isPreview) or (data:blog.view and data:blog.view == data:authorization)'>
<b:comment>headタグ前authorizationここまで</b:comment>
<head>
...
<meta content='same-origin' name='referrer'/>
</head>
...
<body>
...
<b:comment>認証関連スクリプト.通常ページ内用.</b:comment>
<b:if cond='data:authorization and !data:view.isPreview and !data:view.isLayoutMode'>
<script>//<![CDATA[
//スクリプト
//]]></script>
</b:if>
</body>
<b:comment>/body閉じタグ下 パスワード入力ページ</b:comment>
<b:else/>
<b:tag name='head'>
<!--パスワード入力ページhead内タグ-->
<style>/*<![CDATA[*/
/* パスワード入力ページCSS */
/*]]>*/
</style>
</b:tag>
<b:tag name='body'>
<div id='authorization-page'>
<!--パスワード入力ページ本体-->
</div>
<script type='text/javascript'>//<![CDATA[
//スクリプト
//]]></script>
</b:tag>
</b:if>
<b:comment>パスワード入力ページここまで</b:comment>
</b:with>
<b:comment>簡易認証ここまで</b:comment>
</html>
利用上の注意
パスワード付きURLの取り扱いに注意
本システムはURLの末尾に?view=パスワードというパラメータを付与し、Bloggerの表示を制御する仕組みです。正しいパスワードのパラメータ付きURLから直接アクセスすると、パスワード入力なしでサイトを閲覧できます。パラメータ付きURLが検索エンジンに登録されるのを防ぐためにも検索避け設定を必ず行ってください。また、<meta content='same-origin' name='referrer'/>を記述したのはパスワード付きURLがリファラとして外部サーバーに記録されてしまうのを防ぐ目的です。
このパラメータはJavaScriptでURLから削除されるため、通常はアドレスバーやブックマークのURLには残りません。ただし、ブラウザ履歴には残る場合があるためサイト閲覧者がパスワード付きURLを不意に共有してしまうリスクは排除できません。[2]
また、画像そのものにはパスワードがかかりません。画像URLに直接アクセスすればパスワードなしで閲覧できます(livedoorブログ等と同じ仕様)。
個人情報、機密情報など、真に秘匿性の高い情報の保護には用いないでください。
パスワードの仕様
本テンプレートではBloggerのデータタグdata:blog.viewを利用しパスワードを判定します。仕様上、パスワードに使える文字種や設定方法にいくつかルールがあります。
使用できる文字種と記述方法
- 半角英字(大文字・小文字を区別)
- 半角ハイフン(-)
上記以外の文字(数字や日本語文字、その他の記号など)は使えません。
テーマ内の記述は以下のようにシングルクォーテーションとダブルクオーテーションでパスワードを囲みます(ダブルクォーテーション"は保存時に"に変換されます)。
<b:with value='"gQaht-Udfla-AiP"' var='authorization'>
パスワード保護を無効にする
設定値をfalseに書き換えるだけでパスワード保護を無効にできます[3][4]。パスワード記述時と異なりダブルクォーテーションは使いません。
<b:with value='false' var='authorization'>
ページ遷移時の画面のちらつきについて
パスワード入力後のサイト閲覧中に、ブログ内検索やメニュー型アーカイブ(<select>)等からページ遷移すると認証画面(パスワード入力画面)が一瞬表示された後、自動リダイレクトされます。戻るボタン操作等でも起こる場合があります。ちらつきのように感じられますが仕様です。
これはURLパラメータが外れるページ遷移が起きた際にJavaScriptでパラメータを自動的に補完しているためです。タブやブラウザを閉じるまではこの状態が維持されます。
また、JavaScriptによるURLパラメータ削除のため、アドレスバーが一瞬ちらつく場合があります。なおこの仕様により、ブラウザ履歴にはURL違いで同じページの履歴が複数残る場合があります。
認証画面のカスタマイズについて
配布した認証画面(パスワード入力画面)のデザインは必要最低限のシンプルなものです。サイトの雰囲気に合わせてHTML・CSSを自由に編集していただいて構いません。(※ブログタイトルは<data:blog.title/>を削除・書き換えれば表示されなくなります。)
ただし、JavaScriptでフォーム送信やエラーメッセージの書き換えを制御しているため、以下の要素(idやタグ構成)は削除せずご使用ください。
<form id='authForm'>:送信イベントの監視に必要。-
<div id='auth-message'>:「パスワードが違います」等のエラー文の出力先。 <input id='pwInput'>:入力文字列の取得に必要。
※認証画面はプレビューモードでは表示されません。デザイン編集の確認はテストブログ等の本番表示で行ってください。なお、一度正しいパスワードを入力すると、認証画面は自動スキップされます。これを解除するにはタブを一度閉じ、ページを開きなおしてください。
Dynamic Viewsテーマでは使えません
Dynamic
Views(動的ビュー)系テーマではこのテンプレートは使えません。ビューの切り替えにURLパラメータ?view=が使われており競合するためです。
他の一般的な構造のBloggerテーマであれば動作するはずです。筆者環境で公式テーマのContempo、EssentialやカスタムテーマJetTheme、F-light、QooQでの動作を簡単にですが確認済みです。
Bloggerで公開範囲を限定する手法の比較
Bloggerでアクセス制限や閲覧制限をかける方法はいくつかありますが、それぞれ以下の特徴があります。今回のテンプレートが利用目的に合うかご確認ください。
| 制限方法 | メリット | デメリット | 利用例 |
|---|---|---|---|
| Blogger機能「読者の権限」 | 完全な非公開化・読者限定が可能 | 閲覧にGoogleアカウント必須。招待を手動で行う必要があり、100人の上限がある。一部ガジェットや機能が使えなくなる。 | 読者を完全に限定した少人数コミュニティ向けのブログ |
| JavaScriptのprompt()を使った簡易なパスワード保護 | 導入が簡単。ページごとにパスワードや保護の有無を変更可。 | ブラウザの標準機能でページソースを表示するなどの方法で、本文やパスワードが簡単にわかってしまう。 | 閲覧注意の喚起やネタバレ配慮など簡易な保護 |
| 本テンプレート | アカウント不要・パスワードだけで誰でも閲覧可。パスワード認証前には本文ソースは読み込まれない。 | 入力後のパスワードがURLに一瞬露出し履歴にも残る。Blogger設定やURL共有にやや注意が必要。 | クローズドな趣味サイト(同人/創作/個人サイトなど)のパスワード保護や一時的な閲覧制限など。 |
※非公開ブログであってもBloggerコンテンツポリシーに従う必要があり、Bloggerの審査を受ける可能性があります。どの方法をとる場合でも機密情報や違反コンテンツをBloggerに載せることは非推奨です。
おわりに
Bloggerのデータタグdata:blog.viewとURLパラメータ?view=パスワードを使い、ブログ全体をパスワード保護するカスタマイズ方法を紹介しました。Bloggerサーバー側でページ内容を出し分ける仕組みで堅牢な保護を実現します。
お使いのブログやサイトで利用していただけると嬉しいです。もし不具合などあればコメントにてご報告いただければ幸いです。
Special Thanks
本テンプレートの根幹である「data:blog.viewを活用したサーバー側での状態判定」というアイデアは、zkreations氏の「Modo
mantenimiento」からインスピレーションを得たものです。アイデアとその共有に敬意と感謝を表します。そのアイデアをもとに、クリックイベント監視によるパラメータ付与、アドレスバーからのパスワード部分削除、sessionStorageによる自動リダイレクト、無限ループ対策などを施し、「パスワード保護テンプレート」として再設計しました。
Modo mantenimiento para Blogger



