DEV Community

Mohd Hakim Johari
Mohd Hakim Johari

Posted on

I Built a CMS That Runs a Live News Portal on Shared Hosting. LCP: 0.4s.

I Built a CMS That Runs a Live News Portal on Shared Hosting. LCP: 0.4s.

Not a demo. Not a side project collecting dust.

A real news portal. Real traffic. Real deadlines.

And it runs on a regular shared hosting server — the kind that costs a few dollars a month.


Why I Built It

I wanted a CMS that behaved the way I think about performance: lean by default, not lean after you've installed five optimization plugins and tuned a CDN.

Most CMS options in the market fall into two camps:

The heavy camp — battle-tested, massive ecosystems, but you're fighting against the architecture to get clean performance numbers. Every page load carries the weight of decades of backward compatibility.

The headless camp — clean and fast, but now you're managing a Node.js server, a separate frontend, API keys, deployment pipelines. The overhead shifts from runtime to infrastructure.

I wanted a third option: fast by architecture, simple by design, deployable like it's 2010.

That's VonCMS.


The Architecture

React 19 frontend. PHP 8.2 backend. MySQL. No Node in production.

The entire page — content, SEO metadata, ad config, related posts — resolves in a single database query. React handles navigation and rendering as a SPA. PHP handles the data layer, API, sitemaps, schema, and server logic.

The result:

Metric VonCMS
DB Queries per page 1
RAM usage ~15–25MB
LCP (shared hosting) < 0.4s
Plugin dependencies 0

That last line matters. Everything ships built-in:

  • Full SEO stack — meta, schema, XML sitemaps, llms.txt for AI crawlers
  • Ads manager — header, sidebar, in-feed, AdSense-ready
  • Comment system with role-based moderation
  • Newsletter
  • Redirect manager
  • WebP auto-conversion on upload
  • OTA updater with SHA256 integrity validation
  • Image SEO engine — <image:image> sitemap injection, schema image binding
  • SPA ad persistence — solves the AdSense freeze problem on React navigation
  • Path agnosticism — root, subfolder, or subdomain, zero manual config

No plugin hunting. No dependency chain. Just install and publish.


Security as Architecture, Not Afterthought

Every request passes through a 6-layer stack:

  1. SQLi — 100% PDO parameterized queries
  2. XSS — DOMPurify + server-side context-aware stripping
  3. CSRF — mandatory token validation on all state-changing requests
  4. Session binding — cryptographic UA-binding against session hijacking
  5. Rate limiting — IP-based flood protection on high-risk endpoints
  6. Data masking — automatic credential redaction in API responses

Plus: OTA updates enforce SSL and SHA256 package validation before extraction. The installer locks permanently after setup — no UI-level re-installation exploit possible.


It's Proven in Production

VonCMS powers Skrip Global — a live Malay-language news portal.

Scheduled posts. Breaking news workflow. Ad networks. Reader comments. Real traffic spikes.

That's the benchmark I build against. Not synthetic tests.


Who It's For

Developers who want full ownership of their publishing stack. Agencies tired of plugin debt. Publishers who want performance without infrastructure complexity.

It runs on any shared host with PHP 8.0+ and MySQL. Setup is three steps: upload zip, run /install, log into /admin. No Composer. No npm. No Docker.


Where It's Heading

VonCMS goes open source at 1,000 active installations. Not before — so there's a real community ready to sustain it when the repo opens.

Version 2.0 brings a plugin marketplace and full community governance.

Currently at v1.21.0 "Breeze" — performance refinement, UI polish, PHP 8.5 preparation.


GitHub: github.com/Vondereich/VonCMS

If you try it, drop a comment. I read everything.

Top comments (2)

Collapse
 
apex_stack profile image
Apex Stack

The single-query-per-page architecture is the kind of constraint that forces you into genuinely good design decisions. I run a financial data site on Astro + Cloudflare CDN with static generation, and even with that stack, I've seen how easy it is to accidentally add query overhead that kills performance at scale.

The llms.txt inclusion caught my eye — that's forward-thinking. I've been watching how AI crawlers interact with my own pages and the indexing behavior is wildly different from Googlebot. Most CMS platforms haven't even started thinking about this.

Curious about the SPA ad persistence solution — AdSense freezing on client-side navigation is a real problem that trips up a lot of React-based content sites. Is VonCMS re-initializing the ad slots on route change, or did you find a way to keep the existing slots alive across navigations?

Collapse
 
vondereich profile image
Mohd Hakim Johari

The single-query approach is essentially bundled view state — one API call satisfies the entire layout for Home, Post, or Page. No micro-fetching individual components, so latency stays flat and DB overhead is predictable regardless of page complexity.

The llms.txt is dynamic under the hood (llms.php + .htaccess rewrite) — it generates a concise Markdown summary of the site's categories, latest posts, and structure on the fly. Essentially a sitemap for LLMs rather than crawlers. Given how differently Perplexity and ChatGPT Browse behave versus Googlebot, it felt worth treating them as a first-class audience.

For the SPA ad issue — it's iframe isolation keyed to location.pathname. The AdBlock component detects executable scripts and spawns a sandboxed iframe per slot instead of letting AdSense touch the main DOM. On every route change, the key changes, the iframe remounts, and AdSense sees a clean page load. It bypasses the SPA routing problem entirely rather than trying to fight the SDK's internal state.