When migrating Perl CGI applications to a new CentOS 7 server running Apache 2.4, a common roadblock is the "Permission denied" error during script execution. The error typically appears in Apache logs as:
[cgi:error] AH01215: (13)Permission denied: exec of '/path/to/script.pl' failed
End of script output before headers: script.pl
This occurs despite correct file permissions and working manual execution. Let's break down the problem and solution.
Before diving into fixes, ensure these fundamental requirements are met:
1. Script has executable permission (-rwxr-xr-x)
2. Correct shebang line (#!/usr/bin/perl)
3. Apache configured to handle .pl files as CGI
4. SELinux context properly set (if enabled)
On CentOS/RHEL systems, SELinux often blocks CGI execution even with correct permissions. Check the current context:
ls -Z /var/www/html/cgi-test/first.pl
unconfined_u:object_r:httpd_sys_content_t:s0
The proper context for executable CGI scripts should be:
httpd_sys_script_exec_t
Apply the correct context with:
chcon -t httpd_sys_script_exec_t /var/www/html/cgi-test/first.pl
semanage fcontext -a -t httpd_sys_script_exec_t '/var/www/html/cgi-test(/.*)?'
restorecon -Rv /var/www/html/cgi-test/
Apache needs execute permission on all parent directories. Verify with:
namei -l /var/www/html/cgi-test/first.pl
Ensure all directories have at least --x--x--x (711) permissions:
chmod 711 /var
chmod 711 /var/www
chmod 711 /var/www/html
chmod 711 /var/www/html/cgi-test
For production environments, consider these additional security measures:
# In httpd.conf or virtual host
Options +ExecCGI -Indexes
AddHandler cgi-script .pl .cgi
Require all granted
# Optional: Run scripts as specific user
SuexecUserGroup scriptuser scriptgroup
Use this enhanced test script for better debugging:
#!/usr/bin/perl -wT
use strict;
use CGI::Carp qw(fatalsToBrowser);
print "Content-type: text/html\n\n";
print "CGI Test Script\n";
print "Running as: ".getpwuid($>)."\n";
print "SELinux Context: ".ls -Z $0."\n";
Remember to always test configuration changes:
apachectl configtest
systemctl restart httpd
When migrating a Perl-based web application to a new CentOS 7 server running Apache 2.4.6, I encountered a stubborn "500 Internal Server Error" with the following log entries:
[Tue May 12 16:56:44.604660 2015] [cgi:error] [pid 12302] [client 10.0.2.2:56693] AH01215: (13)Permission denied: exec of '/var/www/html/cgi-test/first.pl' failed
[Tue May 12 16:56:44.604708 2015] [cgi:error] [pid 12302] [client 10.0.2.2:56693] End of script output before headers: first.pl
The CGI script (first.pl
) had the following simple content:
#!/usr/bin/perl
print "Content-type: text/html\\n\\n";
print "Hello, World.";
Directory and file permissions were set as:
drwxr-xr-x. 2 root root 21 May 12 16:48 .
drwxr-xr-x. 4 root root 32 May 12 16:48 ..
-r-xr-xr-x. 1 root root 76 May 12 16:48 first.pl
The relevant httpd.conf addition was:
<Directory "/var/www/html/cgi-test">
Options +ExecCGI
AddHandler cgi-script .cgi .pl
</Directory>
After verifying that:
- Perl executable had proper permissions
- The script ran successfully when executed manually as apache user
- SELinux was not interfering (checked with
getenforce
)
The real culprit emerged: the script's parent directory permissions. While the script itself was executable, Apache's httpd process (running as apache user) needed execute permissions on all parent directories to traverse the path.
Here's the complete fix sequence:
# Set proper directory permissions
chmod o+x /var/www
chmod o+x /var/www/html
chmod o+x /var/www/html/cgi-test
# Set script ownership and permissions
chown apache:apache /var/www/html/cgi-test/first.pl
chmod 755 /var/www/html/cgi-test/first.pl
# Verify with namei -l
namei -l /var/www/html/cgi-test/first.pl
For production environments, consider these enhancements:
# Create dedicated CGI directory outside document root
mkdir /var/www/cgi-bin
chown apache:apache /var/www/cgi-bin
chmod 750 /var/www/cgi-bin
# Update httpd.conf
<Directory "/var/www/cgi-bin">
Options +ExecCGI
SetHandler cgi-script
Require all granted
</Directory>
Create a new test script with more robust error handling:
#!/usr/bin/perl -wT
use strict;
use CGI qw(:standard);
print header('text/html');
print start_html('Test Page');
print h1('CGI Test Successful!');
print end_html;
1;