Yes, really: the site you're reading was built with bash-stack, an HTTP server and web framework written entirely in Bash. It's the kind of thing you build because it's fun and because it forces you to think about what a web framework actually needs. This post explains how it works, and what I fixed to make the blog functional.
How it works
File-based routing. Every route is a `.sh` file in `pages/`, `pages/index.sh` is `/`, `pages/blog.sh` is `/blog`, and `pages/blog/[slug].sh` renders individual posts. bash-stack's core (`core.sh`) parses HTTP requests in pure Bash and executes the matching page script.
Components. Shared bits, header, footer, intro, recent posts, projects, live in `components/` and are `source`d by the pages.
Markdown posts. Each blog post is a markdown file in `blog/` with YAML-ish frontmatter (`title`, `date`, `excerpt`). The page script pulls the frontmatter with `awk` and converts the body to HTML with `sed`. It's a miniature markdown engine, and it's about fifty lines.
Static output. A real site needs to survive GitHub Pages, so `build.sh` starts the server locally, `curl`s each route into `dist/`, copies the assets, and touches `.nojekyll`. The GitHub Actions workflow in `.github/workflows/deploy.yml` does the same thing on every push to `main`, installs `ucspi-tcp` (for `tcpserver`), runs the build, publishes `dist/` to the `gh-pages` branch.
The bug that had to be fixed
The original build only pre-rendered two pages: `/` and `/blog`. Every post link pointed at `/blog/`, but no post page was ever generated, so every individual blog post 404'd. Classic.
The fix was small: have `build.sh` discover the markdown files and pre-render each one into `dist/blog/.html`. There's also a date-sorting fix in the "recent posts" component so the list actually orders newest-first and shows real dates.
Why bash?
Honestly? Because it's a fun constraint, and because a framework that can't afford to be complex is a nice reminder that most of "web development" is convention, not machinery. The components, the frontmatter, the static export: it all maps 1:1 to what Jekyll or Hugo does, just with fewer moving parts.
The framework is [cgsdev0/bash-stack](https://github.com/cgsdev0/bash-stack), give it a try if you like seeing how far shell can go.