【制作事例】歯科衛生士限定コミュニティをWordPressで構築した話
「資格を持つ人だけが、安心して本音を話せる場所」。この一言の裏側には、WordPressで会員制コミュニティを『壊さず』作るための設計判断が詰まっています。今回はその制作事例を、うまくいった点だけでなく、選択に伴うトレードオフまで含めてご紹介します。
今回ご紹介するのは、歯科衛生士(DH)の資格を持つ方だけが参加できるクローズドコミュニティ――いわゆる会員サイトを、WordPressで構築した制作事例です。
WordPressでの会員サイト構築は、機能を並べるだけでは終わりません。運営されているのは現役の歯科衛生士の方。
「同じ資格を持つ仲間と、職場では言いにくい悩みを本音で話せる場所をつくりたい」というご相談から始まりました。
- WordPressで会員制サイトを「壊れず・オーナー1人で回せる」形に作る具体的な設計判断
- 既製プラグイン組み合わせの光と影(依存・競合・保守コスト)と、実際のログイン不能トラブルの真因
- Code Snippets集約の是非(白画面回避 vs Git管理・スパゲッティ化)
- 「オーナー1人で回せる」の現実――運用と保守は別物、という持続可能性の線引き
会員制サイトは「作って終わり」ではありません。
登録・承認・投稿制限・退会といった会員データの流れを安全に回し、さらにオーナーがひとりで運用を続けられる状態まで持っていく必要があります。
本記事では、技術選定・設計思想・つまずいた点を、実際の作業に基づいて整理します。
そのうえで、それぞれの選択が抱える”影”――依存・保守性・運用の持続可能性――にも正直に踏み込みます。
成功事例として飾るためではなく、同じ判断を迫られる方の役に立つように、です。
案件概要 ― 会員制コミュニティの3要件「クローズド」「承認制」「心理的安全性」

