I'm trying to run this example CGI program that is from this Wikipedia page https://en.wikipedia.org/wiki/Common_Gateway_Interface

The HTML code is as follows, the file is named "add.html"

<!DOCTYPE html>
  <form action="add.cgi" method="POST">
     <legend>Enter two numbers to add</legend>
     <label>First Number: <input type="number" name="num1"></label><br/>
     <label>Second Number: <input type="number" name="num2"></label><br/>

while the CGI code is as follows, the file is called "add.cgi"

#!/usr/bin/env python3

import cgi, cgitb

input_data = cgi.FieldStorage()

print('Content-Type: text/html') # HTML is following
print('')                         # Leave a blank line
print('<h1>Addition Results</h1>')
    num1 = int(input_data["num1"].value)
    num2 = int(input_data["num2"].value)
    print('<output>Sorry, the script cannot turn your inputs into numbers (integers).</output>')
    raise SystemExit(1)
print('<output>{0} + {1} = {2}</output>'.format(num1, num2, num1 + num2))

My apache config files are located at

User@User-VirtualBox:/etc/apache2/sites-available$ sudo vim 000-default.conf

and the code there is as follows

<VirtualHost *:80>
        # The ServerName directive sets the request scheme, hostname and port that
        # the server uses to identify itself. This is used when creating
        # redirection URLs. In the context of virtual hosts, the ServerName
        # specifies what hostname must appear in the request's Host: header to
        # match this virtual host. For the default virtual host (this file) this
        # value is not decisive as it is used as a last resort host regardless.
        # However, you must set it for any further virtual host explicitly.
        #ServerName www.example.com

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
        <Directory /var/www/>
                Options ExecCGI Indexes FollowSymLinks MultiViews
                AllowOverride None
                Order allow,deny
                allow from all
                AddHandler cgi-script .cgi
        # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
        # error, crit, alert, emerg.
        # It is also possible to configure the loglevel for particular
        # modules, e.g.
        #LogLevel info ssl:warn

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        # For most configuration files from conf-available/, which are
        # enabled or disabled at a global level, it is possible to
        # include a line for only one particular virtual host. For example the
        # following line enables the CGI configuration for this host only
        # after it has been globally disabled with "a2disconf".
        #Include conf-available/serve-cgi-bin.conf 


# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

And the Apache config files are located here

User@User-VirtualBox:/etc/apache2$ sudo vim apache2.conf

The code in the apache2.conf is as follows

# This is the main Apache server configuration file.  It contains the
# configuration directives that give the server its instructions.
# See http://httpd.apache.org/docs/2.4/ for detailed information about
# the directives and /usr/share/doc/apache2/README.Debian about Debian specific
# hints.
# Summary of how the Apache 2 configuration works in Debian:
# The Apache 2 web server configuration in Debian is quite different to
# upstream's suggested way to configure the web server. This is because Debian's
# default Apache2 installation attempts to make adding and removing modules,
# virtual hosts, and extra configuration directives as flexible as possible, in
# order to make automating the changes and administering the server as easy as
# possible.

# It is split into several files forming the configuration hierarchy outlined
# below, all located in the /etc/apache2/ directory:
#       /etc/apache2/
#       |-- apache2.conf
#       |       `--  ports.conf
#       |-- mods-enabled
#       |       |-- *.load
#       |       `-- *.conf
#       |-- conf-enabled
#       |       `-- *.conf
#       `-- sites-enabled
#               `-- *.conf
# * apache2.conf is the main configuration file (this file). It puts the pieces
#   together by including all remaining configuration files when starting up the
#   web server.
# * ports.conf is always included from the main configuration file. It is
#   supposed to determine listening ports for incoming connections which can be
#   customized anytime.
# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/
#   directories contain particular configuration snippets which manage modules,
#   global configuration fragments, or virtual host configurations,
#   respectively.
#   They are activated by symlinking available configuration files from their
#   respective *-available/ counterparts. These should be managed by using our
#   helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See
#   their respective man pages for detailed information.
# * The binary is called apache2. Due to the use of environment variables, in
#   the default configuration, apache2 needs to be started/stopped with
#   /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
#   work with the default configuration.

# Global configuration

# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
# NOTE!  If you intend to place this on an NFS (or otherwise network)
# mounted filesystem then please read the Mutex documentation (available
# at <URL:http://httpd.apache.org/docs/2.4/mod/core.html#mutex>);
# you will save yourself a lot of trouble.
# Do NOT add a slash at the end of the directory path.
#ServerRoot "/etc/apache2"

# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
#Mutex file:${APACHE_LOCK_DIR} default

