Skip to content

Instantly share code, notes, and snippets.

@stokito
Last active June 29, 2025 14:58
CGI shell scripts samples

CGI samples

CGI Variables

Standard set of Common Gateway Interface environment variable are described in RFC3875. For example:

CONTENT_TYPE=application/x-www-form-urlencoded
GATEWAY_INTERFACE=CGI/1.1
REMOTE_ADDR=192.168.1.180
QUERY_STRING=Zbr=1234567&SrceMB=&ime=jhkjhlkh+klhlkjhlk+%A9%D0%C6%AE%C6%AE&prezime=&sektor=OP
REMOTE_PORT=2292
CONTENT_LENGTH=128
REQUEST_URI=/cgi-bin/printenvs
SERVER_SOFTWARE=busybox httpd/1.35 6-Oct-2004
PATH=/bin:/sbin:/usr/bin:/usr/sbin
HTTP_REFERER=http://192.168.1.1/index1.html
SERVER_PROTOCOL=HTTP/1.0
PATH_INFO=
REQUEST_METHOD=POST
PWD=/www/cgi-bin
SERVER_PORT=80
SCRIPT_NAME=/cgi-bin/printenvs
REMOTE_USER=[http basic auth username]

Example of CGI script that prints them /cgi-bin/printenvs.cgi Environment variables are set up and the script is invoked with pipes for stdin/stdout.

Other samples

httpd expects it's CGI script files to be in the subdirectory cgi-bin under main web directory set by options -h (default is /www, so /www/cgi-bin). The CGI script files must also have permission to be executed (min mode 700) e.g chmod +x /cgi-bin/index.cgi. If directory URL is given, no index.html is found and CGI support is enabled, then cgi-bin/index.cgi will be executed.

BusyBox sources contains two useful CGI programs:

Also there is and example of shell script to process File Upload httpd_post_upload.cgi