この会員制コミュニティの要件を整理すると、軸は3つでした。
- クローズド(会員限定):資格保有者だけが中身を見られる。検索エンジンにも載せない(noindex運用)。
- 承認制:申請したら即参加ではなく、運営が内容を確認してから参加を許可する。
- 心理的安全性:匿名に近い形で、職場では言えない悩みを安心して吐き出せる空気をつくる。
「会員数◯人達成!」を急ぐのではなく、まず”安心して話せる箱”を正しく作ることを最優先にした案件です。
上の図は、その全体像――ライフサイクル・技術選定・”壊れても戻せる”設計――を一枚にまとめたものです。
以降では、この図の各要素を、うまくいった理由と、そこに潜むトレードオフの両面から掘り下げます。
技術選定 ― WordPress会員サイトは既製プラグインの組み合わせで車輪の再発明を避ける
会員制コミュニティに必要な機能は、実は個別に見ると「枯れた」プラグインで十分にまかなえます。
フルスクラッチで会員システムを自作するより、実績のあるプラグインを適材適所で組み合わせる方が、初期コストも小さく、個々の部品は多数の本番サイトで検証済みです。
今回の構成は次の通りです。
- Ultimate Member:会員登録・ログイン・プロフィール・承認ワークフロー(申請→保留→承認)を担当。
- bbPress:コミュニティの中心となる「トークルーム」(フォーラム)。承認済みの会員だけが投稿できるよう制限。
- wp-ulike:投稿への「いいね」機能。
- 自作テーマ:ヒーロー・トークルーム一覧・マイページなど、コミュニティ体験に必要な画面をテンプレートで実装。
- Contact Form 7:お問い合わせ導線。
- All-in-One WP Migration:検証環境と本番の行き来、バックアップ。
ポイントは、「見た目とコミュニティ体験は自作テーマで作り込み、会員管理の”心臓部”は実績あるプラグインに任せる」という役割分担です。
「組み合わせる」ことのコスト ― 依存・競合・保守
ここで一度、立ち止まって批評的に見ておきます。
プラグインの組み合わせは「車輪の再発明」を防ぐ一方で、「作らなかったコード」を他社に依存し続けるという別のコストを背負い込みます。これは”タダの近道”ではありません。
依存が増えるほど、システムの実際の複雑さは「プラグインの数」ではなく「プラグイン同士の組み合わせの数」で効いてきます。
各プラグインはそれぞれ独自の更新サイクル・独自のフック・独自の認証やセッションの前提を持っており、単体では正しくても、重ね合わせた瞬間に予期しない相互作用(競合)が生まれる。
後述する「承認済みなのにログインできない」トラブルは、まさにこの競合が表面化した実例でした。どちらのプラグインもバグではない――にもかかわらず、組み合わせると壊れたのです。
- 枯れた実装を再利用でき、車輪の再発明を避けられる
- 認証・承認・フォーラムを”作らずに”揃えられる
- 個々の部品は多数の本番サイトで検証済み
- プラグイン間の競合(今回のログイン不能がまさにこれ)
- 各プラグインの更新・EOL・開発停止に追従する保守コスト
- WP/PHPのメジャー更新が”最も遅いベンダー”に律速される
さらに長期で効いてくるのが保守コストです。
プラグインを1つ採用するたびに、その提供元の更新・脆弱性対応・そして最悪の場合の開発停止(放棄)に、こちらが付き合い続けることになります。
WordPress本体やPHPをメジャーアップデートしたくても、使っているプラグイン群のうち”最も対応が遅い1本”が足かせになり、サイト全体の更新タイミングを縛る――これは会員サイトのように長く運用する箱ほど重くのしかかります。
ではフルスクラッチが正解かというと、それも違います。
自作すれば競合は減りますが、認証・セッション・権限というセキュリティの当たり判定が大きい領域を、自前で永久に保守する義務を負います。
つまりこれは「競合・依存のリスク」と「自作・保守のリスク」のトレードオフであり、どちらもゼロにはできません。
今回の案件(運営者は非エンジニア、要件は標準的な会員制)では、実績あるプラグインに寄せる判断が妥当でした。
ただしそれは”安全だから”ではなく、依存という負債を、競合対応と更新追従で払い続ける前提で選んだ――と正直に捉えています。
設計の核 ― 実装を functions.php ではなく Code Snippets に集約した理由
この案件でもっとも重視したのが、「サイトを白画面(真っ白)にしない」ための設計です。
会員サイトには、投稿制限・退会処理・フォーム拡張など、たくさんのカスタムPHPが必要になります。
これらをテーマの functions.php にまとめて書くのが一般的ですが、ここには落とし穴があります。
functions.php に1文字でも致命的な構文エラーがあると、サイト表示だけでなく管理画面(wp-admin)まで真っ白になり、復旧しづらくなるのです。
そこで今回は、ランタイムのカスタムPHPを「Code Snippets」プラグインに集約しました。Code Snippetsには、
- 保存時に構文チェックが走り、致命的なエラーはそもそも保存されない
- スニペット単位で個別にON/OFFできる(=失敗が1機能に閉じる)
- DBに保存されるため、移行時もまるごと持ち運べる
という利点があります。「コードの一覧性」より「事故ったときに1機能だけ切り離して復旧できること」を優先した、という設計判断です。
実装の置き場所は、次のように住み分けました。
| 実装の種類 | 置き場所 | 理由 |
|---|---|---|
| ランタイムPHP(投稿制限・退会・フォーム拡張) | Code Snippets | 失敗を1機能に閉じ込め、復旧可能にする |
| テンプレート(トップ・マイページ・トークルーム) | 自作テーマのPHP | 通常のテンプレート階層で管理 |
| 見た目の調整CSS | 管理画面から編集できるプラグイン | オーナーが自分で微調整できる |
| 利用規約・プライバシーポリシー本文 | DBの固定ページ | 管理画面から編集できる |
たとえば「承認済みの会員だけが投稿できる」制限も、テーマではなくスニペットとして実装します。イメージは次の通りです(実際のフックや条件はお使いの構成に合わせます)。
// 承認済み(approved)の会員だけ bbPress に投稿できるようにする
add_filter( 'bbp_current_user_can_publish_topics', function ( $can ) {
// Ultimate Member の承認状態を確認
$status = get_user_meta( get_current_user_id(), 'account_status', true );
return ( 'approved' === $status ) ? $can : false;
} );
この数行が万一エラーを起こしても、Code SnippetsならそのスニペットをOFFにするだけでサイト全体は生き続けます。
会員サイトの”心臓部”を、いつでも安全に切り離せる状態にしておく――これが設計の核です。
もうひとつ、この実装には「見た目ではなく、権限判定そのものに割り込む」という意図があります。
未承認者に投稿フォームを”見せない”だけの制御は、URL直打ちや別経路からの送信を止められません。
bbPressが投稿可否を決めるフィルタ(bbp_current_user_can_publish_topics)に条件を差し込めば、どの経路から来ても最後に同じ判定が効きます。
UIの出し分けは”親切”のため、権限フィルタは”防御”のため――層を分けて重ねるのが、会員サイト構築の定石です。
Code Snippets 集約の是非 ― DBにコードを載せるということ
この設計は「運用の安全」という一点では非常に強い。
ですが、手放しで褒めるべきものではありません。
コードがファイルではなくデータベース(DB)に載るという一点が、エンジニアリングの観点では小さくない代償を伴うからです。
最大の論点はバージョン管理です。
functions.php やmu-pluginであればファイルなので、Gitで履歴を残し、差分(diff)でレビューし、プルリクエストで変更を議論し、CIで検査できます。
ところがスニペットはDBのレコードとして存在するため、この一連のワークフローがまるごと効かなくなります。
「いつ・誰が・なぜ・どこを変えたか」という変更履歴もblameも残らず、正となるコードが”本番のDB”――コードにとって最も置きたくない場所――に住むことになります。
- 保存時の構文チェックで白画面を未然に防ぐ
- スニペット単位でON/OFF=事故が1機能に閉じる
- GUIで非エンジニアのオーナーも状態を把握できる
- DBごと移行すればまるごと運べる
- Gitによる版管理・差分レビュー・PR・CIが効かない
- 変更履歴とblameが残らない(正がDBになる)
- モジュール境界がなく、増えるほどロード順依存でスパゲッティ化
- コードとデータが密結合し、コードだけの分離デプロイがしにくい
もうひとつは長期のスパゲッティ化です。
スニペットは基本的に”フラットな一覧”として積み上がり、名前空間もモジュール境界も、依存関係を示す構造もありません。
5本のうちは快適でも、20本30本と増えたとき、全体像を一望できず、暗黙のロード順に依存した解きほぐしにくい塊になりがちです。
皮肉なことに、コードを整理するはずの仕組みが、コードの見通しを悪くしていく。
さらに「まるごと持ち運べる」可搬性は裏を返せばコードとデータの密結合であり、コードだけを切り離してレビュー・デプロイする自由を奪います。
結論としては、
Code Snippetsは「エンジニアリング的な保守性(Git・レビュー・CI)」を、「運用上の安全性(隔離・即時ロールバック)」と「オーナーが触れる可搬性」と引き換えにする選択です。
開発者が張り付ける本格プロダクトなら、迷わずmu-plugin+Gitを採ります。
一方、専任の開発者がいない個人運営の箱で、代替が”そもそも安全に手を入れられない”であるなら、この交換は十分に合理的です。
ただし影を放置しないため、実務では各スニペットをファイルにも書き出してGit管理下に置く/本数を規律を持って抑える/成熟した機能はmu-pluginへ”昇格”させるといった緩和策を併用します。要は、便利さに甘えて”DBだけが正”の状態を固定しないことが肝心です。
幸い、この緩和は難しくありません。
Code Snippetsにはスニペット一式をファイル(.code-snippets.json)へ書き出すエクスポート機能が標準で備わっており、これを定期的にGitリポジトリへコミットするだけで「いつ・何が・どう変わったか」の履歴が残ります。DB側の押さえもWP-CLIの1行で済みます。
# スニペット格納テーブル(wp_snippets)だけを定期バックアップする例
wp db export snippets-backup.sql --tables=wp_snippets
主な実装 ― 会員サイトの”一生”を回すための機能群
冒頭の図の通り、会員が「申請 → 承認 → 参加 → 退会」まで一巡できるよう、要所に手を入れました。代表的なものを挙げます。
- 承認制の会員登録:申請直後は「承認待ち」で保留。運営が確認して承認した会員だけが参加できる。
- 投稿権限の制限:bbPressのトークルームへ投稿できるのは、承認済みの会員だけ。未承認の人は閲覧のみ。
- ベストアンサー機能(自作):bbPressの質問トピックに対して回答を”ベスト”として選べる仕組みを追加し、回答者にはバッジが付く。
- プロフィール項目の追加:登録フォームに「都道府県」など独自項目を追加し、公開/非公開も本人が選べる。
- 「お知らせ」機能:運営が管理画面から1件ずつ告知を投稿できるカスタム投稿タイプを用意(段階リリース方針)。
- 退会時のデータ整理:退会すると、そのユーザーのフォーラム投稿まで含めて確実に削除されるようにした。
- 運用ダッシュボード:管理画面に「承認待ち件数」や日次の運用手順を常時表示し、オーナーが迷わないようにした。
承認ワークフローは「4つの状態」で管理する
承認制の要は、会員一人ひとりの”いまの状態”をきちんと持たせることです。この案件では、会員の状態を account_status という項目で次の4つに分けて管理しました。
| 状態 | 意味 | できること |
|---|---|---|
| 承認待ち | 申請直後・運営の確認前 | 閲覧のみ(投稿不可) |
| 承認済み | 資格を確認して参加を許可 | トークルームへ投稿できる |
| 却下 | 参加を見送った | 参加不可 |
| 停止 | 一時的に無効化 | 参加を止める |
状態を1つの項目に集約したことで、「投稿できるのは承認済みだけ」「承認待ちは閲覧のみ」といった制御を、すべて同じ判定で一貫して書けるようになりました。
先ほどのスニペットが 'approved' だけを通していたのは、この設計があるからです。
退会時に「フォーラム投稿まで」消す理由
退会処理は、ユーザーアカウントを消すだけでは不十分です。その人がトークルームに残した投稿(トピック・返信)まで確実に削除するようにしました。
理由は2つあります。
- プライバシー:退会した人の書き込みが残り続けるのは、本人の意図にも会員制の趣旨にも反します。
- データの整合性:投稿だけが「持ち主のいない状態」で残ると、表示崩れや管理上の混乱の原因になります。
「退会したのに書き込みが残っている」というトラブルは、会員サイトで意外に多い落とし穴です。複数サイトの横断感染からの復旧事例でも触れましたが、会員の個人データを預かる以上、”消えるべきものが確実に消える”設計は責任の一部です。
ただし、この”確実に消す”処理は諸刃の剣でもあります。削除の条件を1つ間違えれば、消してはいけないデータまで巻き込む。だからこそ削除系のスニペットは、対象の絞り込みとバックアップ前提の運用がセットになります。
実装の骨格はこうです(実案件のコードそのものではなく、考え方を示す例です)。要点は「ユーザー本体を消す前に、その人の投稿を先に消す」という順序にあります。
// 退会(ユーザー削除)の直前に、本人のトピック・返信を先に完全削除する
add_action( 'delete_user', function ( $user_id ) {
$ids = get_posts( array(
'post_type' => array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ),
'author' => $user_id,
'post_status' => 'any',
'numberposts' => -1,
'fields' => 'ids',
) );
foreach ( $ids as $id ) {
wp_delete_post( $id, true ); // ゴミ箱を経由せず完全削除
}
} );
delete_user はWordPress本体がユーザー削除の直前に必ず発火させる標準フックです。
ここに載せておけば、管理画面から消しても、プログラム経由で消しても、同じ後始末が必ず走ります。逆順(先にユーザーを消す)だと、投稿の作成者情報が失われた後から対象を探すことになり、取りこぼしの温床になります。
もちろん削除系の処理は失敗が許されないので、実行前バックアップと、対象件数を先に数えて確認する”ドライラン”をセットにして運用します。
つまずきと解決 ― WordPress会員サイトの不具合「承認済みなのにログインできない」の真因
公開準備の段階で、「承認したはずの会員が、なぜかログインできない」という不具合に直面しました。これは前半で述べた「組み合わせの影」――プラグイン同士の競合――が、具体的な症状として現れた場面でもあります。
最初は、コードを読んだ印象から「登録処理のフックが、ユーザー名を書き換えてしまっているのでは」という仮説が立ちました。
もっともらしい説明です。
しかし――ソースコードから導いた仮説を、そのまま”原因”だと断定しなかったのが分かれ道でした。
実際に登録済みの会員データを一覧化して照合したところ、ユーザー名が書き換わっている会員は1人もいませんでした。
仮説は誤り。
真因を実データから追い直した結果、原因は別のセキュリティプラグインの認証設定にありました。
コードの見立てとはまったく別の場所です。
個々のプラグインは正しく動いていたのに、認証まわりの前提が重なった結果、ログインという一番大事な導線だけが通らなくなっていた――依存を増やすほど、こうした”誰のバグでもない競合”の可能性が上がっていきます。
「ソースを読んで『たぶんこれが原因』」で止めず、必ず実機のデータで裏を取る。推測で断定して二次被害を出さない――これは復旧・保守の現場でも同じ鉄則です。
「もっともらしい推測」で修正に走ると、直らないどころか別の不具合を生みます。
実機のデータ・ログ・ファイルで確証を取ってから手を動かす。地味ですが、これが結局いちばんの近道でした。
そして見落とせないのは、この切り分けには相応の技術的な”勘所”が要るという事実です。
開発者ですら最初の仮説を外したこの種の問題を、次章の「オーナーの自走」がどこまでカバーできるのか――伏線として置いておきます。
運用の理想と現実 ― 「ひとりで回せる」を正直に検証する
制作のゴールは「納品」ではなく、オーナーが自分で運用を続けられることです。
そのために、
- 管理画面に承認待ちの件数バッジと日次の運用手順を常時表示
- 会員が使わない標準機能(「投稿」メニュー等)は非表示にして混乱を防止
- 利用規約・プライバシーポリシーは管理画面から編集できる形に
- DBコンテンツの手動更新が必要な作業には、手順書(ランブック)を用意
環境の移行(検証環境から本番へ)も、All-in-One WP Migration でDBごと安全に運べるようにしました。
移行やステージング運用の考え方は、壊さないWordPress ― Git / ステージング / マイグレーション戦略でも詳しく整理しています。
「開発者しか触れない箱」ではなく、現場の運営者が主導権を持てる箱に仕上げること――ここまでは、確かに実現できました。
「運用」と「保守」は別物である
ここが本記事でいちばん正直に書きたいところです。
ダッシュボードとランブックが面倒を見てくれるのは、あらかじめ想定できる”定常運用”の道――会員を承認する、お知らせを出す、規約を直す――に限られます。
非エンジニアである歯科衛生士のオーナーが、この範囲を自分で回せるようになったのは事実で、そこは自信を持って設計した部分です。
問題は“想定できない道”のほうです。
プラグイン更新による突然の白画面、セキュリティインシデント、PHPのメジャー更新でスニペットが動かなくなる、スパムの大量流入、bbPressの表示崩れ――こうした事態の診断と復旧は、ランブックに書き下せない種類の技術判断を要求します。
先ほどのログイン不能トラブルが何よりの証拠で、開発者の最初の仮説すら外した問題を、非エンジニアが独力で切り分けるのは現実的ではありません。
- 会員の承認・却下
- お知らせ投稿・規約の編集
- 日次の運用チェック
- 更新による白画面・プラグイン競合
- セキュリティインシデント対応
- PHP/WPメジャー更新への追従
つまり「運用(オペレーション)」と「保守(メンテナンス)」は別のスキルです。
前者は仕組みで自走化できても、後者の依存はゼロにできない――むしろインシデント時には技術的関与への依存が一時的に100%になります。
しかもWordPressは”生きている”プラットフォームで、本体・プラグイン・PHPの更新は、こちらの都合とは無関係に降ってきます。
技術的な持ち主が不在の会員サイトは、作りが悪いからではなく、土台が動き続けるがゆえに、時間とともに静かに劣化していく。これは避けられない現実です。
「もう私たちに一切頼らなくても大丈夫」な状態は、正直に言えば作れません。日々の運用はオーナーさんが主役でも、WordPressが動き続ける以上、更新やインシデントのときだけは技術の手が要ります。そこを曖昧にせず、最初に『運用は自走・保守は伴走』と線引きするのが、いちばん誠実だと考えています。
だからこの案件で本当に問うべきは、「オーナーが日々の操作をこなせるか(=Yes)」ではなく、「WordPress 8 やPHPの次のメジャーが来たとき、あるいはプラグインが開発停止したとき、誰がこのサイトの技術的な持ち主になるのか」です。
答えは”仕組み”ではなく”関係”の設計――軽い保守リテイナー、マネージド寄りのホスティング、あるいは後任開発者への引き継ぎ計画――で用意すべきものです。
私が納品するのは「二度と手のかからないサイト」ではなく、「日々の運用はオーナーの手に渡り、更新とインシデントのときだけ、決められた軽い形で技術が伴走するサイト」。
この線引きを最初に共有することこそ、本当の意味での運用持続性だと考えています。
成果と、次の会員サイト構築にも効く3つの設計原則
この案件で得られたのは、「会員数」のような一時的な数字ではなく、運営が属人化せず、壊れても戻せる”土台”です。
オーナーは、承認・お知らせ投稿・日次運用を、開発者に頼らず自分で回せる状態になりました。
そして、この案件から抜き出せる他の会員制サイトにも効く設計原則は次の3つです。
- 心臓部は”切り離せる”場所に置く:業務ロジックはテーマ本体ではなく、個別にON/OFFできる場所(Code Snippets等)へ。事故を1機能に閉じ込める。
- 会員の”状態”を1つに集約する:承認・却下・停止などを1つの項目で管理すれば、権限制御が一貫し、ミスが減る。
- “消えるべきもの”を確実に消す:退会時のデータ整理まで設計に含める。プライバシーと整合性は後付けできない。
ただし正直に付け加えれば、どの原則にも”影”があります。
①の「切り離せる場所」はGit管理を手放しやすく、
②の「状態の集約」はその1項目が壊れたときの影響が大きく、
③の「確実に消す」処理はバグると”消してはいけないもの”まで消しかねない。
原則は万能薬ではなく、それぞれの副作用を承知したうえで運用ルール(バックアップ・レビュー・緩和策)とセットにして初めて機能します。
設計原則を掲げることと、そのトレードオフを直視することは、両立させるべきものだと考えています。
よくある質問(FAQ)― WordPress会員サイトの構築・保守
まとめ

