uptime.is is powered by CGI

Power of CGI

uptime.is is powered by CGI and newLISP, slowcgi(8), httpd(8), OpenBSD.

I enjoy the power and freedom of CGI, which come from its simplicity, elegance, ease of deployment and stability:

  1. Put an executable file in a directory where a web server can find it
  2. Read data from standard input and environment variables
  3. Do stuff with data
  4. Write data to standard output

In 2019, after 20 years of programming on the web, I still love CGI. This page is a tribute to CGI and a statement that web programming can be simple.

CGI sets the bar low for starting with doing web programming. In 1999, when I was 17 and knew nothing about web programming, but wanted to start, I found an online book about CGI and Perl. I still remember writing code Notepad and uploading files to Tripod, who were offering web hosting with CGI and Perl support. A year later I was writing webmail application for Runbox...

20 years later I still love and appreciate CGI.

Frontend programming

CGI may be used for frontend programming for web applications. The look and feel of the application will depend on the design skills of the programmer.

Backend programming, API and webhooks

CGI is perfect for backend programming for web applications and creating API endpoints for web services and webhooks.

Internet of Things

CGI is perfect for Internet of Things on low-end devices.

Legal Tech

CGI can be used for Legal Tech (a fancy term which is frequently used to describe simple automation tools).


Below is a script which was written to provide a generator of personal data requests to be sent to some entities who monitor the 'net. The script would generate the letters in PDF or LaTeX.

use strict;
use CGI ();
use CGI::Carp qw(fatalsToBrowser);
use IO::File ();
use File::Temp qw(tempdir);
use Date::Format qw(time2str);
use Encode qw(encode_utf8 decode_utf8);
my $q = CGI->new();
if ($q->param('src') and -x $ENV{'SCRIPT_FILENAME'}) {
    print $q->header(-content_type => 'text/plain; charset=utf-8'),
my ($target, $name, $address, $code, $city, $ip, $host, $script, $time) =
    ((map { s/[^\w\d .:@-]//ug; $_ }
      map { decode_utf8(scalar $q->param($_)) }
     qw(target name address code city)),          # map these
     @ENV{qw(REMOTE_ADDR HTTP_HOST SCRIPT_NAME)}, # but not these (needed for /gee below)
     time2str('%H:%M:%S', time));
chomp(my @targets = grep { /\w/ } IO::File->new('targets.txt')->getlines());
$target = $targets[rand @targets] unless $target and grep { $target eq $_ } @targets;
no strict 'refs';
my $letter = decode_utf8(join '', IO::File->new("$target.tex")->getlines());
my $page = decode_utf8(join '', IO::File->new("$target.html")->getlines());
s/(\$[A-Z]+)/lc $1/gee for ($letter, $page);
if ($name and $address) {
    $letter =~ s/\nI tillegg.*blir slettet\.\n//s unless $q->param('ip'); # make sturles happy
    if ($q->param('tex')) {
        print $q->header(-content_type => 'text/plain; charset=utf-8'), encode_utf8($letter);
    my $tmp = tempdir(DIR => '/home/protected/pdf', CLEANUP => 1); # keep no files!
    IO::File->new("> $tmp/x.tex")->print(encode_utf8($letter));
    my $res = qx|/usr/local/bin/pdflatex -halt-on-error -output-directory '$tmp' '$tmp/x.tex' 2>&1|;
    if ($res =~ /^Output written on/m) {
        print $q->header(-content_type => 'application/pdf', -attachment => 'brev.pdf'),
    } else {
        print $q->header(-content_type => 'text/plain; charset=utf-8'), $res;
print $q->header(-content_type => 'text/html; charset=utf-8'), encode_utf8($page);

– Kirill Miazine, 2019-11-12