Setting up Nginx to use Bash as a server-side scripting language

Why? For the same reason people thought it'd be a good idea to write a server in JavaScript: People are dumb. I also happen to be a person.

The tl;dr is that you just use FastCGI. Despite the name, people don't use CGI these days because it's pretty slow, but my site is a low-traffic, hacky, home-brew pile of garbage, so ¯\_(ツ)_/¯.


I'm creating this abomination on Debian 10 with Nginx already installed. So you can probably follow these instructions on other Debian-based distros, like Ubuntu, Linux Mint, gNewSense, or whatever else you're into.


Install FastCGI:

sudo apt install fcgiwrap


Add this location block to your server block:

# pass Bash scripts to FastCGI server
location ~ \.sh$ {
        gzip off;

        fastcgi_pass  unix:/var/run/fcgiwrap.socket;
        include /etc/nginx/fastcgi_params;

        # regex to split $uri to $fastcgi_script_name and $fastcgi_path
        fastcgi_split_path_info ^(.+?\.sh)(/.*)$;

        # Check that the Bash script exists before passing it
        try_files $fastcgi_script_name =404;

        # Bypass the fact that try_files resets $fastcgi_path_info
        # see:
        set $path_info $fastcgi_path_info;
        fastcgi_param PATH_INFO $path_info;

        include fastcgi.conf;

Also, add to your index directive. Mine ended up looking like this:

# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;

I made all these changes in /etc/nginx/sites-available/default because I'm a lazy admin. But if you were doing it properly, you'd probably want to make a file like /etc/nginx/sites-available/ and link to it from /etc/nginx/sites-enabled/. Anyway, after making those changes, you need to restart Nginx so that the changes can take effect:

sudo systemctl restart nginx.service

Also, people often put their CGI scripts in one directory, like /var/www/html/cgi-bin or something. I assume it helps security by making sure nothing outside that directory can be executed. But I don't care about that. YOLO.

Testing to see if it works

Make a bash file called in your server's root (or some descendant directory) containing the following contents:

#!/usr/bin/env bash

echo 'Content-Type: text/plain'
echo ''
echo 'Hello, world!'

Now when you access that directory in your web browser, you should see "Hello, world!"