# The directory where shm and other runtime files will be stored.

DefaultRuntimeDir ${APACHE_RUN_DIR}

# PidFile: The file in which the server should record its process
# identification number when it starts.
# This needs to be set in /etc/apache2/envvars

# Timeout: The number of seconds before receives and sends time out.
Timeout 300

# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to "Off" to deactivate.
KeepAlive On

# MaxKeepAliveRequests: The maximum number of requests to allow
# during a persistent connection. Set to 0 to allow an unlimited amount.
# We recommend you leave this number high, for maximum performance.
MaxKeepAliveRequests 100

# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
KeepAliveTimeout 5

# These need to be set in /etc/apache2/envvars

# HostnameLookups: Log the names of clients or just their IP addresses
# e.g., www.apache.org (on) or (off).
# The default is off because it'd be overall better for the net if people
# had to knowingly turn this feature on, since enabling it means that
# each client request will result in AT LEAST one lookup request to the
# nameserver.
HostnameLookups Off

# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here.  If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
ErrorLog ${APACHE_LOG_DIR}/error.log

# LogLevel: Control the severity of messages logged to the error_log.
# Available values: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the log level for particular modules, e.g.
# "LogLevel info ssl:warn"
LogLevel warn

# Include module configuration:
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf

# Include list of ports to listen on
Include ports.conf

# Sets the default security model of the Apache2 HTTPD server. It does
# not allow access to the root filesystem outside of /usr/share and /var/www.
# The former is used by web applications packaged in Debian,
# the latter may be used for local directories served by the web server. If
# your system is serving content from a sub-directory in /srv you must allow
# access here, or in any related virtual host.
<Directory />
        Options FollowSymLinks
        AllowOverride None
        Require all denied

<Directory /usr/share>
        AllowOverride None
        Require all granted

<Directory /var/www/>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted

#<Directory /srv/>
#       Options Indexes FollowSymLinks
#       AllowOverride None
#       Require all granted

# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives.  See also the AllowOverride
# directive.
AccessFileName .htaccess

# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
<FilesMatch "^\.ht">
        Require all denied

# The following directives define some format nicknames for use with
# a CustomLog directive.
# These deviate from the Common Log Format definitions in that they use %O
# (the actual bytes sent including headers) instead of %b (the size of the
# requested file), because the latter makes it impossible to detect partial
# requests.
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
# Use mod_remoteip instead.
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

# Include of directories ignores editors' and dpkg's backup files,
# see README.Debian for details.

