When I first started managing servers, the idea of “server-side scripting” felt abstract until I actually enabled it and watched a static HTML page turn into something that could talk to a database. In this guide, I’ll explain what server-side scripting means in the context of Apache, and walk through enabling it for the most common languages, along with the configuration details that trip people up.
What Is Server-Side Scripting?
Server-side scripting means running code on the web server before the response is sent to the browser, rather than in the visitor’s browser (which is client-side scripting, like JavaScript running in Chrome). Common server-side languages include PHP, Python, Perl, and Ruby.
Apache doesn’t execute these languages natively. Instead, it relies on modules or protocols to hand off requests to an interpreter:
- mod_php – runs PHP directly inside the Apache process (legacy but still common)
- PHP-FPM via mod_proxy_fcgi – runs PHP as a separate FastCGI process pool (modern, recommended)
- mod_wsgi – runs Python web applications (Django, Flask)
- mod_perl – embeds a Perl interpreter in Apache
- mod_cgi / mod_cgid – runs any executable script as a CGI program, language-agnostic but slower
This guide covers the general mechanism and setup for CGI and FastCGI, since those are the foundation most language-specific guides (PHP, Python, Ruby) build on. For deep dives into specific stacks, see our companion articles on PHP and Python with Apache.
Prerequisites
- Apache 2.4+ with root/sudo access
- A Linux server (Ubuntu/Debian or CentOS/RHEL examples below)
- Basic familiarity with file permissions and the command line
- The scripting language/interpreter you intend to use already installed (e.g.,
php,python3,perl)
Enabling CGI Scripting (The Foundation)
CGI (Common Gateway Interface) is the original method for server-side scripting and still useful for simple scripts or legacy systems.
Step 1: Enable mod_cgi
Debian/Ubuntu (using the threaded mpm_event, use mod_cgid instead):
sudo a2enmod cgid
sudo systemctl restart apache2
CentOS/RHEL:
LoadModule cgid_module modules/mod_cgid.so
Step 2: Designate a CGI Directory
Edit your virtual host configuration:
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/example.com/public_html
ScriptAlias /cgi-bin/ /var/www/example.com/cgi-bin/
<Directory "/var/www/example.com/cgi-bin">
AllowOverride None
Options +ExecCGI
Require all granted
AddHandler cgi-script .cgi .pl .py
</Directory>
</VirtualHost>
Step 3: Write a Test Script
sudo mkdir -p /var/www/example.com/cgi-bin
sudo nano /var/www/example.com/cgi-bin/test.cgi
#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "<html><body><h1>CGI is working!</h1></body></html>";
Make it executable:
sudo chmod +x /var/www/example.com/cgi-bin/test.cgi
Restart Apache and visit http://example.com/cgi-bin/test.cgi to confirm it works.
Enabling FastCGI (Modern, Recommended Approach)
FastCGI keeps interpreter processes running persistently instead of starting a new process per request, which is dramatically faster under load. This is the backbone of modern PHP-FPM and many Python deployments.
Step 1: Enable Proxy and FastCGI Modules
sudo a2enmod proxy
sudo a2enmod proxy_fcgi
sudo a2enmod setenvif
sudo systemctl restart apache2
Step 2: Configure a FastCGI Handler
Example configuration pointing to a FastCGI process listening on a Unix socket (this pattern applies whether the backend is PHP-FPM, a Python WSGI server behind FastCGI, or similar):
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost/"
</FilesMatch>
This exact pattern is explored in depth in our PHP-specific guide, since PHP-FPM is the most common real-world use of FastCGI with Apache.
Real-World Use Cases
- Contact forms and dynamic content – processing form submissions, sending emails, validating input.
- Content management systems – WordPress, Drupal, and Joomla all rely on server-side PHP execution.
- Web applications – Django/Flask (Python), Rails (Ruby), and similar frameworks require server-side execution to render pages and handle business logic.
- APIs and microservices – server-side scripts process requests, query databases, and return JSON responses.
- Legacy CGI scripts – some older systems (bioinformatics tools, internal admin scripts) still rely on classic CGI.
Common Mistakes to Avoid
- Forgetting +ExecCGI or the correct SetHandler – Without explicitly allowing script execution, Apache will serve the raw script source code instead of running it, which is both a functionality bug and a security risk.
- Incorrect file permissions – Scripts need execute permissions (
chmod +x) for CGI; FastCGI sockets need correct ownership matching the Apache/FPM user. - Mixing mod_php and PHP-FPM – Running both simultaneously can cause confusing behavior; pick one approach per site.
- Not restarting after enabling modules – Module changes require a full restart, not just a reload, in some cases.
- Leaving default/test scripts accessible in production – Test CGI scripts left in place can become an attack surface.
Security Best Practices
- Never enable ExecCGI or scripting handlers on directories that accept file uploads. If an attacker can upload a script and it lands in an executable-enabled directory, they can achieve remote code execution.
- Restrict scripting to specific, well-defined directories rather than enabling it site-wide with
Options +ExecCGIin the DocumentRoot. - Keep interpreters updated – PHP, Python, and Perl all receive regular security patches; outdated interpreters are a common attack vector.
- Run scripts with least privilege – use a dedicated, non-privileged system user for the web server and script execution, never root.
- Validate and sanitize all input in your scripts themselves; Apache’s job is just to execute the code, not to secure your application logic.
- Disable directory listing (
Options -Indexes) so visitors can’t browse your cgi-bin or script directories directly.
Performance Optimization Tips
- Prefer FastCGI/FPM over classic CGI wherever possible; process-per-request CGI is significantly slower under concurrent load.
- Tune process pool sizes (e.g., PHP-FPM’s
pm.max_children) to match your server’s CPU and memory resources. - Use opcode/bytecode caching where available (like PHP’s OPcache) to avoid recompiling scripts on every request.
- Combine with caching (see our Apache caching guide) for pages that don’t need to be regenerated on every single request.
Troubleshooting
“Internal Server Error” (500):
- Check Apache’s error log:
sudo tail -f /var/log/apache2/error.log - Common causes: incorrect shebang line in the script, missing execute permission, or a syntax error in the script itself.
Script downloads instead of executing:
- The handler isn’t correctly configured. Double-check
AddHandlerorSetHandlerdirectives and that the relevant module is enabled.
“Premature end of script headers”:
- The script didn’t output a valid
Content-typeheader before any content. Every CGI script must print headers first, followed by a blank line, then content.
FastCGI socket connection errors:
- Verify the backend process (like PHP-FPM) is running:
sudo systemctl status php8.3-fpm - Confirm the socket path in Apache’s config matches the actual socket file location and permissions.
Frequently Asked Questions
What’s the difference between CGI and FastCGI? CGI spawns a new process for every request, which is simple but slow. FastCGI keeps a pool of persistent processes running, dramatically reducing overhead per request.
Do I need mod_cgi if I’m using PHP-FPM? No. PHP-FPM uses mod_proxy_fcgi, not classic CGI. mod_cgi is only needed for traditional CGI scripts (Perl, Python scripts written as standalone CGI programs, shell scripts, etc.).
Is server-side scripting a security risk by itself? Not inherently, but misconfigured scripting handlers (enabling execution in upload directories, running as root, outdated interpreters) are among the most common causes of server compromise.
Can I run multiple languages on the same Apache server? Yes. It’s common to run PHP-FPM for one site and a Python WSGI app (via mod_wsgi or a reverse-proxied app server) for another, all behind the same Apache instance using separate virtual hosts.
Summary and Key Takeaways
- Server-side scripting lets Apache execute code (PHP, Python, Perl, Ruby, etc.) to generate dynamic content before sending a response.
- Classic CGI (mod_cgi/mod_cgid) is simple but slow; FastCGI (mod_proxy_fcgi) is the modern, performant standard.
- Always scope scripting permissions to specific directories, never site-wide, and never in upload directories.
- Keep interpreters patched and run them under a least-privilege user.
- Check Apache’s error log first when scripts fail — most issues (permissions, missing headers, syntax errors) show up there immediately.