Thirteen, a Gopher server with CGI support
You can find this server’s source code at
https://github.com/abbrev/thirteen-gopher-server[1].
Since early February of 2025 I’ve been developing my own
Gopher server. The initial goal was to have a lightweight
server which can serve only static content (without even
support for processing gophermaps), but as I developed it
and my site concurrently I found utility in supporting CGI
as well. After all, even something as simple as a guestbook
page requires dynamic content. So I added CGI support.
------------------------------------------------------------
CGI support
------------------------------------------------------------
It seems that most of the commonly-used Gopher servers
support CGI in some form. However, support for CGI in the
various servers is spotty and inconsistent.
In developing my own server I wanted to do it properly. I
pored over the Common Gateway Interface RFC[2] to learn what
is actually required of a server and scripts. Sean Conner
did the same[3] a few years ago.
What the RFC _doesn’t_ specify is exactly how a server maps
a request to a script. I decided to support index CGIs
(which are named, oddly enough, `index.cgi`) with additional
path information (`PATH_INFO`). What this means is that,
if `/foo/index.cgi` exists, a request for `/foo/bar` will
end up calling the script `/foo/index.cgi` with `PATH_INFO`
set to `/bar`. Many other servers decide to report that as
an error, which is fine and works for those servers. But I
believe my approach offers a lot more flexibility. (And I’m
not the first to come up with this approach; for one, the
CivetWeb web server[4] since V1.10 handles index CGIs with
additional path the same way.)
Some servers (either Web or Gopher) require the script
name to be spelled out in the path, so that only
`/foo/index.cgi/bar` would give meaningful results. Since
the script is not really an index CGI at that point, a site
admin might choose to call the script something else, so a
request might look like `/foo.cgi/bar` instead. Some servers
take this another step backward and require all CGI scripts
to be under a `cgi-bin` directory, so the full request might
look like `/cgi-bin/foo.cgi/bar`. In any case, it’s an ugly
request and exposes the site’s internal mechanisms. See Cool
URIs don’t change[5] for why that’s bad. Properly supporting
index CGIs with additional path avoids these limitations and
ugliness.
------------------------------------------------------------
Gophermaps? Nope!
------------------------------------------------------------
My Gopher server doesn’t support gophermaps, yet my site
uses gophermaps. How does it work? The secret lies in the
above-mentioned index CGI support.
My site has an index CGI in the document root which can
handle various types of gopher map files. With a request
for `/foo/bar`, the server runs the CGI at `/index.cgi`
with `PATH_INFO` set to `/foo/bar`; this script checks if
`/foo/bar/gophermap` exists (and has the right permissions
etc.) and if so processes it and returns the resulting
Gopher menu to the client. The script does the same with
other menu files as well (e.g., `/foo/bar/index.gph` and
`/foo/bar/index.gmi`). If a directory doesn’t contain a
gopher map, the script instead lists its contents as a
Gopher menu. The script can also transparently decompress
text files on the fly (e.g., the script will send the
decompressed contents of `/blah.txt.gz` with a request for
`/blah.txt`).
Why do it this way? One word: flexibility and extensibility.
OK, that’s actually two words, or three if you count the
conjunction. It really boils down to the ability to add
capabilities to a site that aren’t explicitly supported
by the server itself. As long as the server supports the
required mechanisms (CGI, and specifically index CGIs with
path information), the site can implement the desired
functionality with scripts.
------------------------------------------------------------
References
------------------------------------------------------------
[1] https://github.com/abbrev/thirteen-gopher-server
[2] gopher://asciz.com/0/rfc/rfc3875.txt
[3] gopher://gopher.conman.org/0Phlog:2020/01/06.1
[4] https://github.com/civetweb/civetweb
[5] https://www.w3.org/Provider/Style/URI