# Include generic snippets of statements
IncludeOptional conf-enabled/*.conf

# Include the virtual host configurations:
IncludeOptional sites-enabled/*.conf

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

#########     Adding capaility to run CGI-scripts #################
ServerName localhost
ScriptAlias /cgi-bin/ /var/www/cgi-bin/
Options +ExecCGI
AddHandler cgi-script .cgi .pl .py

While I also have the add.html and add.cgi both in the following directory

User@User-VirtualBox:/usr/lib/cgi-bin$ sudo vim add.cgi
User@User-VirtualBox:/usr/lib/cgi-bin$ sudo vim add.html

I've attempted to make them executable by doing the following commands

User@User-VirtualBox:/usr/lib/cgi-bin$ sudo chmod a+x add.cgi
User@User-VirtualBox:/usr/lib/cgi-bin$ sudo chmod a+x add.html

Now, when I try to run the add.html from the following url


I get a "Forbidden" permission, not allowed.

enter image description here

Does anybody have any advice on how to make the script work? Or perhaps could link me to cgi scrip that works, i'm at my wits end and I have no idea what else to try.

I would very much appreciate any input, Thank you.

Update: I've moved the files into the following directory, gave permission to run and reloaded apache using the following commands

User@User-VirtualBox:/$ cd /var/www/html
User@User-VirtualBox: /var/www/html$ sudo vim add.cgi
User@User-VirtualBox: /var/www/html$ sudo vim add.html
User@User-VirtualBox:/var/www/html$ sudo chmod a+x add.cgi
User@User-VirtualBox:/var/www/html$ sudo chmod a+x add.html
User@User-VirtualBox:/var/www/html$ systemctl reload apache2

The add.html file loads

add.html ex

However I still encounter the same problem when pressing the add button

add.cgi ex

The following are the last two error codes that apache gave out

User@User-VirtualBox:/var/www/html$     sudo tail -n 2 /var/log/apache2/error.log
[Mon Jul 26 21:14:05.882030 2021] [core:notice] [pid 770:tid 139880185056320] AH00094: Command line: '/usr/sbin/apache2'
[Mon Jul 26 21:40:13.567334 2021] [authz_core:error] [pid 772:tid 139880105506560] [client] AH01630: client denied by server configuration: /usr/www


The following code shows the MPM and CGI modules used

User@User-VirtualBox:~$ ls /etc/apache2/mods-enabled/mpm* /etc/apache2/mods-enabled/cgi*
/etc/apache2/mods-enabled/cgid.conf  /etc/apache2/mods-enabled/mpm_event.conf
/etc/apache2/mods-enabled/cgid.load  /etc/apache2/mods-enabled/mpm_event.load

2 Answers 2


You seem to have a Debian-style configuration directory tree for Apache. Different distributions will lay out Apache configuration files differently, and so it would be useful to mention the name of the operating system or Linux distribution you're using.

Which MPM and CGI modules are you using? Please edit your question to show the results of ls /etc/apache2/mods-enabled/mpm* /etc/apache2/mods-enabled/cgi*.

The Apache web server documentation has this to say on the mod_cgi module:

When using a multi-threaded MPM under unix, the module mod_cgid should be used in place of this module.

So, if you're using the classic mpm_prefork, then mod_cgi is fine. But if you are using mpm_worker or mpm_event (for performance and/or for HTTP/2 support), you should be using mod_cgid instead.

If you have neither mod_cgi nor mod_cgid enabled (with a2enmod cgi or a2enmod cgid respectively), that might explain why your CGI scripts won't work.

Note: a2enmod and a2dismod commands are specific to Debian and related distributions, and to this specific Apache configuration file layout. They won't be present in other distributions that will use a more plain Apache default configuration.

The message AH01630: client denied by server configuration: /usr/www suggests your /var/www might actually be a symlink to /usr/www. Any <Directory> blocks in Apache configuration must reference the actual path, not a path that goes through a symlink. So if realpath /var/www/html/add.cgi returns /usr/www/html/add.cgi for you, then your <Directory /var/www/> won't be correct and should be replaced with <Directory /usr/www/>.

Source: http://httpd.apache.org/docs/2.4/mod/core.html#directory

Be careful with the directory-path arguments: They have to literally match the filesystem path which Apache httpd uses to access the files. Directives applied to a particular <Directory> will not apply to files accessed from that same directory via a different path, such as via different symbolic links.

  • Thank you, I've updated the question with MPM and CGI modules apache sends back to me, in any case what should be the Configuration directory tree for apache on Ubuntu? I'll try the rest of the suggestions to see if that can help. Thank you Jul 27, 2021 at 6:49
  • Ubuntu is related to Debian, so it uses the same configuration directory layout as Debian does. And as noted by roaima's answer, if you are using file:// URLs to access the files, then the Apache server is not involved at all: the browser is reading the files straight from the local filesystem.
    – telcoM
    Jul 27, 2021 at 7:07
  • I did the following to enable CGI, please let me know if that's all I should do or if I should go into the files and add text User@User-VirtualBox:~$ sudo a2enmod mod_cgid ERROR: Module mod_cgid does not exist! User@User-VirtualBox:~$ sudo a2enmod cgi Your MPM seems to be threaded. Selecting cgid instead of cgi. Module cgid already enabled Jul 27, 2021 at 7:22
  • I also changed <Directory /var/www/> to <Directory /usr/www/> and then did a sudo systemctl reload apache2 and now when I go to http://localhost/add.html as roaima suggested it loads, however when I click "add" the CGI script still says "403 Forbidden You don't have permission to access this resource" Jul 27, 2021 at 7:32
  • Ok, I forgot that the mod_ prefix should not be used with a2enmod/a2dismod. What does the Apache error.log tell you now? Is the message still exactly the same?
    – telcoM
    Jul 27, 2021 at 7:43

Look at the URL you are using


You're loading the HTML form directly from the filesystem, not from the webserver. You've used a relative path to the form action, add.cgi, which will translate to file:///var/www/html/add.cgi. Your web browser has opened this file and listed its contents for you.

Try this instead


The corresponding relative action will then become http://localhost/add.cgi, allowing the webserver to execute the code on your behalf

  • Unfortunately I still get a "403 Forbidden You don't have permission to access this resource." when I use http://localhost/add.html Jul 27, 2021 at 7:02
  • after doing what telcoM suggested of changing <Directory /var/www/> to <Directory /usr/www/> and then doing a sudo systemctl reload apache2 and now when I go to http://localhost/add.html it loads, however when I click "add" the CGI script still says "403 Forbidden You don't have permission to access this resource" Jul 27, 2021 at 7:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .