A Simple Python CGI Server Tutorial

In this post I’m going to explain how to create a simple Python CGI Server. To illustrate the concepts involved more clearly I will not attempt to make this server extensible, rather I will try to keep the code as simple and clear as possible.

To start with we need to create a directory for our server and CGI scripts to reside in. I simply created a directory named ‘server’ in my home directory, but you may name it anything you wish and place it anywhere in your filesystem.

The next step is to create a simple CGI server. To do this open up your favorite text editor and write the program below. (I’ll go through it line-by-line at the end of this tutorial.)

#!/usr/bin/env python

import BaseHTTPServer
import CGIHTTPServer
import cgitb; cgitb.enable()  ## This line enables CGI error reporting

server = BaseHTTPServer.HTTPServer
handler = CGIHTTPServer.CGIHTTPRequestHandler
server_address = ("", 8000)
handler.cgi_directories = ["/"]

httpd = server(server_address, handler)

Save this file in the ‘server’ directory as ‘server.py’, or whatever name tickles your fancy, and give it executable permissions. On Linux systems this is achieved with the command below…

$ chmod +x server.py

That’s it for the server! Simple wasn’t that? Now that we have a server ready to go we need something for it to serve. For that we will create a simple “Hello World!” CGI script. Open up your text editor again and type in the following program.

#!/usr/bin/env python

print "Content-type: text/html"
print "<title>Test CGI</title>"
print "<p>Hello World!</p>"

Save this file in the ‘server’ directory as ‘test_cgi.py’ and give it executable permissions, just like the server.py file.

$ chmod +x test_cgi.py

Now we have all the components in place. To start your Python CGI server simply open up a terminal and cd into your ‘server’ directory. When you are there simply type the following command.

$ ./server.py

Your server is now fully operational! To see your first page fire up a browser and type the following into the location bar.


You should be greeted by a webpage that proudly proclaims “Hello World!”. If you are not, make sure to check your file permissions (both the server and CGI script should be given executable permissions) and make sure you copied the programs above verbatim. To kill your server simply go back to the terminal and hit ‘ctrl-c’.


Server Code Line-By-Line

For those of you who want a more in-depth analysis of what this code is doing, here is a line-by-line explanation.

The first thing we need to do is make this script executable on the command line. The following ‘she-bang’ line simplifies this process so that we can type: ./server.py instead of the more verbose: python server.py

#!/usr/bin/env python

The next two lines import the necessary libraries. The first, BaseHTTPServer, provides us with a simple web server and CGIHTTPServer provides us with a request handler.

import BaseHTTPServer
import CGIHTTPServer

The following line enables in-browser CGI error reporting. This can be very handy as you develop CGI scripts, however if you don’t want to expose the internals of your program to users you may wish to log the output of this to a file instead.

import cgitb; cgitb.enable()  ## This line enables CGI error reporting

The next two lines create a server and a request handler.

server = BaseHTTPServer.HTTPServer
handler = CGIHTTPServer.CGIHTTPRequestHandler

The next line specifies an address for the server, in this case we want to use ‘localhost’ so simply specifing “” will give us this. This line also specifies a port number to associate with our server. You can choose whatever port number you wish.

server_address = ("", 8000)

The next line specifies where the CGI scripts will reside in relation to the ‘server’ directory. In this example we put our CGI script in the base directory by using “”, however you may perfer to place your scripts in a special ‘cgi’ or ‘cgi-bin’ directory.

handler.cgi_directories = [""]

The next line actually ‘creates’ our server, passing it the server address and port number, as well as the CGI handler. If you’re wondering why we name it httpd it stands for HTTP Daemon.

httpd = server(server_address, handler)

The final line puts our program into an infinite loop so that the server can ‘serve_forever’. Until you kill the server with ‘ctrl-c’ from the terminal, it will continue to listen for requests and serve the appropriate web pages.


GOTO Part 2 | A Simple Python CGI Tutorial – CGI Input and Output



