1SSGs Are Overkill
Static site generators. Hugo, Astro, Next.js, Gatsby — there's no shortage of options.
But let's be honest. Do you really need something this heavy?
SSGs are surprisingly bloated.
Node.js, npm, node_modules, config files, plugins, template engines, build pipelines.
All you want is static HTML output. Why does that require such a massive ecosystem?
Think about it.
Component separation, shared element management, conditional rendering — list out what you actually need, and it sounds exactly like what PHP was built for.
<?= $var ?> is template syntax. <?php include ?> is a component.
And PHP is a programming language. It can do much more.
So what if we just render pages with PHP's built-in server and save the output? That would be an SSG — and that's exactly what we built.
2How It Works
Three steps.
That's it. The whole thing is remarkably simple. The first prototype was done in about an hour.
This system is already running in production on this site and on our company site (nanomix.co.jp) . Multiple landing pages, policy pages, and the top page — all built this way. It was done almost before we knew it — from concept to production in about two hours.
3PHP Is a Programming Language
This might be the most important point.
Typical SSG template engines (Liquid, Nunjucks, Handlebars) are just "template syntaxes." They offer minimal logic — basic conditionals, loops — within a constrained grammar.
PHP is different. It's a general-purpose programming language. At build time, you can do anything.
- Inline images as Base64 — embed small icons without HTTP requests
- Concatenate and minify CSS/JS — merge multiple files into one at build time
- Fetch data from external APIs — pull content from a CMS or spreadsheet during build
- Integrate with AI — call an AI API at build time to generate meta descriptions
- Cache busting — auto-append
?v=a3f1b2c4from file hashes - Environment-based output — emit different code for development vs. production
A template engine's capabilities are defined by the template spec. PHP's capabilities are defined by the language itself — which means they're practically unlimited.
<!-- Example: Inline image as Base64 -->
<img src="data:image/png;base64,<?= base64_encode(file_get_contents('icon.png')) ?>">
<!-- Example: Fetch data from API at build time -->
<?php $news = json_decode(file_get_contents('https://api.example.com/news')); ?>
<?php foreach ($news as $item): ?>
<article><h2><?= $item->title ?></h2></article>
<?php endforeach; ?>
<!-- Example: Cache buster -->
<link rel="stylesheet" href="/css/style.css?v=<?= Site::v('/css/style.css') ?>">
4Compared to Conventional SSGs
How does this differ from existing SSGs? An honest comparison.
| Conventional SSG | PHP-SSG | |
|---|---|---|
| Runtime | Node.js + npm | php.exe only |
| Dependency size | node_modules: hundreds of MB | 0 (zero) |
| Template syntax | JSX / Nunjucks / Liquid etc. | PHP (<?= ?>) |
| Config files | Multiple required | None |
| Learning curve | Framework-specific concepts | Basic PHP syntax only |
| Build predictability | Build and dev can diverge | Server output = build output (100% match) |
| Dev server | Vite / webpack-dev-server etc. | PHP built-in server (included) |
| HMR | Available (needs config) | Unnecessary (PHP processes on every request) |
| Breaking changes | Major updates often break things | PHP's backward compatibility is excellent |
| Portability | npm install required | Works right after git clone |
| Optimal scale | Dozens to thousands of pages | Depends on how far you develop the build tool |
"Optimal scale depends on the build tool" — fair enough. If you make build.php sophisticated enough, it can handle hundreds of pages. But conversely, with a simple build tool, small to medium scale is the sweet spot. Company websites, landing pages, service sites with a few dozen pages — that's where this shines out of the box.
Choose your tools to match the size of your problem.
And when the problem grows, grow your tools.
5Directory Structure
The key is the _php/ directory. This is where PHP shared code lives — headers, footers, utilities. Since this entire directory is excluded during build, nothing from it ends up in production.
The dev server's router.php executes .html files as PHP, so the file extension stays .html. URLs in development match production exactly.
6Writing Templates
There is no proprietary syntax. Just plain HTML with <?php mixed in.
<?php require_once __DIR__ . '/_php/init.php'; ?>
<!DOCTYPE html>
<html lang="ja">
<head>
<!-- Shared meta tags and CSS -->
<?= Site::head('Page Title') ?>
</head>
<body>
<?= Site::header() ?>
<!-- Page-specific content -->
<main>
<h1>This is plain HTML</h1>
</main>
<?= Site::footer() ?>
</body>
</html>
When built, the <?= Site::header() ?> part is expanded into actual HTML (the navigation bar, etc.) and saved. No PHP code remains in the output.
localhost:8090 in your browser to see PHP-processed results in real time. Save the file, reload the page. HMR (Hot Module Replacement) is unnecessary — because PHP processes server-side on every request.7The Build Process
The build.php workflow is remarkably simple. The code is under 200 lines.
- Recursively collect all files in the source directory (
_php/is excluded) - Read each file and check if it contains
<?phpor<?= - PHP templates → send HTTP request to dev server, save the response
- Static files → compare timestamps, copy if newer
- Detect orphan files (files that exist only in the output directory)
Safety mechanisms are built in:
- Destination protection — if the output file is newer than the source, skip it. Prevents accidentally overwriting direct edits
- Orphan detection — warns about files that exist in the output but not in the source
- Dry-run — the
--dry-runflag shows what would be processed without executing - Diff log — the
--diffflag shows git diff after the build
8The Environment Is Portable
The biggest advantage of this approach: it doesn't care about your environment.
Think about what it takes to move a Node.js-based SSG project to a new machine. Install Node.js, run npm install, resolve dependency conflicts, check version compatibility — there goes 30 minutes.
PHP-SSG is different.
git clonethe repo- Double-click
build.bat - Done.
Why? Because the entire ecosystem is version-controlled.
A portable PHP build (php.exe + bundled DLLs) is only a few dozen megabytes. Include it in a .dev-tools/ directory in the repo, and the moment you git clone, the build environment is ready. No npm, no composer. The concept of "environment setup" simply vanishes.
Even if you'd rather not include the executable in the repo, it's not a problem. Just drop php.exe and php.ini into a designated directory. PHP's configuration file is just php.ini — no tsconfig.json, no .babelrc, no postcss.config.js. Even the runtime's configuration is a single file.
.dev-tools/php/. Porting to a new project means copying src/ + _php/ + build.php. A new site's build environment is up and running in five minutes.And don't forget PHP's excellent backward compatibility. Code written for PHP 7 runs on PHP 8 with almost no changes. The "npm install broke after a Node.js major update" scenario? That doesn't happen here. Even when you version-control the entire ecosystem, it's unlikely to stop working years later.
9Simplicity Wins in the AI Era
We're in an era where AI (Claude, ChatGPT, Copilot) writes code. That changes how we choose our tech stack.
AI excels with simple, universal technologies. HTML, CSS, PHP — technologies with 30+ years of history and massive training data. AI handles these remarkably well. AI breaks config files, but it rarely breaks HTML and PHP.
What AI tends to break:
vite.config.tssettings- webpack loader chains
- Plugin version compatibility
- tsconfig.json path resolution
- babel/postcss preset configuration
The more complex the ecosystem, the more places AI has to intervene. The more it touches, the higher the chance something breaks.
PHP-SSG's stack is just HTML + PHP.
There's almost nothing for AI to break.
Easy for AI to Break
- node / npm / vite / webpack
- typescript / babel / eslint
- plugin / loader / adapter
- 10+ config files
- Cross-version compatibility issues
Hard for AI to Break
- HTML + PHP
- No config files
- No proprietary syntax
- Zero dependencies
- 30 years of training data
When you ask AI to write a template, all it needs to write is <?= Site::header() ?>. Essentially the same as React's <Header />, but with no build pipeline to break.
10Frontend Took 20 Years to Come Back to PHP
A brief look at history.
In 1995, when PHP was born, this is how you "componentized" a web page:
<?php include("header.php"); ?>
<h1>Hello</h1>
<?php include("footer.php"); ?>
In 2016, when React introduced SSR:
<Header />
<h1>Hello</h1>
<Footer />
The essence is the same. Split the view into components and generate HTML.
What React innovated was the virtual DOM, state management, client-side rendering, and developer experience. But the structural idea of "componentize and output HTML" is a concept PHP had already perfected 30 years ago.
Frontend took 20 years to come back to PHP.
Next.js App Router, Astro Islands Architecture, Remix Server Components — the latest frontend frameworks are all converging back toward server-side rendering. They built massive ecosystems, only to arrive back where PHP started.
PHP-SSG is the choice to return to that origin. Generate static HTML using a method with 30 years of proven track record, without adding anything unnecessary.
11Room to Scale
"It's only for small projects, right?" — you might think. But look at the architecture. It actually has significant headroom.
Break down PHP-SSG's structure, and three responsibilities separate cleanly:
- PHP — build engine (template processing, data transformation)
- PHP built-in server — SSR renderer (the runtime that generates HTML)
- build.php — orchestrator (what to build, when, and how)
In other words, just grow the orchestrator (build.php) — the renderer and engine stay as they are. Concrete extensions:
- Parallel builds — use
curl_multifor simultaneous rendering. 100 pages at once is feasible - Incremental builds — use
hash(src)to build only changed pages - Route-driven — define
routes.jsonand handle 10,000 pages - Section builds — run
build blog,build docsby category
PHP starts fast, handles I/O well, and excels at HTTP processing. Template rendering is precisely PHP's forte. Even at large scale, the language characteristics won't hold you back.
12Summary
PHP-SSG has exactly three components.
- PHP built-in server —
php -S localhost:8090 - build.php — fetch HTML from the server with curl and save it
- PHP templates — include, function calls, conditionals. Standard PHP features only
Zero external libraries. Zero config files. Zero proprietary concepts to learn.
Many dismiss PHP as "old." But as a template engine, no other language is as ready to use out of the box. Just write <?= ?> inside HTML — a simpler template syntax probably doesn't exist.
And simple technology is powerful in the AI era. Hard to break, easy to understand, easy to port.
Complex tools aren't bad. But when the problem is simple, the solution can be simple too.
What you see in the browser goes straight to production. That's all there is to it.