HomeArticles

Why this site runs on a single PHP file — and why that's a feature, not a hack

The setup

When I rebuilt this blog, I had one constraint: I wanted to be able to deploy it by uploading a single file to a shared host. No Composer, no Node, no database. Just PHP and a text file for content.

The result is a single index.php that handles routing, rendering, and even a basic admin interface for writing posts. The content lives in a Markdown file. That's it.

Why not a framework?

I've used Laravel, Symfony, and WordPress. They're great tools, but for a site that serves a few thousand visitors a month, they bring a lot of overhead. A framework means a bootstrap process, dependency resolution, and a whole directory tree of files. Every request has to load all of that, whether it's a homepage or a 404.

With a single file, the request lifecycle is dead simple: PHP reads the file, matches the URL, and outputs the page. No autoloading, no service providers, no middleware. Just a few conditionals and a loop.

How it works

Here's the core of the routing logic:

$path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');

if ($path === '' || $path === 'index.php') {
    render_home();
} elseif (preg_match('/^post\/([a-z0-9-]+)$/', $path, $matches)) {
    render_post($matches[1]);
} else {
    http_response_code(404);
    echo 'Not found';
}

That's the whole router. The content is stored as a Markdown file with a simple format:

---
title: My post
date: 2025-02-15
---

Post body here...

I parse that with a small function that returns an array. No database, no queries, no caching layer. For a site this size, reading a 20KB file on every request is fast enough. PHP's opcache handles the script itself, and the file system cache is more than adequate.

The admin is a form

To write a new post, I open /admin, which shows a textarea pre-filled with the current content. I edit, hit save, and the script writes the file back using file_put_contents(). There's no authentication beyond a simple password check — I'm the only one who uses it, and it runs on a non-standard port behind a reverse proxy.

It's not fancy, but it works. I've never had a problem with it, and I've been using this setup for three years.

Speed and maintenance

The site responds in under 50ms on a cheap shared host. There's no JavaScript, no external fonts, no CDN. The entire page is maybe 10KB of HTML. That's not because I'm trying to win a speed contest; it's because there's nothing to slow it down.

Maintenance is trivial. When I want to change the layout, I edit a few lines in the same file. When I want to add a feature, I add a function. There's no dependency to update, no security patch to apply (well, apart from PHP itself). I've spent more time writing this article than I've spent on the entire codebase this year.

But isn't it a hack?

People might call this a hack because it's not how you're supposed to build a website. But let me ask: what's the actual cost of this approach? The code is concise, the behavior is predictable, and the server requirements are minimal. It's the opposite of a hack — it's a deliberate choice to do only what's needed.

Sure, if this site grew to thousands of posts and daily traffic, I'd need to revisit. But that's not the situation. And when the time comes, I can move to a framework without throwing everything away — the content is just a text file, and the routing logic is simple enough to recreate.

What I'd suggest

Next time you're starting a small project, ask yourself: do I need a framework? Or can I get away with a single file? You might be surprised how much you can do with a few hundred lines of PHP. And you'll learn more about how HTTP and PHP actually work than you would by installing yet another package.

This site is proof that a single file can be a feature: easier to deploy, easier to maintain, and faster for your visitors. That's not a hack — that's engineering.