CGI Mode

View as MarkdownOpen in Claude

CGI overview

CGI (Common Gateway Interface) allows web servers to execute scripts and return their output as HTTP responses.

CGI mode is primarily supported by the Python and Perl SDKs. Compiled languages (Go, C++, Java) are better served by their built-in HTTP servers. TypeScript and Ruby can use CGI via their respective interpreters but this is uncommon.

LanguageCGI SupportNotes
PythonFullAutomatic detection via GATEWAY_INTERFACE

| TypeScript | Possible | Via node as CGI; prefer server mode instead |

Benefits:

  • Works with shared hosting
  • Simple deployment - just upload files
  • No separate process management
  • Compatible with Apache, nginx

Drawbacks:

  • New process per request (slower)
  • No persistent connections
  • Limited scalability

CGI detection

The SDK detects CGI mode via the GATEWAY_INTERFACE environment variable:

1## Automatic detection
2if os.getenv('GATEWAY_INTERFACE'):
3 # CGI mode detected
4 mode = 'cgi'

Basic CGI script

1## agent.py - Basic CGI agent script (add #!/usr/bin/env python3 shebang)
2from signalwire import AgentBase
3
4class MyAgent(AgentBase):
5 def __init__(self):
6 super().__init__(name="my-agent")
7 self.add_language("English", "en-US", "rime.spore")
8 self.prompt_add_section("Role", "You are a helpful assistant.")
9
10if __name__ == "__main__":
11 agent = MyAgent()
12 agent.run() # Automatically detects CGI mode

Make scripts executable:

$chmod +x agent.py agent.pl

CGI request flow

CGI request flow diagram showing web server, CGI script execution, and SignalWire Cloud.
CGI Request Flow

Apache configuration

Enable CGI

1## Enable CGI module
2LoadModule cgi_module modules/mod_cgi.so
3
4## Configure CGI directory
5<Directory "/var/www/cgi-bin">
6 Options +ExecCGI
7 AddHandler cgi-script .py
8 Require all granted
9</Directory>

Virtual host configuration

1<VirtualHost *:443>
2 ServerName agent.example.com
3
4 SSLEngine on
5 SSLCertificateFile /etc/ssl/certs/agent.crt
6 SSLCertificateKeyFile /etc/ssl/private/agent.key
7
8 ScriptAlias / /var/www/cgi-bin/agent.py
9
10 <Directory "/var/www/cgi-bin">
11 Options +ExecCGI
12 SetHandler cgi-script
13 Require all granted
14 </Directory>
15
16 # Set environment variables
17 SetEnv SWML_BASIC_AUTH_USER "myuser"
18 SetEnv SWML_BASIC_AUTH_PASSWORD "mypassword"
19</VirtualHost>

nginx configuration

nginx doesn’t natively support CGI, but you can use FastCGI with fcgiwrap:

1server {
2 listen 443 ssl;
3 server_name agent.example.com;
4
5 ssl_certificate /etc/ssl/certs/agent.crt;
6 ssl_certificate_key /etc/ssl/private/agent.key;
7
8 location / {
9 fastcgi_pass unix:/var/run/fcgiwrap.socket;
10 fastcgi_param SCRIPT_FILENAME /var/www/cgi-bin/agent.py;
11 fastcgi_param GATEWAY_INTERFACE CGI/1.1;
12 fastcgi_param PATH_INFO $uri;
13 fastcgi_param SWML_BASIC_AUTH_USER "myuser";
14 fastcgi_param SWML_BASIC_AUTH_PASSWORD "mypassword";
15 include fastcgi_params;
16 }
17}

CGI host configuration

In CGI mode, the SDK needs to know the external hostname for generating URLs:

$## Using swaig-test to simulate CGI mode
$swaig-test my_agent.py --simulate-serverless cgi --cgi-host agent.example.com

Or set environment variable:

1SetEnv SWML_PROXY_URL_BASE "https://agent.example.com"

Testing CGI locally

Use swaig-test to simulate CGI environment:

$## Test SWML generation in CGI mode
$swaig-test my_agent.py --simulate-serverless cgi --dump-swml
$
$## With custom host
$swaig-test my_agent.py --simulate-serverless cgi --cgi-host mysite.com --dump-swml
$
$## Test a function
$swaig-test my_agent.py --simulate-serverless cgi --exec function_name --param value

Authentication in CGI mode

The SDK checks basic auth in CGI mode:

1## Authentication is automatic when these are set
2## SWML_BASIC_AUTH_USER
3## SWML_BASIC_AUTH_PASSWORD
4
5## The SDK reads Authorization header and validates

If authentication fails, returns 401 with WWW-Authenticate header.

Directory structure

/var/www/cgi-bin/
├── agent.py # Main CGI script
├── requirements.txt # Dependencies
└── venv/ # Virtual environment (optional)

Shared hosting deployment

For shared hosting where you can’t install system packages:

1## agent_shared.py - CGI agent for shared hosting (add #!/usr/bin/env python3 shebang)
2import sys
3import os
4
5## Add local packages directory
6sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'packages'))
7
8from signalwire import AgentBase
9
10class MyAgent(AgentBase):
11 def __init__(self):
12 super().__init__(name="my-agent")
13 self.add_language("English", "en-US", "rime.spore")
14
15if __name__ == "__main__":
16 agent = MyAgent()
17 agent.run()

Install packages locally:

$pip install --target=./packages signalwire

CGI best practices

Performance

  • Keep imports minimal - each request starts fresh
  • Consider FastCGI for better performance
  • Cache what you can (but remember process dies)

Security

  • Set proper file permissions (750 or 755)
  • Don’t expose .py files directly if possible
  • Use HTTPS always
  • Set auth credentials as environment variables

Debugging

  • Check web server error logs
  • Verify shebang line (#!/usr/bin/env python3)
  • Test script from command line first
  • Ensure proper line endings (LF, not CRLF)

Common CGI issues

IssueSolution
500 Internal Server ErrorCheck error logs, verify permissions
Permission deniedchmod +x agent.py
Module not foundCheck sys.path, install dependencies
Wrong Python versionUpdate shebang to correct Python
Malformed headersEnsure proper Content-Type output
TimeoutOptimize code, increase server timeout

Migration from CGI

CGI to FastCGI

Keep same code, use fcgiwrap or gunicorn. Better performance, persistent processes.

CGI to server mode

Same code works - just run differently (python agent.py instead of CGI). Add systemd service, nginx reverse proxy.

CGI to serverless

Same code works with minor changes. Add Lambda handler wrapper. Deploy to AWS/GCP/Azure.