What is CGI? The Common Gateway Interface explained
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
- The browser requests a URL such as
/cgi-bin/hello.cgi?name=Ann. - The web server sees that the file is a CGI program and starts it as a new process.
- It passes the request details in environment variables (
REQUEST_METHOD,QUERY_STRING,REMOTE_ADDR…) and the body of a POST request on standard input. - The program writes HTTP headers, an empty line and the page to standard output.
- 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:
- Never pass input to a shell.
system("grep $name file")lets a visitor run any command by sendingname=; rm -rf /. Use list forms that do not involve a shell, and validate input against what you expect. - Escape output to prevent cross-site scripting, as above.
- Keep CGI programs out of the document root, so their source cannot be downloaded, and make them non-writable by the web server user.
- In 2014 the "Shellshock" bug in bash let attackers run commands through CGI environment variables on any server where a CGI program used bash. Keep the system patched, and prefer not to write CGI programs in shell for public sites.
What replaced CGI
Starting a new process for every request is slow. Later technologies keep the program running between requests:
- FastCGI: the program stays running and the server talks to it over a socket. PHP-FPM is a FastCGI server.
- Embedded interpreters such as the old
mod_perlandmod_php. - Application servers: WSGI and ASGI for Python (Gunicorn, Uvicorn), PSGI for Perl (Plack), Node.js, Java servlet containers. The web server forwards requests to them as a reverse proxy.
CGI remains handy for small internal tools, router web interfaces and quick experiments, where its simplicity matters more than speed.