Matt’s Script Archive contained 14 Perl CGI scripts, 2 C++ programs, and 2 third-party contributions. Here is the complete reference.
Matt’s Script Archive (MSA) was one of the most influential sources of free CGI scripts on the early web. Distributed from worldwidemart.com/scripts/ beginning in 1995 and later mirrored at scriptarchive.com, the archive provided ready-to-use Perl programs that webmasters could install on any Unix server with a cgi-bin directory. Every script shipped as a .tar.gz or .zip archive containing the main Perl file, a README with configuration instructions, and in some cases sample HTML pages. By the late 1990s, FormMail alone had been downloaded more than two million times. The archive stopped receiving updates around 2002, but the scripts remain a landmark in web development history—they taught an entire generation how server-side programming worked.
This page documents every program that appeared in the archive: 14 original Perl scripts written by Matt Wright, 2 C++ ports of existing scripts, and 2 third-party contributions hosted alongside the core collection. For each entry you will find the version number, approximate release date, a technical summary, and a link to the full documentation page.
| # | Script | Language | Version | Date | Purpose |
|---|---|---|---|---|---|
| 1 | FormMail | Perl | 1.6 | 1997-05-02 | Email form handler |
| 2 | Guestbook | Perl | 2.3.1 | 1995-10-29 | Visitor guestbook |
| 3 | WWWBoard | Perl | 2.0A2.1 | 2000-01-07 | Discussion forum |
| 4 | Counter | Perl | 1.1.1 | 1996-04-25 | Graphical hit counter |
| 5 | Simple Search | Perl | 1.0 | ~1997 | Site search engine |
| 6 | TextCounter | Perl | 1.2 | ~1996 | Text-based hit counter |
| 7 | Random Link | Perl | 1.0 | ~1996 | Random URL redirector |
| 8 | Random Text | Perl | 1.0 | ~1996 | Random text display |
| 9 | TextClock | Perl | 1.0 | ~1996 | Date/time via SSI |
| 10 | HTTP Cookie Library | Perl | 2.1 | 1996-12-23 | Cookie management library |
| 11 | Countdown | Perl | 1.0 | ~1997 | Date countdown timer |
| 12 | Free For All Links | Perl | 2.2 | ~1996 | Community link directory |
| 13 | Animation | Perl | 1.0 | ~1996 | Server-push animation |
| 14 | SSI Random Image | Perl | 1.2 | ~1996 | Random image via SSI |
| 15 | TextCounter C++ | C++ | 1.0 | ~1996 | C++ port of TextCounter |
| 16 | TextClock C++ | C++ | 1.0 | ~1996 | C++ port of TextClock |
| 17 | Credit Card Verifier | Perl | 1.0 | ~1997 | Luhn algorithm checker |
| 18 | Book ’em Dano | Perl | 1.0 | ~1997 | Perl utility collection |
Rows 15–16 are C++ ports. Rows 17–18 are third-party contributions hosted alongside the main collection.
FormMail was the most widely deployed CGI script in web history. It accepted any HTML form submission, parsed the fields, and emailed the results to a list of recipients via the server’s sendmail binary. Configuration happened entirely through hidden form fields—recipient, subject, redirect, required—which meant webmasters never had to edit the Perl source to add new forms.
The script read STDIN for POST data or $ENV{'QUERY_STRING'} for GET, decoded URL-encoded pairs, validated required fields, composed a MIME message, and piped it to /usr/lib/sendmail -t. A single configuration variable, @referers, was supposed to restrict which domains could use the script, but the check was trivially bypassed. FormMail v1.0 through 1.6 contained an email header injection flaw (CVE-2001-0357) that let attackers use any installation as an open mail relay. By 2002 it was the number-three attack vector tracked by SecurityFocus.
Despite the security issues, FormMail was historically significant because it gave millions of static HTML sites the ability to collect feedback without any server-side language knowledge. Modern equivalents include PHP’s mail() function, Formspree, Netlify Forms, or any backend API endpoint.
The Guestbook script let visitors sign a public log on any website. Entries included the visitor’s name, email, URL, city, state, country, and a free-text comment. Each new entry was prepended to a flat HTML file so the most recent signatures appeared first. An optional email notification alerted the webmaster whenever someone signed.
Technically, the script wrote directly to an HTML file using Perl’s file I/O, inserting new entries at a marker comment. It used flock() for basic file locking to prevent concurrent-write corruption. Version 2.3.1 shipped as two files: guestbook.pl (the CGI handler) and addguest.pl (the form processor). Configuration variables at the top of each file controlled paths, colors, and field requirements.
Guestbooks were one of the defining features of personal home pages in the mid-1990s, predating comment systems. The concept evolved into WordPress comments, Disqus, and social media walls.
WWWBoard was a threaded discussion forum built entirely on flat HTML files. Each message was stored as its own .html file on disk, and the main board page listed topics with indented replies forming visible threads. Users could post new topics, reply to existing messages, and optionally follow threads via email notifications. An admin script (wwwadmin.pl) allowed the board operator to delete messages, ban words, and set a maximum message count.
The script generated static HTML pages on every post, reading a master index file and rewriting it with the new thread structure. This approach required no database, but it scaled poorly—boards with thousands of messages became slow because every post operation rewrote the index. File locking and sequential numbering ($meession) prevented most race conditions. Version 2.0 Alpha 2.1 (January 2000) was the final release, patching a password bypass in the admin interface.
WWWBoard is the ancestor of all forum software: phpBB, vBulletin, Discourse, and Reddit threads all descend from the same concept of nested discussion.
Counter displayed a graphical hit counter on any web page. It read a count from a flat file, incremented it on each request, and dynamically composed a GIF image by stitching together individual digit images. The output was served as image/gif with a Content-type header, so webmasters embedded it using a standard <img> tag pointing at the CGI URL.
Each digit (0–9) was a separate GIF file in a configurable directory, allowing different visual styles—LED displays, odometer wheels, or simple block numbers. The script used flock() on the counter data file and supported multiple counters per site through a page identifier parameter. It could also be configured to ignore reloads from the same IP within a time window.
Graphical hit counters were ubiquitous on 1990s websites. They have been replaced by analytics platforms like Google Analytics, Plausible, and server-side log analysis tools.
Simple Search provided basic full-text search across a set of HTML files on the server. The webmaster defined a list of directories to index, and the script would scan every file in those directories for the user’s query string, returning a page of matching links with context snippets.
The search was brute-force: on every query, the script opened each file, read its contents, and ran a case-insensitive regex match. There was no pre-built index, which made it practical only for small sites (a few dozen to a few hundred pages). Results were ranked by the number of matches per file. Boolean operators (AND, OR) were supported through Perl regex alternation.
Simple Search filled a real need before hosted search services existed. Today its function is handled by Algolia, Elasticsearch, Meilisearch, or even the site: operator in Google.
TextCounter was the lightweight alternative to the graphical Counter script. Instead of composing a GIF image, it output a plain text number via Server Side Includes (SSI). The webmaster placed an SSI <!--#exec cgi="..."--> directive in their HTML, and the server replaced it with the current visit count on each page load.
The script read a count from a flat file, incremented it, wrote the new value back, and printed the number to STDOUT. Because SSI directives are processed inline by the web server (Apache mod_include), the count appeared as part of the HTML without requiring an image or JavaScript. This made TextCounter faster and simpler than the graphical version, though it lacked visual flair.
Text-based counters disappeared along with SSI. Modern server-side rendering achieves the same result through template engines in PHP, Python, Ruby, or Node.js.
Random Link selected a URL at random from a flat-file list and issued an HTTP redirect to send the visitor there. Webmasters used it as a “random site” button, a way to distribute traffic among partner pages, or a “surprise me” feature on link directories.
The script read a text file where each line contained a URL, counted the lines, generated a random index using Perl’s rand() function seeded with srand(time), and sent a Location: header to redirect the browser. The entire process was stateless—no tracking of which links had already been shown. The redirect was a 302 (temporary) by default.
Random link generators still exist in the form of StumbleUpon successors, Reddit’s “random” button, and JavaScript-based link randomizers on modern sites.
Random Text output a randomly selected text snippet via SSI, similar to how TextCounter worked. Webmasters stored quotes, tips, facts, or taglines in a flat file (one per line or separated by a delimiter), and the script picked one at random on each page load to display inline.
The implementation was minimal: read the data file into an array, pick a random element with $array[rand @array], and print it. Because SSI replaced the directive before the page reached the browser, the random text appeared as native HTML. The script supported HTML markup within entries, so bold text, links, or images could be included in the rotation.
The same functionality now lives in JavaScript widgets, WordPress plugins for rotating quotes, and server-side template logic in any modern framework.
TextClock displayed the current date and time on a web page through SSI. The output format was configurable—webmasters could choose from preset styles or define custom formats using tokens like %H, %M, %S for hours, minutes, and seconds. It supported both 12-hour and 24-hour clocks and could display the server’s local time or a specified timezone offset.
The script called Perl’s localtime() function, formatted the components according to configuration variables, and printed the result as plain text. Since it ran via SSI on every page request, the time was accurate to the moment of page generation (not real-time updating like a JavaScript clock). Some webmasters used it for “last updated” timestamps.
Client-side JavaScript’s Date object and the Intl.DateTimeFormat API have entirely replaced server-side date display for most use cases.
The HTTP Cookie Library was not a standalone script but a Perl module (cookie.lib) that other CGI programs could require to read and write browser cookies. It provided three functions: &SetCookies to send Set-Cookie headers, &GetCookies to parse the $ENV{'HTTP_COOKIE'} string into a hash, and &DeleteCookies to expire existing cookies.
Version 2.1 supported the Netscape cookie specification including path, domain, expiry, and secure flags. The library predated Perl’s CGI.pm cookie handling (which shipped with Perl 5.004 in 1997) and was simpler to integrate into existing scripts. Many webmasters used it alongside FormMail or Guestbook to remember returning visitors.
Cookie handling is now built into every web framework and language runtime. JavaScript’s document.cookie API and the newer CookieStore API handle it on the client side.
Countdown computed and displayed the time remaining until a specified future date. Output was rendered via SSI as plain text, for example: “32 days, 14 hours, 27 minutes until launch.” The target date was set in configuration variables at the top of the script, and the output format could be customized to show only days, or days and hours, or the full breakdown including seconds.
Internally the script converted both the target date and the current time to epoch seconds using Perl’s timelocal(), subtracted them, and divided the difference into days, hours, minutes, and seconds. If the target date had passed, it could either display zero or switch to counting up from the event.
Countdown timers are now implemented almost exclusively in client-side JavaScript, often with real-time updates. Libraries like countdown.js and CSS-based timers have replaced server-side solutions.
Free For All Links (FFA Links) was a community link directory where anyone could add a URL. The page displayed categorized links in a single HTML file, and a form at the top let visitors submit new links with a title, URL, and category selection. No registration or approval was required—hence “free for all.”
The script read the existing links page, inserted the new entry at the top of the selected category section, and rewrote the file. A configurable maximum number of links per category prevented unlimited growth—when the limit was reached, the oldest link was dropped. Version 2.2 added email notification, link validation (checking that URLs started with http://), and basic profanity filtering.
FFA link pages were a precursor to social bookmarking sites like del.icio.us, Digg, and later Reddit and Hacker News. They were also heavily abused by SEO spammers, which contributed to their decline.
The Animation script created rudimentary animations using Netscape’s server-push mechanism. It sent a series of images to the browser as a multipart/x-mixed-replace response, where each part replaced the previous one in the browser window. The effect was a flip-book animation that played without JavaScript, Java applets, or browser plugins.
The script set the Content-type to multipart/x-mixed-replace;boundary=ThisRandomString, then looped through a list of image files, sending each one with the appropriate MIME boundary and a configurable delay between frames (implemented via Perl’s sleep()). The animation could be set to loop indefinitely or stop after one pass.
Server-push animation was Netscape-specific and never worked reliably in Internet Explorer. It was superseded by animated GIFs, Flash, and ultimately CSS animations, the Web Animations API, and <video>/<canvas>.
SSI Random Image displayed a randomly selected image on each page load. The script was called via an SSI directive and output an <img> HTML tag pointing to a randomly chosen file from a configured list of images. It was commonly used for rotating banner ads, random header graphics, or photo galleries.
The image list could be specified either as a configuration array in the script or by reading all files from a directory (filtering by extension). The script picked one with rand(), constructed the <img> tag with configurable width, height, alt, and optional link-wrap attributes, and printed it. Version 1.2 added support for linking each image to a different URL through a parallel array.
Random image rotation is now handled by JavaScript, CSS background cycling, or ad-serving platforms. Server-side implementations use template helpers in PHP, Django, Rails, or similar frameworks.
Matt Wright also distributed C++ versions of two SSI scripts. The C++ programs compiled to native binaries, which executed faster than Perl scripts on the same hardware. However, they required a C++ compiler on the server and were harder to configure, so they saw less adoption than their Perl counterparts.
The C++ version of TextCounter performed the same function as the Perl original: it incremented a counter stored in a flat file and printed the current count to STDOUT for SSI inclusion. The difference was performance—a compiled binary executed in microseconds without the overhead of loading the Perl interpreter, which mattered on heavily trafficked pages where SSI scripts ran on every request.
The source code was a single .cpp file that used standard C++ file I/O (fstream) and basic string formatting. Compilation required a simple g++ -o textcounter textcounter.cpp command, and the resulting binary was placed in the cgi-bin directory with execute permissions. Configuration was done through command-line arguments or by editing constants in the source before compiling.
Compiled CGI programs have been superseded by FastCGI, application servers, and interpreted languages with opcode caching (PHP OPcache, Python bytecode).
The C++ TextClock was a compiled equivalent of the Perl TextClock. It called the system time() and localtime() functions, formatted the result according to a configurable template, and printed it for SSI inclusion. The same format tokens as the Perl version were supported.
Like the C++ TextCounter, this program offered faster execution at the cost of requiring compilation. The source was a single .cpp file using <ctime> for time functions and <iostream> for output. Most webmasters stuck with the Perl version because shared hosting providers rarely offered compiler access.
Server-side date formatting is now a one-line operation in any language (date() in PHP, strftime in Python, Intl.DateTimeFormat in JavaScript).
Two scripts written by other authors were hosted alongside the core collection on Matt’s Script Archive.
Written by an author known as Spider, the Credit Card Verifier implemented the Luhn algorithm (mod-10 check) to validate credit card numbers client-side form submissions before sending them to a payment processor. It accepted a card number, determined the card type (Visa, MasterCard, American Express, Discover) based on the prefix and length, and ran the checksum to verify that the number was mathematically valid.
The script parsed the card number from form input, stripped spaces and dashes, identified the issuer by matching the leading digits against known BIN ranges, and applied the Luhn formula. It returned either a validation success page or an error explaining what was wrong (bad checksum, unrecognized card type, wrong length). The script did not process payments or connect to any gateway—it was purely a format validator.
Luhn validation is now built into payment libraries (Stripe.js, Braintree), HTML5 pattern attributes, and server-side validation frameworks.
Book ’em Dano, written by Dan Oberlin, was a collection of Perl utility subroutines packaged as a library file. It provided common functions that CGI script authors needed but had to rewrite from scratch: form data parsing, HTML entity encoding, date formatting, file reading/writing, and basic string manipulation.
The library was distributed as a single .pl file that other scripts could include with require. Functions included &parse_form (similar to CGI.pm’s param()), &html_escape for encoding special characters, &read_file and &write_file for simplified file I/O, and &date_format for human-readable timestamps. It was designed as a lightweight alternative to the much larger CGI.pm module.
These utility functions are now standard library features in every web language: PHP’s htmlspecialchars(), Python’s html.escape(), Node.js template engines with auto-escaping.
The Not Matt’s Scripts (NMS) project, started by the London Perl Mongers in 2001, produced security-audited, drop-in replacements for the most popular Matt’s Script Archive programs. NMS scripts were designed to be compatible with existing configurations while fixing the security vulnerabilities (especially the FormMail header injection flaw) that plagued the originals.
| Original Script | NMS Replacement | NMS Version | Key Fixes |
|---|---|---|---|
| FormMail 1.6 | NMS FormMail | 1.93 | Header injection fix, taint mode, @postmaster |
| Guestbook 2.3.1 | NMS Guestbook | 1.22 | Input sanitization, HTML encoding, spam prevention |
| WWWBoard 2.0A2.1 | NMS WWWBoard | 1.22 | XSS prevention, secure admin password handling |
| Free For All Links 2.2 | NMS Free For All Links | 1.14 | URL validation, anti-spam measures |
| Simple Search 1.0 | NMS Simple Search | 1.10 | Path traversal fix, input validation |
| TextCounter 1.2 | NMS TextCounter | 1.10 | File locking improvements, taint mode |
| Random Link 1.0 | NMS Random Link | 1.10 | Input sanitization, header injection prevention |
| Random Text 1.0 | NMS Random Text | 1.10 | HTML encoding, path validation |
| TextClock 1.0 | NMS TextClock | 1.10 | Taint mode, input validation |
| SSI Random Image 1.2 | NMS SSI Random Image | 1.10 | Path validation, output encoding |
-T) and pass use strict and use warnings.
The complete Matt’s Script Archive collection is available for download as a single package. This includes all 14 Perl scripts, both C++ programs, README files, sample HTML pages, and documentation.
All scripts, documentation, and example files in one download. Includes original README files and configuration instructions.
All Perl CGI scripts in the archive follow the same general installation procedure. These steps apply to any Unix/Linux server with Apache and Perl installed.
.pl file to your server’s cgi-bin directory using FTP or SCP. Upload in ASCII mode (not binary) to ensure line endings are correct for Unix.
chmod 755 script.pl. Data files that the script writes to need chmod 666 or chmod 777 for the directory.
#!/usr/bin/perl), your domain in @referers, and any output paths. Each script’s README lists every configurable variable.
https://yourdomain.com/cgi-bin/script.pl. If you see a 500 Internal Server Error, check the Apache error log (/var/log/httpd/error_log) for the Perl error message. Common issues: wrong Perl path on line 1, missing modules, or incorrect file permissions.
action attribute to the CGI URL. For SSI-based scripts (TextCounter, TextClock, Random Text), use <!--#exec cgi="/cgi-bin/script.pl"--> and ensure your server has SSI enabled (Options +Includes in Apache).
For detailed installation instructions, troubleshooting, and server configuration, see the Installation Help section.
Browse all scripts with download links, documentation, and working examples.
Frequently asked questions for each script, covering installation, configuration, and common errors.
Get the complete archive in a single download. All Perl scripts, C++ programs, and documentation.
Step-by-step instructions for installing CGI scripts on Apache, Nginx, and other web servers.
The story of WorldWideMart.com and Matt’s Script Archive from 1995 to the present.
Third-party extensions, modifications, and utility scripts contributed by the MSA community.