Introduction
Common Gateway Interface (CGI) scripts in Apache execute external programs to generate dynamic content. When a client requests a CGI script, Apache spawns a new process to run the script, captures its output, and returns it to the client. This traditional approach to dynamic web content requires careful configuration of file permissions, script location, and Apache modules.
CGI script permission errors occur when Apache cannot execute the script file due to permission restrictions, ownership mismatches, or configuration errors. Apache's suexec mechanism further complicates permissions by enforcing strict ownership and mode requirements for security. Understanding the permission hierarchy—from filesystem permissions through Apache configuration to suexec constraints—is essential for debugging CGI execution failures.
Modern web applications typically use embedded scripting languages (PHP, Python) or application servers rather than CGI, but legacy applications, system administration interfaces, and specialized tools still rely on CGI scripts. These scripts often interface with system commands, process data files, or provide administrative functions that embedded languages cannot easily access.
Symptoms
When Apache CGI script permission errors occur, you will observe these symptoms:
- HTTP 500 Internal Server Error when accessing CGI script URLs
- Apache error logs show "Permission denied" or "exec format error"
- Script execution attempts result in premature termination
- Browser displays generic error page without specific details
- Script file exists and is syntactically correct but won't execute
- Other CGI scripts work but specific scripts fail
- Apache suexec logs show permission violations
Common Apache error log entries:
``` # Permission denied errors [Tue Jan 15 10:30:45] [error] [client 192.168.1.100] (13)Permission denied: exec of '/var/www/cgi-bin/script.cgi' failed
# Exec format error (wrong interpreter) [Tue Jan 15 10:30:45] [error] [client 192.168.1.100] (8)Exec format error: exec of '/var/www/cgi-bin/script.cgi' failed
# suexec permission violation [Tue Jan 15 10:30:45] [error] suexec policy violation: see suexec log for more details suexec.log: directory /var/www/cgi-bin is not owned by user www-data ```
HTTP response indicating CGI failure:
``` HTTP/1.1 500 Internal Server Error Content-Type: text/html
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>500 Internal Server Error</title> </head><body> <h1>Internal Server Error</h1> <p>The server encountered an internal error while attempting to execute the script.</p> </body></html> ```
Common Causes
Several factors cause Apache CGI script permission errors:
- 1.Script lacks execute permission: The script file's mode doesn't include execute bits (
chmod +x). Apache needs execute permission to spawn the script process. - 2.Script directory lacks proper permissions: The cgi-bin directory itself needs appropriate permissions for Apache to access and execute scripts within it.
- 3.Wrong file ownership: If Apache runs under a specific user (www-data, apache), the script file must be accessible to that user. suexec adds stricter requirements.
- 4.Missing shebang line: Scripts without a proper
#!/path/to/interpreterfirst line produce exec format errors because Apache doesn't know which interpreter to use. - 5.Incorrect interpreter path: Shebang references non-existent or incorrect interpreter (e.g.,
#!/usr/bin/perlwhen Perl is at/usr/local/bin/perl). - 6.Script not in configured ScriptAlias directory: Apache only executes CGI scripts from directories configured with ScriptAlias or Options +ExecCGI.
- 7.suexec policy violations: When suexec is enabled, it enforces strict ownership, permission, and directory requirements that regular Apache doesn't.
- 8.SELinux or AppArmor restrictions: Security modules may block script execution even when file permissions appear correct.
- 9.Script syntax errors: Malformed scripts fail during execution, sometimes producing permission-related error messages.
Step-by-Step Fix
Follow these steps to diagnose and resolve Apache CGI permission errors:
Step 1: Check script file permissions
Verify the script has execute permissions:
```bash # Check current permissions ls -la /var/www/cgi-bin/script.cgi
# Expected output (755 or similar): -rwxr-xr-x 1 www-data www-data 1234 Jan 15 script.cgi
# If execute bits missing, add them chmod +x /var/www/cgi-bin/script.cgi chmod 755 /var/www/cgi-bin/script.cgi
# Check directory permissions ls -la /var/www/cgi-bin/
# Directory should allow Apache to enter and read chmod 755 /var/www/cgi-bin/ ```
Step 2: Verify file ownership
Confirm Apache user can access the file:
```bash # Find Apache user ps aux | grep apache | head -1 # Or grep -E "^User|^Group" /etc/apache2/apache2.conf # Or cat /etc/apache2/envvars | grep APACHE_RUN_USER
# Common Apache users: www-data, apache, daemon
# Check current ownership ls -la /var/www/cgi-bin/script.cgi
# Change ownership if needed chown www-data:www-data /var/www/cgi-bin/script.cgi
# For suexec, ownership must match exactly chown user:user /var/www/cgi-bin/script.cgi chown user:user /var/www/cgi-bin/ ```
Step 3: Check shebang line
Verify the script has correct interpreter specification:
```bash # Check first line head -1 /var/www/cgi-bin/script.cgi
# Expected for Perl: #!/usr/bin/perl
# Expected for Python: #!/usr/bin/python3
# Expected for Bash: #!/bin/bash
# Verify interpreter exists which perl which python3 which bash
# If interpreter is elsewhere, update shebang vim /var/www/cgi-bin/script.cgi # Update first line to match actual interpreter location ```
Step 4: Test script execution directly
Run the script outside Apache to verify it works:
```bash # Execute script directly /var/www/cgi-bin/script.cgi
# Or with specific interpreter perl /var/www/cgi-bin/script.cgi python3 /var/www/cgi-bin/script.cgi bash /var/www/cgi-bin/script.cgi
# If script fails directly, fix script content before Apache issues perl -c /var/www/cgi-bin/script.cgi # Perl syntax check python3 -m py_compile /var/www/cgi-bin/script.cgi # Python syntax check bash -n /var/www/cgi-bin/script.cgi # Bash syntax check ```
Step 5: Verify Apache CGI configuration
Check Apache is configured to execute CGI:
```bash # Check ScriptAlias configuration grep -r "ScriptAlias" /etc/apache2/
# Typical configuration: ScriptAlias /cgi-bin/ /var/www/cgi-bin/
# Check directory options grep -A10 "cgi-bin" /etc/apache2/sites-available/default.conf
# Expected: <Directory "/var/www/cgi-bin"> Options +ExecCGI AddHandler cgi-script .cgi .pl .py AllowOverride None Require all granted </Directory>
# If missing, add to virtual host configuration vim /etc/apache2/sites-available/default.conf ```
Step 6: Verify CGI module is loaded
Ensure Apache has CGI module enabled:
```bash # Check loaded modules apache2ctl -M | grep cgi
# Expected modules: cgi_module or cgid_module
# Enable CGI module if missing a2enmod cgi # Or for threaded MPM a2enmod cgid
# Restart Apache systemctl restart apache2
# Verify module loaded apache2ctl -M | grep cgi ```
Step 7: Check suexec configuration (if enabled)
If using suexec, verify strict requirements:
```bash # Check if suexec is enabled apache2ctl -M | grep suexec
# View suexec configuration /usr/lib/apache2/suexec -V # Or apache2-suexec -V
# suexec requirements: # - Document root ownership matches target user # - CGI directory is under document root # - Script owned by target user # - Script has mode 755 (no write for group/others) # - Directory has mode 755
# Check suexec log cat /var/log/apache2/suexec.log
# Typical suexec error: [2026-01-15 10:30:45]: directory /var/www/cgi-bin is not owned by target user [2026-01-15 10:30:45]: target user/group mismatch ```
Step 8: Check SELinux/AppArmor
Verify security modules aren't blocking execution:
```bash # Check SELinux status getenforce # Output: Enforcing, Permissive, or Disabled
# If Enforcing, check file context ls -Z /var/www/cgi-bin/script.cgi
# Set correct SELinux context for CGI chcon -t httpd_sys_script_exec_t /var/www/cgi-bin/script.cgi
# Or restore default context restorecon -Rv /var/www/cgi-bin/
# For AppArmor aa-status # Check apache2 profile
# Temporarily disable for testing setenforce 0 # SELinux aa-disable /etc/apparmor.d/usr.sbin.apache2 # AppArmor ```
Step 9: Test with minimal CGI script
Create test script to verify Apache CGI works:
```bash # Create minimal test CGI script cat > /var/www/cgi-bin/test.cgi << 'EOF' #!/bin/bash echo "Content-type: text/html" echo "" echo "<html><body>" echo "<h1>CGI Test Successful</h1>" echo "Date: $(date)" echo "</body></html>" EOF
# Set permissions chmod 755 /var/www/cgi-bin/test.cgi chown www-data:www-data /var/www/cgi-bin/test.cgi
# Test via browser or curl curl http://localhost/cgi-bin/test.cgi
# Expected output: <html><body> <h1>CGI Test Successful</h1> Date: Tue Jan 15 10:30:45 UTC 2026 </body></html> ```
Verification
After fixing permissions, verify CGI scripts execute correctly:
```bash # Test script execution curl http://localhost/cgi-bin/script.cgi
# Check Apache error log is clean tail -20 /var/log/apache2/error.log | grep -i cgi # Should show no recent errors
# Verify file permissions ls -la /var/www/cgi-bin/script.cgi # Output: -rwxr-xr-x (755)
# Test multiple scripts if applicable for script in /var/www/cgi-bin/*.cgi; do curl http://localhost/cgi-bin/$(basename $script) | head -5 done
# Check Apache configuration syntax apache2ctl configtest # Expected: Syntax OK ```
Verify CGI response headers:
```bash curl -I http://localhost/cgi-bin/script.cgi
# Expected headers: HTTP/1.1 200 OK Content-Type: text/html # Should not show 500 error ```
Prevention
To prevent Apache CGI permission errors:
- 1.Standardize script permissions: Use consistent 755 mode for all CGI scripts.
chmod 755 /var/www/cgi-bin/*.cgi
chmod 755 /var/www/cgi-bin/*.pl- 1.Set correct ownership during deployment: Ensure deployment scripts set proper ownership.
#!/bin/bash
# Deployment script
cp scripts/*.cgi /var/www/cgi-bin/
chown www-data:www-data /var/www/cgi-bin/*.cgi
chmod 755 /var/www/cgi-bin/*.cgi- 1.Add permission checks to monitoring: Monitor for permission drift.
# Simple check script
#!/bin/bash
for f in /var/www/cgi-bin/*.cgi; do
perms=$(stat -c %a $f)
if [ "$perms" != "755" ]; then
echo "Wrong permissions on $f: $perms" | mail -s "CGI Alert" admin@example.com
fi
done- 1.Use ScriptAlias consistently: Define CGI directories clearly in Apache configuration.
# Standard CGI configuration
ScriptAlias /cgi-bin/ /var/www/cgi-bin/
<Directory /var/www/cgi-bin>
Options +ExecCGI
AddHandler cgi-script .cgi .pl .py .sh
AllowOverride None
Require all granted
</Directory>- 1.Validate shebang lines: Check interpreter paths during script deployment.
# Check shebang in deployment script
for f in scripts/*.cgi; do
interpreter=$(head -1 $f | cut -d'!' -f2)
if [ ! -x "$interpreter" ]; then
echo "Missing interpreter $interpreter in $f"
exit 1
fi
done- 1.Test scripts before production: Validate CGI functionality in development.
# Pre-deployment test
perl -c script.cgi && chmod 755 script.cgi
/var/www/cgi-bin/script.cgi | head -10 # Validate output- 1.Document CGI requirements: Maintain documentation of required configurations.
## CGI Script Requirements
- Location: /var/www/cgi-bin/
- Permissions: 755 (rwxr-xr-x)
- Ownership: www-data:www-data
- Shebang: #!/usr/bin/perl or #!/usr/bin/python3
- Apache config: ScriptAlias + ExecCGI optionsRelated Articles
- [WordPress troubleshooting: Fix CloudFormation Access Denied 403 - C](fix-cloudformation-access-denied-403-u1p0)
- [WordPress troubleshooting: Fix EC2 Configuration Error - Complete T](fix-ec2-configuration-error-szi0)
- [WordPress troubleshooting: Fix CloudFormation Configuration Error -](fix-cloudformation-configuration-error)
- [WordPress troubleshooting: Fix ELB Configuration Error - Complete T](fix-elb-configuration-error-2d3l)
- [WordPress troubleshooting: Fix S3 Access Denied 403 - Complete Trou](fix-s3-access-denied-403-g5cv)
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Apache CGI Script Permission Error", "description": "Apache CGI script fails when file permissions not set to executable.", "url": "https://www.fixwikihub.com/fix-apache-cgi-script-permission", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-01-23T01:04:11.819Z", "dateModified": "2026-01-23T01:04:11.819Z" } </script>