29 thoughts on “A Simple Python CGI Server Tutorial

  1. from what i can see, what happens now if i open a browser with the URL
    http://localhost:8000/server.py … ?
    this is not a good idea the have the server executing cgi script from the root folder
    You should not change this handler.cgi_directories = [“”]

    • What happens when you open http://localhost:8000/server.py ?

      You get this error message:
      [Errno 98] Address already in use

      This server is meant as a very minimal testing server or as a starting point for someone interested in building their own server. I would contend that it is usually not a good idea to write your own server with the large number of capable open-source servers available.

      As for the error above, I’ll reiterate the old doctor’s advice when you tell the doc, “It hurts when I do this.”

      Then don’t do that.

      I agree, for a public server, you would want a separate cgi directory. Which is why I made a note of that in the article. For a private testing server or project the old doctor’s advice works just fine.

  2. Hi,
    I followed your tutorial and when I run my file(.py) from browser, instead of printing hello world, it prints whole file in browser window. where I went wrong. can you please help me.

    • If the server is running correctly, it sounds like you didn’t give executable permissions to your script. In linux this is achieved by the command:

      $ chmod +x test_cgi.py

      Where test_cgi.py should be replaced by the filename of your script.

      • What if we’re on Windows, and .py extensions already have executable permissions? I’m having the same problem. It has executable permissions, the server is handling requests, but all it does is display the contents of the script rather than run it.

        • It appears that the CGIHTTPServer directories default to [‘/cgi-bin’, ‘/htbin’] now. So you will likely have to include any cgi scripts in one of those directories to make it work.

          I have switched from using this script to simply calling the CGIHTTPServer from the command line.

          $ python -m CGIHTTPServer

          Run that command (may be slightly different on windows) in the directory where your files are stored. You must have your cgi scripts in a folder /cgi-bin.

    • I had the same problem on my Mac following this example. I found out, by changing this line and adding a slash, everything works like intended:
      handler.cgi_directories = [“”]
      handler.cgi_directories = [“/”]

  3. Hello Friend,
    I have exactly the same issue as some of the others. I am using Windows and through the file is served up, it is served up as is without the python interpretor interpreting it first.
    (With a lot of searching I came across your example as that was exactly what I was looking for – doing cgi from scratch.)
    Please help as I am trying to learn Web programming in Python and coming up with this issue.

    • Unfortunately I don’t have a Windows box to test this on and I don’t know exactly what is going on here. I suspect it is a permissions problem of some sort, but I’m unfamiliar with how Windows handles such things.

        • No problem, I wish I could help further. About the only thing I can do for you at the moment is point you to the Python mailing list. The people there are very helpful and you will probably get an answer to your question within a few hours.


          EDIT: If you do get an answer there I’d greatly appreciate it if you left a comment here to help out future visitors.

      • This is not an issue with windows. I had the same problem on Ubuntu. Now it is solved as mentioned by some user above.

  4. I notice the CGIHTTPRequestHandler does not implement the do_GET method. So it uses SimpleHTTPRequestHandler.do_GET(), and that one does a shutil.copyfileobj

    (I use python 2.7.2, Activestate windows-build)

    This makes me wonder a bit how the example can work, assuming copyfileobj doesn’t execute.

  5. Please ignore my remark.
    I was not yet understanding the code. do_GET calls send_head and that one is overridden.
    Mileage varies. I have one Win XP SP3 + Python 2.72 where it works and another where it doesn’t. Too bad, no energy to debug

  6. For anyone who’s just seeing the script in plain-text, it worked for me once I changed:

    handler.cgi_directories = [“”]


    handler.cgi_directories = [“/”]

    (I think the “/” is the part of the URL it will look in for the script, not the root “/” directory in the filesystem)

  7. I followed your tutorial to the word and copy pasted the code as well but when i execute localhost:8080/test_cgi.py I am only getting the entire code in the output. I have already given the executable permissions to the test_cgi.py

    Not sure where I am going wrong?

    • I was able to get it working by setting the handler directory right,
      handler.cgi_directories = [“/”]

      • Faced the same problem on Mac OS X 10.5 + python 2.7

        my cgi scripts are in $SERVER_DIR/cgi-bin/
        I’ve commented the line in the code:
        #handler.cgi_directories = [“”]

        as result cgi scripts run properly.

        Thanks for the tutorial!

      • Plus one for:

        handler.cgi_directories = [“/”]

  8. USE:

    NOT: handler.cgi_directories = [‘/’]

    It will work then

  9. Same for me on Mac OS 10.8.4 – if you want the test_cgi.py to work either use handler.cgi_directories.append(“/”) or the less favored handler.cgi_directories = [“/”]. Thanks to those who provided that specific help along with the original author for the python cgi tutorial.

  10. **using Python 3.3**


        #!/usr/bin/env python3
        PORT = 8000
        import http.server
        httpd = http.server.HTTPServer( ("", PORT), http.server.CGIHTTPRequestHandler)
            print ("Server Started at port:", PORT)
        except KeyboardInterrupt:
            print('\nShutting down server')


        #!/usr/bin/env python3
        print ("Content-type: text/html")
        print ()
        print ("Test CGI")
        print ("Hello World!")

Comments are closed.