/n Nano CMS GitHub →

Open source · MIT · v1.2.0

The blog system
your static site has been waiting for.

Nano CMS bolts a fast, SEO-driven blog onto any existing HTML/CSS client site. No database. No plugins. No build step. The admin uploads via SFTP when you publish, then leaves. What's on disk is the database.

~4,500 lines of hand-written PHP · deploys in 350 KB · PHP 8.1+

4,500
lines of code total
350 KB
deployed footprint
0
database engines
0
JS frameworks

What it is

Markdown files on disk are the database.

Nano CMS solves a specific problem: client websites that should stay as fast static HTML, but need a steady stream of fresh blog content for search ranking. Rather than converting the whole site to WordPress or Joomla, Nano CMS slots a small blog system into an existing static site with minimal disruption.

It is deliberately not a general-purpose CMS. It does one thing and tries to do it well: serve a blog with strong SEO output in as little code as possible. If you've ever installed WordPress just to publish four blog posts a year on a client site, this is for you.

How it works

Two codebases. One on-disk format.

01

Frontend (permanent)

Lives inside the client's webroot at /blog/. Renders pages, generates the sitemap and RSS, outputs full SEO metadata on every URL. Once it's there it never needs touching.

02

Admin (portable)

A universal folder, identical for every site. Upload it via SFTP when you want to publish. Edit posts. Remove the folder when done. No persistent admin = drastically reduced attack surface on client sites.

03

Config (outside webroot)

Password hash and site settings live in a JSON file outside the document root, structurally unreachable via HTTP. Backup is rsync /posts/ /media/ /config.json. That's the entire CMS state.

/public_html/blog/           <-- permanent
 ├── posts/     Markdown files (the "database")
 ├── media/     Uploaded images
 ├── core.php    Parser, renderer, ~600 lines
 ├── index.php   Category landing + archives
 ├── post.php    Single post
 └── template.php  Per-site HTML wrapper

/blog-config/              <-- OUTSIDE webroot
 ├── config.json   Password hash, site settings
 └── rate-limit.json Login attempt tracking

/public_html/blog/admin/         <-- ephemeral
 Uploaded via SFTP to publish, removed after.

Features

Everything a small blog needs. Nothing it doesn't.

Flat-file by design

Markdown posts with YAML frontmatter. No SQLite, no MySQL. Backups are an rsync.

🔗

Clean URLs out of the box

/blog/<category>/<slug>/ via .htaccess. No ?p=42 nonsense, ever.

📝

Markdown editor

Toolbar, autocomplete on categories, draft preview behind admin auth. Shortcodes for YouTube and Vimeo.

🎨

Category landing

Homepage shows a card grid of categories sorted by post count. Each can have its own hero image. Click in, read articles.

📷

Auto thumbnails

Every upload generates a pre-cropped thumbnail. Card grids stay light; single-post heroes keep the original.

🚀

Per-grid layout control

Independent 3- or 4-column settings for the homepage and the article archive. Tune separately from admin.

🔒

HTTPS-only admin

HTTPS enforced. CSRF on every POST. bcrypt password hash. Rate-limited login. Sessions invalidated on password change.

📷

Image safety pipeline

Uploads are decoded and re-encoded through GD or Imagick. Strips embedded payloads (EXIF smuggling, the works).

📂

Removable admin

Sole-developer-friendly. Upload, publish, remove. Nothing for an attacker to find when you're not actively editing.

SEO output

Technical SEO most WordPress sites need three plugins for.

Install

Five minutes, one setup wizard.

  1. Upload the frontend zip to /public_html/blog/ on your client's host.
  2. Edit bootstrap.php with the path to your config directory (sample provided).
  3. Upload the admin zip to /public_html/blog/admin/.
  4. Visit https://yoursite.com/blog/admin/setup.php. Set a password. The wizard writes config.json and goes away.
  5. Publish. Drop the admin folder when done. Your blog stays live.

Backups are a single line of cron: rsync -az clientsite:/blog/posts/ /backups/clientname/. The whole CMS state is files.

Comparison

Why not WordPress, Joomla, or Grav?

Those are all excellent for sites that need them. For a static HTML client site that needs occasional SEO content, they aren't.

WordPress

~500,000 lines

  • Requires a database
  • Ongoing security updates
  • Plugin maintenance
  • Significant attack surface
  • Fundamentally converts the site to a database app

Joomla

Hundreds of thousands of lines

  • Steeper learning curve
  • More weight than the use case justifies
  • Same database+app fundamental shape

Grav

~30,000 lines

  • Closest in spirit but designed as a standalone site builder
  • Own template language and plugin architecture
  • Bigger than the use case for client SEO blogs

Nano CMS

~4,500 lines

  • Drop-in onto existing static sites
  • No database, no plugins, no build step
  • Removable admin = minimal long-term attack surface
  • Static HTML stays static

Who it's for

Web developers, not end users.

Nano CMS assumes operators are fluent with Markdown and comfortable with SFTP. It is not aimed at non-technical end users. For those, WordPress is genuinely the right answer.

If you build static sites for clients and want to add ranking blog content without taking on the weight of a full CMS, this is for you.

Like it? Help keep it going.

Nano CMS is solo-developed and MIT-licensed. If it saved you the cost of a WordPress install on a client site, a coffee covers a lot of late-night debugging.