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.
| Group | Purpose | Shortcode |
|---|---|---|
news | Top headlines sidebar | [nt_feed_list group="news" limit="6"] |
topics | Deep-dive section | [nt_feed_list group="topics" limit="10"] |
economy | Business/markets widget | [nt_feed_list group="economy" limit="4"] |
all | Cross-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
| Attribute | Default | What it does |
|---|---|---|
group | "all" | Which feed group to display |
limit | 6 | Number of items |
size | "md" | Font preset: sm / md / lg |
font_size | — | Direct CSS override: "14px", "0.95rem" |
show_feed | 1 | Show source name |
show_date | 1 | Show publish date |
show_border | 1 | Show item separators (0 = no borders) |
class | — | Extra 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:
| Before | After |
|---|---|
| All edit forms always visible | Collapsed by default, “Edit” button toggles |
| Feed form overflowed on narrow screens | Constrained grid layout |
| Default fetch limit: 8 items | Default: 1 item (conservative) |
| No duplicate prevention | Same-URL feeds blocked with warning |
| Manual shortcode assembly | Interactive shortcode generator with copy button |
| No border toggle | show_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
| Metric | Value |
|---|---|
| Total PHP lines | ~1,050 |
| External dependencies | 0 |
| Frontend HTTP calls on render | 0 |
| Cron refresh interval | 1 hour |
| Max items per feed | 30 |
| Custom DB tables | 1 (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