ZashRSS: A Zero-Latency Feed Registry for WordPress

ZashRSS: A Zero-Latency Feed Registry for WordPress

The Problem: 30 Feeds, Zero Control

We run a news commentary site that aggregates headlines from ~30 external sources. WordPress has fetch_feed() and a handful of RSS plugins, but they all share the same disease: they hit external feeds on every page load.

That’s fine for a personal blog with 2 feeds. For a production site with 30 feeds and real traffic, it’s a recipe for:

  • 3-5 second first-paint delays when feeds are cold
  • Random timeouts when upstream RSS servers are slow
  • No visibility into which feeds actually drive reader clicks

We needed something different. We needed a registry.

Architecture: Cache First, Fetch Never (on Frontend)

ZashRSS separates “fetching” from “displaying” completely.

┌──────────────────────────────────────────────┐
│  WP-Cron (hourly)                            │
│  ┌─────────┐   ┌─────────┐   ┌─────────┐   │
│  │ Feed A  │   │ Feed B  │   │ Feed C  │   │
│  │ fetch → │   │ fetch → │   │ fetch → │   │
│  │ cache   │   │ cache   │   │ cache   │   │
│  └─────────┘   └─────────┘   └─────────┘   │
│         ↓             ↓             ↓        │
│     wp_options: ntmr_feed_cache              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│  Frontend (shortcode render)                 │
│  [nt_feed_list group="news" limit="6"]       │
│         ↓                                    │
│  Read from wp_options (cached)               │
│  Zero external HTTP calls                    │
│  Render HTML → done                          │
└──────────────────────────────────────────────┘

The frontend never talks to an external RSS server. It reads pre-cached items from wp_options and renders pure HTML. Page load impact: effectively zero.

Feed Groups: Not Folders, Think Playlists

Feeds aren’t dumped into one giant list. They’re organized into groups — think Spotify playlists, not file folders.

GroupPurposeShortcode
newsTop headlines sidebar[nt_feed_list group="news" limit="6"]
topicsDeep-dive section[nt_feed_list group="topics" limit="10"]
economyBusiness/markets widget[nt_feed_list group="economy" limit="4"]
allCross-feed aggregate[nt_feed_list group="all" limit="8"]

A single feed can belong to multiple groups. Adding NHK to both news and all is one checkbox, not a duplication headache.

Shortcode: One Line, Full Control

The shortcode is designed for Cocoon HTML widgets — paste one line and you’re done.

[nt_feed_list group="news" limit="6" size="sm" show_border="0"]

Attribute Reference

AttributeDefaultWhat it does
group"all"Which feed group to display
limit6Number of items
size"md"Font preset: sm / md / lg
font_sizeDirect CSS override: "14px", "0.95rem"
show_feed1Show source name
show_date1Show publish date
show_border1Show item separators (0 = no borders)
classExtra CSS class for custom styling

CSS Variables (Theme Override)

.ntmr-feed-list {
  --ntmr-font-size: 0.93rem;
  --ntmr-line: #e4e7eb;     /* border color */
  --ntmr-ink: #203040;      /* text color */
  --ntmr-muted: #6f7d8c;    /* meta text color */
}

Override these in the Cocoon child theme to match your site’s palette.

Click Tracking: Where Do Readers Actually Go?

Every outbound click goes through a redirect handler that logs:

  • Timestamp
  • Feed ID and label
  • Group slug
  • Article title and URL
  • Target hostname
  • IP hash (anonymized)

This answers the question every publisher asks: “Which feeds are actually sending traffic?”

The admin dashboard shows a summary table of recent clicks per feed per group. No external analytics dependency — it’s all in a WordPress custom table (wp_ntmr_click_log).

Admin UI: Lessons from v0.1 to v0.2

The v0.1 admin page had a problem: every feed’s edit form was visible all the time. With 30 feeds, you’d scroll forever.

v0.2 fixes:

BeforeAfter
All edit forms always visibleCollapsed by default, “Edit” button toggles
Feed form overflowed on narrow screensConstrained grid layout
Default fetch limit: 8 itemsDefault: 1 item (conservative)
No duplicate preventionSame-URL feeds blocked with warning
Manual shortcode assemblyInteractive shortcode generator with copy button
No border toggleshow_border="0" attribute

The shortcode generator is the nicest addition — select a group, set options, copy the generated shortcode. No documentation lookup needed.

Duplicate Prevention

Registering a feed URL that already exists now shows:

:warning: その feed URL はすでに「NHK NEWS WEB」として登録済みです。

URL comparison is case-insensitive and trailing-slash-normalized. Simple, but saves headaches.

Deployment Architecture

wp-content/
├── mu-plugins/
│   └── nt-rss-registry.php     ← Loader only (15 lines)
└── plugins/
    └── nt-rss-registry/
        ├── nt-rss-registry.php  ← Plugin body (~1,050 lines)
        └── README.md

The mu-plugin loader ensures the plugin loads before theme initialization, which matters for shortcode registration timing. The actual code lives in plugins/ for standard WordPress update workflows.

Why mu-plugin + loader instead of just activating the plugin?

Because Cocoon’s HTML widgets render shortcodes early. If the plugin isn’t loaded by init priority 1, shortcodes in widgets produce raw [nt_feed_list ...] text instead of rendered HTML. The mu-plugin guarantees load order.

What’s Next

  • Per-feed favicon display — show the source site’s favicon next to headlines
  • Stale feed detection — alert when a feed hasn’t updated in 48+ hours
  • Click-through rate per feed — impressions vs. clicks to identify high-performing sources
  • OPML import/export — bulk feed migration

Numbers

MetricValue
Total PHP lines~1,050
External dependencies0
Frontend HTTP calls on render0
Cron refresh interval1 hour
Max items per feed30
Custom DB tables1 (click log)
Active feeds in production~30

ZashRSS is an internal tool built for a specific editorial workflow. It’s not published on the WordPress plugin directory, but the architecture patterns — cache-first RSS, grouped shortcodes, click tracking — are reusable for any feed aggregation use case.

— Zash Studio Engineering / 2026-04-10