WordPressでの会員サイト構築は、「機能を並べる」作業ではなく、トレードオフを引き受ける設計判断の連続でした。
既製プラグインの組み合わせは車輪の再発明を避ける一方で依存と競合を抱え込み、Code Snippetsへの集約は白画面を防ぐ一方でGit管理という保守性と引き換えになり、「オーナーが自走できる箱」は定常運用を任せられても保守・インシデント対応まで手放せるわけではない。
大切なのは、これらの”影”を隠して成功事例に仕立てることではなく、影を承知したうえで、この案件の制約(非エンジニアの個人運営・標準的な要件・限られた予算)に対して最も筋の良い交換を選び、緩和策と線引きをセットで用意することです。
実データに基づく原因特定・切り離せる設計・そして”運用は自走・保守は伴走”という正直な線引き――それがWEB先案内の基本姿勢です。
プラグインを壊さず使いこなす考え方は、別のプラグイン活用事例でも同じ軸で解説しています。
もし今、「WordPressで会員制サイトを作りたい」「作ったはいいが、運用や不具合で行き詰まっている」なら――準備は何も要りません。
整った要件書も、きれいにまとめた資料も不要です。
今のサイトのURLと、困っていること(または実現したいこと)を一言添えて送っていただくだけで、こちらで現状を確認し、壊さず進める道筋と対応範囲をご提案します。
相談した結果「今は作らない」でもまったく問題ありません。
まずは無料でご相談ください
トレードオフを正直にお話ししながら、現場目線でお手伝いします。






