ZashRSS — WordPress のフィード管理を「レジストリ」にするゼロレイテンシ RSS プラグイン
問題:フィード30本、コントロールゼロ
運営中のニュースサイトでは、約30の外部ソースからヘッドラインを集約している。WordPress には fetch_feed() と一握りの RSS プラグインがあるが、どれも同じ病気を抱えている。ページ読み込みのたびに外部フィードを叩きに行くのだ。
フィード2本の個人ブログなら問題ない。フィード30本で実トラフィックのある本番サイトでは、こうなる。
- キャッシュが冷えていると初回描画が3〜5秒遅延する
- 上流の RSS サーバが遅いとランダムにタイムアウトする
- どのフィードが実際に読者のクリックを生んでいるか、まったく見えない
必要なのは別物——レジストリだ。
アーキテクチャ:キャッシュ優先、フロントエンドは外部を叩かない
ZashRSS は「取得」と「表示」を完全に分離する。
┌──────────────────────────────────────────────┐
│ WP-Cron(毎時) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Feed A │ │ Feed B │ │ Feed C │ │
│ │ fetch → │ │ fetch → │ │ fetch → │ │
│ │ cache │ │ cache │ │ cache │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ ↓ ↓ ↓ │
│ wp_options: ntmr_feed_cache │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ フロントエンド(ショートコード描画) │
│ [nt_feed_list group="news" limit="6"] │
│ ↓ │
│ wp_options のキャッシュを読むだけ │
│ 外部 HTTP コールはゼロ │
│ HTML を描画して終わり │
└──────────────────────────────────────────────┘
フロントエンドは外部の RSS サーバと一切通信しない。wp_options にキャッシュ済みのアイテムを読んで、純粋な HTML を描画するだけ。ページ表示への影響は実質ゼロだ。
フィードグループ:フォルダではなくプレイリスト
フィードは巨大な一枚リストに放り込まない。グループで整理する——ファイルフォルダではなく、Spotify のプレイリストのイメージだ。
| グループ | 用途 | ショートコード |
|---|---|---|
news | トップヘッドラインのサイドバー | [nt_feed_list group="news" limit="6"] |
topics | 深掘りセクション | [nt_feed_list group="topics" limit="10"] |
economy | ビジネス/マーケットのウィジェット | [nt_feed_list group="economy" limit="4"] |
all | 横断アグリゲート | [nt_feed_list group="all" limit="8"] |
1本のフィードは複数グループに所属できる。NHK を news と all の両方に入れるのはチェックボックス1つで済み、重複登録の頭痛はない。
ショートコード:1行でフルコントロール
ショートコードは Cocoon の HTML ウィジェットに1行貼れば終わるように設計した。
[nt_feed_list group="news" limit="6" size="sm" show_border="0"]
属性リファレンス
| 属性 | デフォルト | 働き |
|---|---|---|
group | "all" | 表示するフィードグループ |
limit | 6 | 表示件数 |
size | "md" | フォントプリセット: sm / md / lg |
font_size | — | CSS 直接指定: "14px", "0.95rem" |
show_feed | 1 | ソース名の表示 |
show_date | 1 | 公開日の表示 |
show_border | 1 | 区切り線の表示(0 で消す) |
class | — | カスタムスタイル用の追加 CSS クラス |
CSS 変数(テーマ側での上書き)
.ntmr-feed-list {
--ntmr-font-size: 0.93rem;
--ntmr-line: #e4e7eb; /* 区切り線の色 */
--ntmr-ink: #203040; /* 本文色 */
--ntmr-muted: #6f7d8c; /* メタ情報の色 */
}
Cocoon 子テーマ側でこれを上書きすれば、サイトのパレットに合わせられる。
クリック計測:読者は実際どこへ行くのか
外部への全クリックはリダイレクトハンドラを経由し、以下を記録する。
- タイムスタンプ
- フィード ID とラベル
- グループスラッグ
- 記事タイトルと URL
- 遷移先ホスト名
- IP ハッシュ(匿名化済み)
これで、運営者なら誰でも抱く問い——「どのフィードが実際にトラフィックを送っているのか」——に答えられる。管理ダッシュボードにはフィード別・グループ別の直近クリック集計テーブルを表示する。外部アナリティクスへの依存はなく、すべて WordPress のカスタムテーブル(wp_ntmr_click_log)に収まる。
管理画面:v0.1 から v0.2 への教訓
v0.1 の管理ページには問題があった。全フィードの編集フォームが常時展開されていて、30本もあると永遠にスクロールする羽目になる。
v0.2 での修正:
| Before | After |
|---|---|
| 編集フォームが常時全展開 | デフォルト折りたたみ、「編集」ボタンでトグル |
| 狭い画面でフォームがはみ出す | グリッドレイアウトで幅を制約 |
| 取得上限のデフォルト8件 | デフォルト1件(保守的に) |
| 重複登録の防止なし | 同一 URL のフィードを警告付きでブロック |
| ショートコードは手組み | コピーボタン付きの対話型ジェネレータ |
| 区切り線のトグルなし | show_border="0" 属性を追加 |
一番の収穫はショートコードジェネレータだ。グループを選び、オプションを設定し、生成されたショートコードをコピーする。ドキュメントを引く必要がない。
重複防止
登録済みのフィード URL を再登録しようとすると、こう表示される。
⚠️ その feed URL はすでに「NHK NEWS WEB」として登録済みです。
URL 比較は大文字小文字を無視し、末尾スラッシュを正規化して行う。単純だが、頭痛の種を確実に減らす。
デプロイ構成
wp-content/
├── mu-plugins/
│ └── nt-rss-registry.php ← ローダーのみ(15行)
└── plugins/
└── nt-rss-registry/
├── nt-rss-registry.php ← プラグイン本体(約1,050行)
└── README.md
mu-plugin のローダーが、テーマ初期化より前にプラグインを読み込むことを保証する。これはショートコード登録のタイミングに効く。
なぜ普通にプラグインを有効化するのではなく mu-plugin+ローダーなのか?
Cocoon の HTML ウィジェットはショートコードを早いタイミングで描画するからだ。init priority 1 までにプラグインがロードされていないと、ウィジェット内のショートコードは描画済み HTML ではなく生の [nt_feed_list ...] テキストとして表示されてしまう。mu-plugin ならロード順を保証できる。
今後
- フィード別 favicon 表示 — ヘッドラインの横にソースサイトの favicon を出す
- 更新停止検出 — 48時間以上更新のないフィードをアラート
- フィード別クリック率 — 表示回数とクリックを突き合わせて高成績ソースを特定
- OPML インポート/エクスポート — フィードの一括移行
数字で見る ZashRSS
| 指標 | 値 |
|---|---|
| PHP 総行数 | 約1,050 |
| 外部依存 | 0 |
| 描画時のフロントエンド HTTP コール | 0 |
| cron 更新間隔 | 1時間 |
| フィードあたり最大アイテム数 | 30 |
| カスタム DB テーブル | 1(クリックログ) |
| 本番稼働中のフィード数 | 約30 |
ZashRSS は特定の編集ワークフローのために作った内部ツールで、WordPress の公式プラグインディレクトリには公開していない。ただし、キャッシュ優先 RSS・グループ化ショートコード・クリック計測というアーキテクチャパターンは、どんなフィード集約の用途にも再利用できる。
追記(2026-07-19): 立ち上げ期に英語で書いていた記事を、内容そのままに日本語へ改稿した。