Radio Nations is a live radio directory. Visitors pick a country, pick a station and listen in the browser. It runs on WordPress, and we built and maintain it.
The number that surprises people is the plugin count. A site this size normally carries an SEO plugin, a multilingual plugin, a caching plugin, a security plugin, a reviews plugin and a REST API plugin. This one runs one, and it only sends transactional email.
| Measured on the live site | |
|---|---|
| Published station pages | 981 |
| Countries | 22 |
| Pages indexed by Google | 861, up from 106 on 1 June |
| Active plugins | 1 |
| Homepage weight | 0.30 MB across 15 requests |
| Time to first byte | 0.035 to 0.045 s |
| Total Blocking Time | 0 ms |
| Cumulative Layout Shift | 0 |
What we built
A custom theme, liveradio-theme, currently at version 1.12.26. Roughly 19,000 lines: 11,909 across 23 PHP modules, 2,997 in templates, 2,610 lines of CSS and 1,647 of JavaScript.
The modules are the product rather than a skin over other people’s plugins.
| Module | What it replaces |
|---|---|
seo.php, schema.php, sitemap.php, hreflang.php, redirects.php, ai-discovery.php | an SEO plugin |
i18n.php, genre-i18n.php, flags.php | a multilingual plugin |
performance.php | a caching plugin |
security.php | a hardening plugin |
reviews.php | a reviews plugin |
app-api.php | a REST API plugin |
post-types.php, taxonomies.php, meta.php | a custom-fields plugin |

Every one of those is code we own, that loads only what the page needs, and that cannot break when somebody else ships an update.
The front end carries a persistent audio player, a live listener count, favourites tied to real user accounts, star reviews, station search, a country switcher and a consent banner that actually gates the analytics.
The multilingual engine
Station pages are written in the language of the country they belong to. A Polish station reads in Polish, a German one in German.
Behind that: an 86-entry locale registry, six compiled UI translations (en_US, en_GB, de_DE, es_ES, fr_FR, pt_BR), 23 hreflang tags on the homepage and every country archive, and an <html lang> that changes per country. Station pages carry no hreflang, which is correct rather than missing: a station belongs to one country, so it has no alternates to point at.
Structured data
A station page emits RadioStation, WebSite, WebPage, FAQPage, BreadcrumbList, ListenAction, SpeakableSpecification, SearchAction and ImageObject as JSON-LD. The FAQ blocks are real questions with real answers, not padding.
Speed
Time to first byte measured from Dhaka is 0.035 to 0.045 seconds. That is edge delivery, not a fast origin.
The theme sends cache-control: public, max-age=300, s-maxage=14400, stale-while-revalidate=86400 plus its own x-lr-edge: eligible marker, and a Cloudflare cache rule reads them. HTML is cached at the edge for anonymous visitors, revalidated in the background, and bypassed for anyone with a WordPress cookie. Cache purges are automatic when content changes.
The rest is weight the page never picks up:
- The homepage transfers 0.30 MB across 15 requests.
theme.min.cssis 51.7 KB,app.min.jsis 30.9 KB.gtag.jsis not downloaded until the first user interaction, or five seconds, whichever comes first. The inlinegtag('config', β¦)still runs immediately, and because gtag calls only queue intodataLayer, the library replays the queue when it arrives. Nothing is lost except bounces shorter than five seconds.
The result is Total Blocking Time of 0 ms and Cumulative Layout Shift of 0. There is no main-thread work to block on and nothing moves after paint.
SEO, including the part that has not worked yet
The technical side is done and verifiable: canonical tags, hreflang clusters, a multilingual sitemap index, JSON-LD on every template, breadcrumbs, redirect handling for legacy country codes, and a discovery layer for AI crawlers.
Indexed pages went from 106 on 1 June to 861 on 21 August. Google crawls the site mostly as Smartphone Googlebot, and 68% of its requests are HTML.

Impressions followed. Comparing the first fourteen days of the measured period with the last fourteen: 1,254 to 2,171, about 73% more.
Clicks are still small: 66 across 87 days. Average position also moved the wrong way, from 49.5 to 66.3.
That second number is worth explaining rather than hiding. When a site adds roughly 750 pages in three months, most of them new and unlinked from anywhere, the average position across all queries falls even when nothing has got worse. The site is ranking for far more things, most of them deep. Impressions rising while position falls is the signature of that, not of a penalty.
What it means practically: indexing and crawlability are solved, and earning clicks is the open problem. That is a content and link-building job, not a technical one.
Security
Verified live on the day this was written.
Working:
xmlrpc.phpreturns 403/wp-json/wp/v2/usersreturns 401?author=1enumeration redirects to the homepage- HSTS for one year,
includeSubDomains,preload - Content-Security-Policy on
frame-ancestors,base-uri,object-src,form-action X-Frame-Options: SAMEORIGIN,X-Content-Type-Options: nosniff- Visitor IP is only trusted from a real Cloudflare peer, so the login rate limit cannot be bypassed with a spoofed header
- Prepared statements, nonces on every AJAX endpoint, honeypots on forms
Still open, and tracked:
wp-login.phpis directly reachable. A custom/rn-login/route exists but the block is inactive until the nginx rewrite and an option flag are set.readme.htmlreturns 200, which leaks the WordPress version./register/loads publicly.- The origin IP answers on 443 directly, so the Cloudflare WAF can be bypassed by anyone who knows the address. The fix is a firewall rule limiting 80 and 443 to Cloudflare ranges.
None of those are code defects. They are server and settings work, they are on the list, and publishing them here is more useful than claiming a clean sheet.
What monthly maintenance actually looks like
WordPress 7.1 on PHP 8.3. The database is backed up every night at 03:15 and the most recent backup is from this morning.
The theme has shipped around fifty releases between May and August 2026, from version 1.6.2 to 1.12.26. Small changes, shipped often, each one versioned so it can be rolled back on its own.
Still open
Beyond the four security items above:
- Clicks. The technical foundation is in place; the traffic is not.
- Two WordPress.org plugins, extracting the player and the station directory from the theme, are at idea stage and have not been built.
- Cloudflare Tiered Caching is available on the current plan and is not switched on yet, which would raise the global cache hit ratio.
Scope: custom theme build plus ongoing monthly maintenance. Stack: WordPress, PHP 8.3, custom theme, nginx, CloudPanel, Cloudflare.