How to use CGI scripts with NGINX using Fastcgi

A tutorial on setting up Fastcgi and NGINX

by spitemim, 2021-08-04


After hours of deliberating over NGINX error logs and unhelpful online tutorials, I have finally figured out how to use CGI scripts with Fastcgi and NGINX. In case anyone else is having the same problem I was, here’s the solution.

After following these instructions, you will have Fastcgi and NGINX set up together, and will be able to execute any executable script as a CGI script when placed somewhere within your document root.

Note: these instructions were written around August of 2021, and confirmed working as of July 2022. If you’re reading these months or years into the future, the instructions may be outdated. Read up on the current state of FastCGI online.

Important Notes

To put it simply, when the user requests a .cgi file on your webserver, FastCGI executes this file and pipes its stdout to the user. Typically, we will write these scripts to write HTML to stdout. This makes it easy to write dynamic parts of an otherwise simple/static webpage without leveraging a big language like PHP. CGI was the big way to write dynamic web applications in the ‘90s, and honestly I still like it today.

What we will be doing is setting up NGINX and FastCGI so that, when accessed by the user, .cgi files will be executed and their outputs sent to the user’s browser.

Instructions

For simplicity, we will be using a shell script as an example, but you can use any interpreted language as long as it has the correct shebang.

You can even run C programs as CGI scripts if your compiler supports running C programs. See TCC.

These instructions are meant for Debian/Ubuntu systems, and they assume some familiarity with NGINX and the command line.

  1. Make sure you have NGINX and fcgiwrap installed.

    sudo apt-get install fcgiwrap nginx
    
  2. Enable NGINX and fcgiwrap.

    sudo systemctl enable --now nginx fcgiwrap fcgiwrap.socket
    
  3. If the file /etc/nginx/fastcgi.conf doesn’t exist, create it with these contents:

    #fastcgi.conf
    fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
    fastcgi_param  SERVER_SOFTWARE    nginx;
    fastcgi_param  QUERY_STRING       $query_string;
    fastcgi_param  REQUEST_METHOD     $request_method;
    fastcgi_param  CONTENT_TYPE       $content_type;
    fastcgi_param  CONTENT_LENGTH     $content_length;
    fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
    fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
    fastcgi_param  REQUEST_URI        $request_uri;
    fastcgi_param  DOCUMENT_URI       $document_uri;
    fastcgi_param  DOCUMENT_ROOT      $document_root;
    fastcgi_param  SERVER_PROTOCOL    $server_protocol;
    fastcgi_param  REMOTE_ADDR        $remote_addr;
    fastcgi_param  REMOTE_PORT        $remote_port;
    fastcgi_param  SERVER_ADDR        $server_addr;
    fastcgi_param  SERVER_PORT        $server_port;
    fastcgi_param  SERVER_NAME        $server_name;
    
  4. Add the following to the server block of the NGINX site file you want to enable CGI scripts for. This file is located in /etc/nginx/sites-available and, if it is enabled, should have a symlink in /etc/nginx/sites-enabled.

    location ~ (\.cgi)$ {
        gzip off;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi.conf ;
    }
    
  5. Create a test CGI script. This example script is writtten in sh and should display memory usage details. Create a file called test.cgi somewhere in your document root (e.g:/var/www/html/test.cgi) and add these contents:

    #!/bin/sh
    
    
    echo "Content-type: text/html"
    echo ""
    cat << EOF
    <html>
    <head>
        <meta http=equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Example CGI script</title>
    </head>
    <body>
        <h1>CGI scripts work!!!</h1>
        <pre>
    EOF
    
    
    free
    
    
    cat << EOF
    </pre>
    </body>
    </html>
    EOF
    exit 0
    
  6. Make sure the script is executable and readable by the webserver.

    sudo chmod a+rx test.cgi
    
  7. Test that the script worked by trying to access it from your browser. If you saved it at /var/www/html/test.cgi and nginx is running on https://example.com, you should go to https://example.com/test.cgi in your browser. If everything went smoothly, you should see something similar to this:
    Screenshot of the CGI script’s output in the browser

If the above steps didn’t work, this page is probably outdated or maybe I’m retarded. Try a more up-to-date guide or ask your local guru.