The Common Gateway Interface (CGI) is a standard protocol, formally defined in RFC 3875, that specifies how a web server can execute an external program and return its output as an HTTP response. When a browser sends a request to a CGI-enabled URL, the web server does not simply serve a static file. Instead, it launches a program — a CGI script — passes the request data to it through environment variables and standard input, collects whatever the program writes to standard output, and sends that back to the browser as the response. CGI scripts could be written in virtually any programming language: Perl, C, Python, shell scripts, Tcl, or anything else that could read from stdin and write to stdout. For nearly a decade, from roughly 1993 to 2003, CGI was the dominant method for making websites do anything beyond serving static HTML pages.
The mechanics of CGI are surprisingly straightforward. Understanding them helps explain both why CGI was so accessible and why it eventually became a bottleneck.
When a browser requests a URL mapped to a CGI script, the web server performs the following steps:
The CGI specification defined a set of environment variables that the web server must populate before executing the script. These were the script's window into the request:
| Variable | Purpose | Example |
|---|---|---|
REQUEST_METHOD |
HTTP method used | GET or POST |
QUERY_STRING |
Everything after the ? in the URL |
name=John&age=30 |
CONTENT_TYPE |
MIME type of POST body | application/x-www-form-urlencoded |
CONTENT_LENGTH |
Size of POST body in bytes | 24 |
REMOTE_ADDR |
Client IP address | 192.168.1.100 |
SERVER_NAME |
Server hostname | www.worldwidemart.com |
SCRIPT_NAME |
Path to the CGI script | /cgi-bin/formmail.pl |
HTTP_USER_AGENT |
Browser identification string | Mozilla/4.0 |
A CGI script's output had to begin with HTTP headers, followed by a blank line, followed by the body. The absolute minimum requirement was a Content-type header. Here is a complete, working CGI script in Perl that generates a dynamic HTML page:
#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "<html><body>\n";
print "<h1>Hello from CGI</h1>\n";
print "<p>Your IP address is: $ENV{'REMOTE_ADDR'}</p>\n";
print "<p>Server time: " . localtime() . "</p>\n";
print "<p>Query string: $ENV{'QUERY_STRING'}</p>\n";
print "</body></html>\n";
That double newline after Content-type: text/html\n\n was critical. Omitting it — printing just one \n instead of two — was one of the most common mistakes beginners made, and it resulted in a 500 Internal Server Error. The first newline terminates the header line; the second newline creates the blank line that separates headers from the body.
The concept of a dedicated directory for executable scripts originated with Rob McCool and the NCSA HTTPd web server, first released in November 1993 at the National Center for Supercomputing Applications at the University of Illinois at Urbana-Champaign. NCSA HTTPd was the most popular web server before Apache (which itself began as a collection of patches to NCSA HTTPd — "a patchy server").
The convention was simple: files inside the /cgi-bin/ directory were not served as static content. Instead, the web server treated them as programs to be executed. A request to http://example.com/cgi-bin/script.pl would not display the Perl source code — it would run the script and return whatever the script printed.
In Apache, this mapping was configured with the ScriptAlias directive:
ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/
This directive told Apache that any URL starting with /cgi-bin/ should be mapped to the filesystem path /usr/local/apache/cgi-bin/, and that files found there should be executed rather than served. Scripts also needed the Unix execute permission bit set:
chmod 755 /usr/local/apache/cgi-bin/formmail.pl
The 755 permissions meant the file owner could read, write, and execute the file, while the web server process (running as a different user) could read and execute it. This permission model was a source of both security and confusion. Shared hosting providers spent considerable effort ensuring that CGI scripts from one customer could not read files belonging to another — a challenge that led to solutions like suEXEC and CGIWrap.
The cgi-bin convention became so deeply embedded in web culture that the path /cgi-bin/ appeared in millions of URLs throughout the 1990s and early 2000s. It was one of the most recognizable URL patterns of the era, a visible indicator that a website was using server-side programming. Even today, legacy URLs containing /cgi-bin/ can be found in web archives and on still-running older systems.
While CGI was language-agnostic by design, one language dominated the CGI era so thoroughly that "Perl CGI" became almost redundant — like saying "web browser." Larry Wall's Perl earned the nickname "the duct tape of the internet" because it was the tool that held the early dynamic web together.
Several factors made Perl the default choice for CGI scripting:
By the mid-1990s, Perl was already a standard tool on virtually every Unix and Linux system. System administrators used it for text processing, log analysis, and automation long before the web existed. When web servers needed CGI scripts, Perl was simply already there — no installation required.
Perl 5, released in October 1994, had the most powerful regular expression engine of any mainstream language. Since CGI scripts spent most of their time parsing form data, generating HTML, and manipulating strings, Perl's text processing abilities made it a natural fit. What took 50 lines of C could be done in 5 lines of Perl.
The Comprehensive Perl Archive Network (CPAN), launched in 1995, became the largest repository of reusable code modules for any language at the time. Modules like CGI.pm (by Lincoln Stein), DBI (database access), LWP (web requests), and MIME::Lite (email) meant that developers rarely had to build functionality from scratch.
Perl scripts did not require a compiler, a build step, or a complex project structure. A single .pl file uploaded to the cgi-bin directory was a complete application. This simplicity was critical in an era when many webmasters were hobbyists, students, or small business owners with limited technical backgrounds.
The alternatives each had significant drawbacks for CGI use. C required compilation for each server platform, manual memory management, and considerably more code for string operations. Python (version 1.x in 1995) was less mature, had a smaller standard library, and was not as widely pre-installed on hosting servers. Shell scripts (bash, sh) lacked data structures and string processing needed for anything beyond trivial tasks. Tcl had a small but devoted following, particularly for expect scripts, but never achieved Perl's reach.
The CGI.pm module, written by Lincoln Stein and included in the Perl core distribution from 1997 to 2015, became the de facto standard for handling CGI in Perl. It provided form parameter parsing, cookie handling, HTML generation, file upload processing, and proper header output. A typical Perl CGI script using CGI.pm looked like this:
#!/usr/bin/perl
use CGI;
my $q = CGI->new;
print $q->header('text/html');
print $q->start_html('Form Results');
print $q->h1('Thank you, ' . $q->param('name'));
print $q->p('Your message has been received.');
print $q->end_html;
Matt Wright's scripts on worldwidemart.com were all written in Perl. FormMail, Guestbook, Counter, WWWBoard — every script in the collection was a Perl CGI program. This was not unusual; it was the standard. Perl and CGI were effectively synonymous in web development from 1995 to 2000.
For all its historical importance, CGI had fundamental architectural limitations that made it unsuitable for the growing demands of the web. As websites moved from thousands to millions of daily visitors, these limitations became critical.
This was CGI's defining weakness. Every HTTP request to a CGI script caused the operating system to fork a new process, load the interpreter (for interpreted languages like Perl), parse the script, execute it, and then destroy the process. On a busy site receiving 100 requests per second, this meant 100 Perl interpreters being started and destroyed every second. Each process consumed memory (typically 5–15 MB for a Perl CGI script), CPU time for startup and teardown, and file system reads to load the script and any modules it required.
A static HTML file could be served in under a millisecond. A CGI script typically took 50–200 milliseconds just for the process overhead, before any actual application logic ran. Under load, this difference was catastrophic.
Because each request spawned a new process, there was no way to maintain state between requests within the CGI program itself. Database connections could not be pooled. In-memory caches were impossible. Configuration files had to be re-read on every request. Any shared state had to go through external storage — files, databases, or shared memory segments — adding latency and complexity.
The cgi-bin model placed executable files on the web server, often in a shared hosting environment where dozens of customers had CGI access. Poorly written scripts could expose file systems, leak environment variables, or be exploited to execute arbitrary commands. FormMail's early versions, for instance, could be abused as an open email relay because they did not adequately validate the recipient address. This vulnerability was documented by CERT and affected millions of websites.
Each CGI script was essentially a standalone application. There was no framework, no routing, no middleware pipeline. If ten CGI scripts on the same site needed database access, each one had to include its own database connection code. Shared libraries helped, but the lack of an application structure meant that CGI-based sites tended to become collections of loosely related scripts rather than cohesive applications.
The transition away from CGI was not a single event but a gradual evolution spanning more than a decade. Each replacement addressed specific CGI limitations while introducing its own tradeoffs.
| Year | Technology | Key Innovation |
|---|---|---|
| 1996 | mod_perl | Embedded the Perl interpreter directly inside the Apache web server process. Scripts ran persistently, eliminating the process-per-request overhead. Perl code was loaded once and executed from memory on subsequent requests, making it 10–100x faster than standard CGI. |
| 1996 | ASP (Active Server Pages) | Microsoft's server-side scripting for IIS. VBScript or JScript code embedded directly in HTML files. Brought server-side programming to the Windows ecosystem. |
| 1997 | PHP (as Apache module) | Rasmus Lerdorf's "Personal Home Page Tools" evolved into a full language embedded in Apache via mod_php. PHP files mixed HTML and code, were placed anywhere in the document root (no cgi-bin needed), and the interpreter stayed resident in memory. PHP's radical simplicity — drop a .php file on the server and it works — eventually made it the dominant web language. |
| 1997 | Java Servlets | Sun Microsystems introduced servlets as persistent Java programs running inside a JVM container (Tomcat, JBoss). The JVM stayed running, handling thousands of requests without process creation overhead. This led to JSP, Struts, Spring, and the enterprise Java ecosystem. |
| 1996–1999 | FastCGI | A backward-compatible evolution of CGI. Instead of spawning a new process per request, FastCGI kept the CGI program running as a persistent daemon. The web server communicated with it over a socket. This gave CGI's language flexibility with near-module performance. FastCGI is still widely used today, particularly for PHP-FPM (FastCGI Process Manager). |
| 2003 | WSGI (Python) | The Web Server Gateway Interface standardized how Python web applications communicated with web servers. WSGI enabled frameworks like Django (2005) and Flask (2010) and is still the foundation of Python web deployment. |
| 2007 | Rack (Ruby) | Ruby's equivalent of WSGI, enabling Rails, Sinatra, and other Ruby frameworks to work with any compatible web server. |
| 2009 | Node.js | Ryan Dahl's event-driven JavaScript runtime eliminated the separate web server entirely. A Node.js application is the server, handling HTTP requests directly with an event loop rather than threads or processes. |
| 2014+ | Serverless (AWS Lambda, Cloud Functions) | Functions-as-a-service platforms abstract away the server completely. Code runs in response to events, auto-scales, and bills per execution. In a sense, serverless is CGI's spiritual successor — a function that runs, returns a response, and goes away — but implemented at infrastructure scale with container reuse instead of process forking. |
The common thread in all of these replacements is persistence. CGI created and destroyed a process for every request. Everything that replaced it found a way to keep the application code loaded in memory between requests, whether by embedding the interpreter in the web server (mod_perl, mod_php), running a separate application server (servlets, FastCGI, WSGI), or building the HTTP server into the application itself (Node.js).
CGI is not dead, but it has largely retreated from the public web into niches where its simplicity and universality still provide value.
Apache still fully supports CGI through mod_cgi and mod_cgid. Nginx does not have native CGI support but can proxy to CGI scripts through fcgiwrap or by using FastCGI. Both remain documented and maintained features of their respective servers.
Legacy systems are the most significant remaining use case. Government websites, university departments, internal corporate tools, and scientific computing environments often have Perl CGI scripts that have been running, unchanged, for 15–20 years. These scripts work, they serve their purpose, and rewriting them in a modern framework would cost time and money with no functional benefit. In these environments, CGI's simplicity is actually an advantage — there are no framework dependencies to update, no security patches for middleware layers, just a Perl script and a web server.
Perl itself continues to be actively developed. Perl 5.40 was released in June 2024, and the language maintains a dedicated community. However, CGI.pm — the module that was synonymous with Perl web programming — was removed from the Perl core distribution in version 5.22 (2015). It remains available on CPAN for anyone who needs it, but its removal from core was a symbolic acknowledgment that CGI is no longer the standard approach to web development in Perl.
Education is another area where CGI retains value. Because the CGI protocol is so simple — environment variables in, text on stdout out — it is an excellent teaching tool for understanding how web servers and server-side programs communicate. Many computer science courses still use CGI examples to explain the HTTP request/response cycle before introducing more abstract frameworks.
Git's web interface, gitweb, is a well-known example of a CGI application that remained in widespread use well into the 2010s. Written in Perl, gitweb provided a web-based view of Git repositories and shipped with Git itself. While largely superseded by GitLab, GitHub, and Gitea, gitweb demonstrated that CGI could serve real-world needs long after the technology was considered obsolete for new development.
CGI stands for Common Gateway Interface. It is a standard protocol (defined in RFC 3875) that specifies how a web server passes requests to an external program and receives the output back as an HTTP response. CGI was the first widely adopted method for generating dynamic content on the World Wide Web. The term "gateway" refers to the web server acting as a gateway between the HTTP client (browser) and the external application.
CGI is still technically supported by web servers like Apache and Nginx (via fcgiwrap), but it is rarely used for new projects. It survives primarily in legacy systems — government websites, university tools, internal corporate applications — where Perl CGI scripts have been running reliably for decades and rewriting them would provide no functional benefit. For new web development, modern alternatives like FastCGI, WSGI, application servers, and serverless functions are universally preferred.
CGI scripts could be written in any programming language capable of reading environment variables and writing to standard output. In practice, Perl dominated CGI programming from 1995 to 2002, earning it the nickname "the duct tape of the internet." Other languages used for CGI included C (for performance-critical applications), Python, shell scripts (bash/sh), Tcl, and even compiled languages like Fortran in scientific contexts. The language-agnostic nature of CGI was one of its design strengths.
CGI was gradually replaced by technologies that kept processes running persistently rather than spawning a new process for each request. The major replacements, in chronological order: mod_perl (1996) embedded Perl in Apache; ASP (1996) brought server-side scripting to Microsoft IIS; PHP as an Apache module (1997) eliminated the need for cgi-bin entirely; Java Servlets (1997) ran inside a persistent JVM; FastCGI kept CGI processes running as daemons; WSGI (2003) standardized Python web apps; Rack (2007) did the same for Ruby; Node.js (2009) made the application its own server; and serverless platforms (2014+) like AWS Lambda abstracted the server away entirely.
The cgi-bin directory is a special folder on a web server designated for executable CGI scripts. It was introduced with the NCSA HTTPd web server in November 1993 by Rob McCool. Files placed in cgi-bin were treated as programs to be executed rather than static files to be served. In Apache, the mapping was configured using the ScriptAlias directive (e.g., ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/), and scripts required execute permissions (chmod 755). The /cgi-bin/ path became one of the most recognizable URL patterns of the 1990s web.
CGI was slow because the web server started a brand-new operating system process for every single HTTP request. This meant loading the interpreter (e.g., Perl), parsing the script, executing it, and then destroying the process — all repeated for every page view. A single Perl CGI process typically consumed 5–15 MB of memory and 50–200 milliseconds of startup time. Under heavy traffic (hundreds of requests per second), this process-per-request model exhausted server memory and CPU. Modern alternatives solve this by keeping the application loaded in memory between requests.
The most widely used CGI script ever written — a form-to-email gateway in Perl.
Hit counter with customizable digit images — a CGI staple of every 90s homepage.
Visitor sign-in system that defined personal websites in the late 1990s.
One of the earliest web-based discussion forums, powered by Perl CGI.
The complete timeline of worldwidemart.com and the scripts that shaped the web.
The original domain of Matt's Script Archive and its role in web history.