What is CGI? The Common Gateway Interface explained

Updated November 2026

CGI, the Common Gateway Interface, is the original way for a web server to run a program and send its output to the browser. It made the first dynamic websites possible in 1993. Few new sites use it today, but it is simple, it still works, and understanding it explains how every later web technology works.

How CGI works

  1. The browser requests a URL such as /cgi-bin/hello.cgi?name=Ann.
  2. The web server sees that the file is a CGI program and starts it as a new process.
  3. It passes the request details in environment variables (REQUEST_METHOD, QUERY_STRING, REMOTE_ADDR …) and the body of a POST request on standard input.
  4. The program writes HTTP headers, an empty line and the page to standard output.
  5. The server sends that output to the browser, and the process ends.

Any language that can read environment variables and print text can be used: shell, C, Perl, Python, even awk. CGI is defined in RFC 3875.

A CGI program in shell

#!/bin/sh
# /usr/lib/cgi-bin/env.cgi - show what the server passes to a CGI program
printf 'Content-Type: text/plain\r\n\r\n'
echo "Method: $REQUEST_METHOD"
echo "Query:  $QUERY_STRING"
echo "From:   $REMOTE_ADDR"

The first line printed is a header; the blank line (\r\n\r\n) ends the headers. Make the file executable with chmod 755.

The same in Perl and Python

#!/usr/bin/perl
use strict;
use warnings;
use CGI qw(:standard escapeHTML);    # apt install libcgi-pm-perl

my $name = param('name') // 'world';
print header(-charset => 'utf-8');
print "<p>Hello, ", escapeHTML($name), "!</p>\n";
#!/usr/bin/env python3
import html, os, urllib.parse

q = urllib.parse.parse_qs(os.environ.get("QUERY_STRING", ""))
name = q.get("name", ["world"])[0]
print("Content-Type: text/html; charset=utf-8\n")
print(f"<p>Hello, {html.escape(name)}!</p>")

Note that both escape the user's input before printing it. Without that, anyone could put HTML and JavaScript into your page (cross-site scripting). Python 3.13 removed the old cgi module, so parse the query string with urllib.parse as shown.

Enable CGI in Apache

sudo a2enmod cgid
sudo systemctl restart apache2
# programs in /usr/lib/cgi-bin/ are now served at /cgi-bin/
sudo cp env.cgi /usr/lib/cgi-bin/ && sudo chmod 755 /usr/lib/cgi-bin/env.cgi
curl 'http://localhost/cgi-bin/env.cgi?test=1'

On RHEL the directory is /var/www/cgi-bin and CGI is enabled by default for it.

CGI with nginx

nginx cannot start programs itself. Use fcgiwrap, a small FastCGI server that runs CGI programs on nginx's behalf:

sudo apt install fcgiwrap
# in the nginx server block:
location /cgi-bin/ {
    gzip off;
    root /usr/lib;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/fcgiwrap.socket;
}

Security

A CGI program runs with the web server's rights and processes input from anyone on the internet. The classic mistakes are still the classic attacks:

What replaced CGI

Starting a new process for every request is slow. Later technologies keep the program running between requests:

CGI remains handy for small internal tools, router web interfaces and quick experiments, where its simplicity matters more than speed.