CGI applications

Ferron supports classic CGI (Common Gateway Interface) applications through the http-cgi module. This is useful for legacy stacks and script-based workflows.

For new deployments, prefer HTTP reverse proxying or FastCGI when possible, because it avoids starting a new process per request and usually performs better.

Basic CGI setup⁠#

To run CGI programs, enable cgi at the HTTP host scope:

example.com {
    root /var/www/html
    cgi
}

Ferron automatically treats scripts inside a cgi-bin directory as CGI programs. The directory must carry the exact name cgi-bin (case-insensitive) and must sit directly under the document root.

Example directory structure:

/var/www/html/
├── index.html
└── cgi-bin/
    ├── handler.php
    └── script.py
Note
  • Ferron always removes the Proxy header to prevent the httpoxy vulnerability.
  • If a CGI script exits with a non-zero status, Ferron logs a WARN message and returns a 500 Internal Server Error response. Ferron logs stderr output from the script as a warning, trimmed before logging.
  • Ferron sets the working directory to the directory containing the script file.

Executing scripts by extension⁠#

You can also execute CGI scripts outside cgi-bin by registering additional file extensions:

example.com {
    root /var/www/html
    cgi {
        extension ".php"
        extension ".py"
        extension ".rb"
    }
}

With this configuration:

  • Ferron treats /var/www/html/scripts/process.php as a CGI script
  • Ferron treats /var/www/html/scripts/convert.py as a CGI script
  • Ferron serves /var/www/html/static/style.css as a static file
Note
  • Ferron matches extensions case-insensitively (.PHP matches .php).
  • Ferron executes files with registered extensions as CGI scripts regardless of their location.
  • This is complementary to cgi-bin directory matching. A file can be CGI either by being in cgi-bin or by having a registered extension.

Custom CGI interpreters⁠#

Define explicit interpreters for specific file extensions:

example.com {
    root /var/www/html
    cgi {
        interpreter ".php" php-cgi -c /etc/php/8.2/cgi/php.ini
        interpreter ".pl" perl
        interpreter ".py" python3
    }
}

Ferron automatically appends the file path as the final argument to the interpreter command. A request to /cgi-bin/handler.php with the above configuration runs:

php-cgi -c /etc/php/8.2/cgi/php.ini /var/www/html/cgi-bin/handler.php

Disabling default interpreters⁠#

Pass false as the second argument to interpreter to disable the default interpreter for that extension:

example.com {
    root /var/www/html
    cgi {
        interpreter ".php" false
    }
}

This allows Ferron to handle PHP files via shebang lines (#!/usr/bin/env php) or direct execution instead of the default php-cgi interpreter.

Built-in default interpreters⁠#

When no custom interpreter directive matches, Ferron uses these built-in defaults:

ExtensionDefault interpreter
.plperl
.pypython
.shbash
.kshksh
.cshcsh
.rbruby
.phpphp-cgi
.exe (Windows)(direct execution)
.bat (Windows)cmd /c
.vbs (Windows)cscript

On Unix systems, Ferron parses scripts with a shebang line (for example #!/usr/bin/env python3) and derives the interpreter from the shebang. On Windows, Ferron executes .exe files directly.

Environment variables⁠#

Set CGI environment variables that Ferron passes to the interpreter process:

example.com {
    root /var/www/html
    cgi {
        environment "APP_ENV" "production"
        environment "APP_SECRET" "{{env.APP_SECRET}}"
        environment "RUBY_VERSION" "3.3"
    }
}
Note
  • Environment variables take precedence over any existing variables with the same name.
  • Values support interpolation (for example {{env.VAR}} for environment variable substitution).
  • Ferron always sets the following CGI environment variables automatically:
VariableDescription
SERVER_SOFTWAREAlways Ferron.
SERVER_NAMEServer hostname.
SERVER_ADDRLocal server address.
SERVER_PORTServer port.
REQUEST_METHODHTTP method.
REQUEST_URIOriginal request URI.
QUERY_STRINGQuery string (empty string if none).
PATH_INFOPath info extracted from the request.
SCRIPT_NAMEThe script path relative to the document root.
AUTH_TYPEAuthentication type from the Authorization header.
REMOTE_USERAuthenticated username, if available.
SERVER_ADMINServer administrator email (from admin_email configuration).
HTTPSFerron sets this to on when the connection uses encryption.

Security considerations⁠#

File upload safety⁠#

Keep upload and download directories outside cgi-bin and outside any extension-registered directories. Otherwise, a user could upload a malicious script and execute it as CGI.

Example safe configuration:

example.com {
    root /var/www/html

    # CGI is only enabled for cgi-bin and .php scripts
    cgi {
        extension ".php"
    }

    # Upload directory is safe (no CGI execution)
    location /uploads {
        root /var/www/html/uploads
        cgi false
    }
}

Interpreter permissions⁠#

On Unix systems, scripts without a matching interpreter directive must have the executable permission bit set (chmod +x). On Windows, Ferron executes .exe files directly and parses scripts with shebangs like Unix.

Distributed tracing⁠#

When you enable tracing in Ferron, CGI scripts automatically receive W3C Trace Context headers (traceparent, tracestate, baggage) as CGI environment variables (HTTP_TRACEPARENT, HTTP_TRACESTATE, HTTP_BAGGAGE). This enables end-to-end distributed tracing without any CGI-side configuration.

Info

See Tracing configuration for details on enabling trace generation and sampling.

Default index files⁠#

When you enable CGI and you do not configure an explicit index directive, Ferron automatically injects default index file names. Ferron checks these files in order: index.html, index.htm, index.xhtml.

If you register additional extensions via the extension directive, Ferron also prepends corresponding index files to the front of the list:

Registered extensionPrepend to index list
.cgiindex.cgi
.phpindex.php

For example, with extension ".php" configured, the injection order becomes: index.php, index.html, index.htm, index.xhtml.

This injection only applies when you do not set an explicit index directive. If you configure your own index directive, Ferron uses that instead.

Examples⁠#

PHP with a custom PHP-CGI binary⁠#

example.com {
    root /srv/www/example
    cgi {
        extension ".php"
        interpreter ".php" php-cgi -c /etc/php/8.2/cgi/php.ini
    }
}

Multiple interpreters with environment variables⁠#

example.com {
    root /srv/www/app
    cgi {
        extension ".rb"
        interpreter ".rb" ruby
        interpreter ".py" python3
        environment "RUBY_VERSION" "3.3"
        environment "PYTHONUNBUFFERED" "1"
    }
}

Disabling the default PHP interpreter⁠#

example.com {
    root /srv/www/example
    cgi {
        interpreter ".php" false
    }
}

This allows Ferron to handle PHP files via shebang lines or direct execution instead.

Using cgi-bin with additional extensions⁠#

example.com {
    root /srv/www/example

    cgi {
        extension ".php"
        environment "APP_ENV" "production"
    }

    # /srv/www/example/cgi-bin/handler.py is treated as CGI
    # /srv/www/example/scripts/script.php is also treated as CGI
    # (because of the ".php" extension directive)
}