Back to Articles Building a Flat-File Blog on a LEMP Stack July 26, 2026 • PHP Nginx WebDev Tutorial # Building a Flat-File Blog on a LEMP Stack Many developers default to complex platforms like WordPress or Ghost when creating a personal blog. However, for a simple site focused on reading speed, security, and developer-friendly publishing, a **flat-file blog engine** is often superior. In this guide, we'll walk through how to build a dynamic, database-free blog using PHP and Nginx on a LEMP stack. ## Why Choose a Flat-File Architecture? 1. **Lightning-Fast Performance**: There are no database queries. Server response times are in the single-digit milliseconds. 2. **Infinite Security**: Since there is no database (no SQL) and minimal dynamic backend logic, the attack surface is virtually zero. No SQL injection, no complex administrative portals to hack. 3. **Markdown-Based Workflow**: You write posts in local `.md` files using your favorite text editor (VS Code, Vim, Obsidian) and publish them by simply transferring them to your server. 4. **Zero Maintenance**: No database backups, no regular plugin updates, and extremely low resource usage on your server. --- ## 1. Organizing Your Directory Structure To keep raw Markdown files secure, it's best to separate them from Nginx's public document root. That way, users cannot access the raw draft files directly via the browser. We use the following structure: ```text /var/www/gungormehmet.com/ ├── public/ # Public webroot (accessible via HTTP) │ ├── index.php # Parses & displays the list of posts │ └── post.php # Parses & displays a single post └── posts/ # Secure folder outside public webroot └── article.md # Raw Markdown files ``` --- ## 2. Reading Post Metadata in PHP Each Markdown file starts with a header block known as **Front Matter**, formatted with YAML-like syntax between triple dashes (`---`). ```markdown --- title: Getting Started with Nginx date: 2026-07-26 tags: Nginx, Sysops description: A short description of the post... --- ``` In PHP, we scan the `posts/` folder, open each file, and extract this metadata using regular expressions: ```php function get_post_metadata($filepath) { $content = file_get_contents($filepath); $meta = []; if (preg_match('/^---\s*\n(.*?)\n---\s*\n(.*)/s', $content, $matches)) { $meta_section = $matches[1]; // Parse YAML-like key-value pairs $lines = explode("\n", $meta_section); foreach ($lines as $line) { $parts = explode(':', $line, 2); if (count($parts) === 2) { $key = trim($parts[0]); $value = trim($parts[1]); $meta[$key] = $value; } } } return $meta; } ``` --- ## 3. Creating Clean URLs with Nginx Rather than forcing users to visit URL routes like `/post.php?id=my-post`, we can configure Nginx to rewrite elegant routes such as `/post/my-post` automatically: ```nginx # Clean URL routing for posts: /post/some-post -> /post.php?id=some-post location /post/ { rewrite ^/post/([^/]+)/?$ /post.php?id=$1 last; } ``` Now, when a user accesses `/post/my-post`, Nginx serves `/post.php?id=my-post` behind the scenes, keeping the address bar clean. --- ## 4. Efficient Rendering of Markdown To display the Markdown beautifully, we can pass the raw markdown string to the frontend and utilize the popular, ultra-fast `marked.js` library via CDN. This shifts the Markdown-to-HTML rendering load to the client's browser, maximizing server scalability and speed. Simply load `marked.js` in your script: ```html <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <script> const rawMarkdown = document.getElementById('raw-md').textContent; document.getElementById('content').innerHTML = marked.parse(rawMarkdown); </script> ``` This combination of a PHP file-system scanner and a client-side Markdown renderer yields a lightweight, database-free publishing system that is extremely elegant and maintainable. Loading article...