← Pablo Murad

curl pablomurad.com

My blog now has a terminal edition. Run `curl pablomurad.com` to read posts, browse archives and explore tags in ANSI color. Eleventy builds it automatically, and nginx handles the rest. A small tribute to the internet I grew up with.

Contents

Open a terminal and type:

curl pablomurad.com

Since 14 October 2026, that command has opened my blog in the terminal. Not a dump of the HTML you would normally get in a browser, but a separate version with colors, posts, archives and full articles, all formatted for an 80-column window.

The website itself hasn’t changed. Visit it in a browser and you’ll see the same site as before.

One Windows detail: PowerShell has its own ideas about what curl means. Use curl.exe there.

Where the idea came from

I found the idea on nytpu’s blog, in a post from 18 March 2026 called “Fancy Curl Sites” (nytpu.com/gemlog/2026-03-18). nytpu had seen it on trinkey.com and proot.party. All three sites are worth trying with curl.

The trick is wonderfully simple. The server looks at the User-Agent, notices that the request came from curl and sends back a text file with ANSI escape codes instead of a regular web page.

nytpu wrote the terminal home page by hand and gets the posts from a markup language they built, which already knows how to produce terminal output. My setup is different. The blog is a static site built with Eleventy, and its posts come from PaCord, my own fork of Pagecord. PaCord stores and serves the posts; it doesn’t render terminal pages.

I wanted the entire blog available this way, not just a clever home page. And I certainly didn’t want to maintain two versions of every post.

After years on BBSes, and after building Hammer, my little BBS that you connect to over SSH, I was unlikely to leave the idea alone.

What you see in the terminal

The home page begins with the site name in ASCII art. I made it once with figlet’s slant font and kept the result as a constant. There’s a short line about me, followed by the twenty latest posts. Each entry includes the command to open it:

2026-09-10  Hammer
            $ curl pablomurad.com/hammer/

 

Under the list are shortcuts to the archive, tags, about page and RSS feed, along with links to my profiles elsewhere.

Open a post and you get its title highlighted in the same lime green I use on the site, followed by the date, tags and body text. Paragraphs wrap at 72 columns. Code blocks keep their original lines with four spaces of indentation. Quotes have a bar on the left, and nested lists stay nested.

Images become numbered placeholders such as [image: description]. Links stay in the prose with a number in brackets, then all the addresses appear at the bottom, much like they do in Lynx. Related posts are listed as curl commands. If the post also lives on Mastodon, Bluesky or Nostr, those copies appear under “Also on”.

Tables took more work than I expected.

Some posts have tables with cells far too long for 72 columns. If a table fits, I draw it with aligned columns. If it doesn’t, each row turns into a little block: the first cell is bold, with the remaining cells underneath as “Header: value”. It sounds awkward, but it’s much easier to read than a table spilling off the side of the screen.

The archive, tag pages, ordinary pages and even the 404 page have terminal versions. The search page, photos and newsletter form don’t. Those need a browser, so curl still receives HTML for them.

Building it without another publishing job

The converter lives in one file, lib/terminal.js. It’s a few hundred lines and adds no dependencies. The site already uses cheerio to work on images and headings, so I reused it to walk through each post’s HTML.

Paragraphs become wrapped text, strong becomes bold, em becomes italic, and code gets cyan. The wrapping logic measures what you can actually see on screen, ignoring ANSI escape codes because they occupy no columns. It also closes and reopens styles when a line wraps, so a bold word doesn’t accidentally turn the next quote bar or list marker bold.

For colors, I stuck to the basic ANSI codes and the 256-color palette. Everything I tested understood them. The highlight uses color 191, the closest match I could get to the site’s #d4ff3a. It’s a lime background with black letters. Lime-colored text disappears on some light terminal themes; black on lime works on both light and dark ones.

I also kept the layout entirely in ASCII: #, *, | and -. The old Windows console can make a mess of box-drawing characters, and I’d rather have something plain that works.

Eleventy needed just one more template. It loops over the screens produced by the converter, using the same data as the HTML pages, and writes an index.ansi beside each index.html:

dist/hammer/index.html
dist/hammer/index.ansi

 

Publishing hasn’t changed at all. I write a post in PaCord, it calls the builder, Eleventy runs, and the terminal copy comes along for free. The full build still takes about 17 seconds on the server.

Letting nginx decide

The last piece is nginx. The build generates a rules file, which I include inside the server block. I couldn’t use map there, since that directive belongs in the http context, but set and if were enough for this:

set $terminal_index index.html;
set $not_found /404.html;
if ($http_user_agent ~* "^curl/") {
    set $terminal_index index.ansi;
    set $not_found /404.ansi;
}

 

There’s also a small exception for requests carrying the X-Original header, which switches the index back to index.html. The idea is to keep the terminal response where it belongs without changing what the normal site serves.

And that’s it. No separate service, no hand-written terminal pages, no extra step whenever I publish. Just another way into the same blog.

I’ve caught myself reading my own posts through curl now. I suppose that means the experiment worked.

Reply by email

Your message goes straight to Pablo Murad. Your address is only used to write back.

Comments

Leave a comment

Comments are read before they show up here. No email address needed.