#!/bin/sh
# Echo CGI envs as headers and body
CONTENT=$(cat -)
printf "Content-Length: ${#CONTENT}\r\n"
printf "Content-Type: text/html\r\n"
printf "ENV_REQUEST_METHOD: \"$REQUEST_METHOD\"\r\n"
printf "ENV_CONTENT_TYPE: \"$CONTENT_TYPE\"\r\n"
printf "ENV_CONTENT_LENGTH: \"$CONTENT_LENGTH\"\r\n"
printf "ENV_REMOTE_ADDR: \"$REMOTE_ADDR\"\r\n"
printf "ENV_REMOTE_PORT: \"$REMOTE_PORT\"\r\n"
printf "ENV_SERVER_PORT: \"$SERVER_PORT\"\r\n"
printf "ENV_REQUEST_URI: \"$REQUEST_URI\"\r\n"
printf "ENV_QUERY_STRING: \"$QUERY_STRING\"\r\n"
# all headers from request now available with the HTTP_ prefix
printf "ENV_HTTP_HOST: \"$HTTP_HOST\"\r\n"
printf "ENV_HTTP_USER_AGENT: \"$HTTP_USER_AGENT\"\r\n"
printf "ENV_HTTP_ACCEPT: \"$HTTP_ACCEPT\"\r\n"
printf "ENV_HTTP_REFERER: \"$HTTP_REFERER\"\r\n"
# username from basic auth (e.g. without password)
printf "ENV_REMOTE_USER: \"$REMOTE_USER\"\r\n"
printf "ENV_SCRIPT_NAME: \"$SCRIPT_NAME\"\r\n"
printf "ENV_PATH_INFO: \"$PATH_INFO\"\r\n"
printf "ENV_PATH: \"$PATH\"\r\n"
printf "ENV_PWD: \"$PWD\"\r\n"
printf "ENV_SERVER_PROTOCOL: \"$SERVER_PROTOCOL\"\r\n"
printf "ENV_SERVER_SOFTWARE: \"$SERVER_SOFTWARE\"\r\n"
printf "\r\n"
printf "$CONTENT"
#!/usr/bin/perl
use strict;
use warnings;
print "Content-type: text/html\r\n";
print "\r\n";
my $name = '';
if ($ENV{QUERY_STRING}) {
($name) = $ENV{QUERY_STRING} =~ /^name=(.*)$/;
}
print "Hello $name\n";
#!/bin/sh
# Imitation of webdav to read and edit a file
if [ "$REMOTE_USER" != "admin" ]; then
printf "Status: 403\r\n"
printf "\r\n"
printf "Only admin can change users but you are %s" "$REMOTE_USER"
exit
fi
if [ "$REQUEST_METHOD" = "GET" ]; then
printf "Content-Type: text/plain\r\n"
printf "\r\n"
cat /etc/hosts
elif [ "$REQUEST_METHOD" = "PUT" ]; then
CONTENT=$(cat -)
printf "%s" "$CONTENT" > /etc/hosts
printf "Status: 204\r\n"
printf "\r\n"
else
printf "Status: 405\r\n"
fi
#!/usr/bin/python
print("Content-type:text/html\r\n")
print("\r\n")
print('<html>')
print('<head>')
print('<title>Hello from CGI in Python</title>')
print('</head>')
print('<body>')
print('<h1>Hello World!</h2>')
print('</body>')
print('</html>')
#!/bin/sh
# GPLv2. From BuxyBox https://git.busybox.net/busybox/tree/networking/httpd_post_upload.cgi
# post_upload.htm example:
# <html>
# <body>
# <form action=/cgi-bin/httpd_post_upload.cgi method=post enctype=multipart/form-data>
# File to upload: <input type=file name=file1> <input type=submit>
# </form>
# POST upload format:
# -----------------------------29995809218093749221856446032^M
# Content-Disposition: form-data; name="file1"; filename="..."^M
# Content-Type: application/octet-stream^M
# ^M <--------- headers end with empty line
# file contents
# file contents
# file contents
# ^M <--------- extra empty line
# -----------------------------29995809218093749221856446032--^M
file=$(mktemp)
CR=`printf '\r'`
# CGI output must start with at least empty line (or headers)
printf '\r\n'
IFS="$CR"
read -r delim_line
IFS=""
while read -r line; do
test x"$line" = x"" && break
test x"$line" = x"$CR" && break
done
cat >"$file"
# We need to delete the tail of "\r\ndelim_line--\r\n"
tail_len=$((${#delim_line} + 6))
# Get and check file size
filesize=`stat -c"%s" "$file"`
test "$filesize" -lt "$tail_len" && exit 1
# Check that tail is correct
dd if="$file" skip=$((filesize - tail_len)) bs=1 count=1000 >"$file.tail" 2>/dev/null
printf "\r\n%s--\r\n" "$delim_line" >"$file.tail.expected"
if ! diff -q "$file.tail" "$file.tail.expected" >/dev/null; then
printf "<html>\n<body>\nMalformed file upload"
exit 1
fi
rm "$file.tail"
rm "$file.tail.expected"
# Truncate the file
dd of="$file" seek=$((filesize - tail_len)) bs=1 count=0 >/dev/null 2>/dev/null
printf "<html>\n<body>\nFile upload has been accepted"
#!/bin/bash
# list files in a directory. Please not that it needs for Bash
printf "Content-Type: text/html\r\n"
printf "\r\n"
echo "<p>List of files in </strong><code>/</code></p>"
files=($(ls /))
for file in "${files[@]}"
do
echo "<code>$file</code><br>"
done
#!/bin/sh
# print all env variables
printf "Content-Type: text/plain\r\n"
printf "\r\n"
echo "Environment variables:"
env
#!/bin/sh
# Redirect from HTTP to HTTPS
printf "Status: 302 Redirect\r\n"
printf "Location: https://$SERVER_NAME:$SERVER_PORT/\r\n"
printf "\r\n"
#!/bin/sh
printf "Content-Type: text/html\r\n"
printf "\r\n"
cat <<EOF
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CGI render HTML sample</title>
</head>
<body>
<h1>Hello from CGI</h1>
</body>
</html>
EOF
#!/bin/sh
# Slowly read and write back the request
printf "Content-Type: text/plaint\r\n"
printf "\r\n"
set | while read line; do echo $line; sleep 1; done
@nebuxadnezzar
Copy link

some improvement to printenv.cgi

#!/bin/sh
# print all env variables
printf "Content-Type: text/html\r\n"
printf "\r\n"
echo "Environment variables:"
echo "<pre>"
env | sort
echo "</pre>"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment