So, I'm new to CGI / Perl, I'm trying to move a perl-based web app to a new server.

My new server is CentOS 7, which runs Apache HTTPD 2.4.6.

I'm trying to get a basic Perl CGI working from an HTTP request.

The web request is returning "500 Internal Server Error"

The error log is showing "permission denied":

[Tue May 12 16:56:44.604660 2015] [cgi:error] [pid 12302] [client] 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] End of script output before headers: first.pl

My CGI script is in /var/www/html/cgi-test/first.pl

It looks like this:

print "Content-type: text/html\n\n";
print "Hello, World.";

In the cgi-test directory the permissions look like this:

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

Perl is in the normal place and has I think normal permissions

[root@localhost cgi-test]# ls -al /usr/bin/perl
-rwxr-xr-x. 2 root root 11400 Mar  6 05:07 /usr/bin/perl

My httpd.conf is the default. I've just added the following section in order to allow cgi in my cgi-test directory:

<Directory "/var/www/html/cgi-test">
    Options +ExecCGI
    AddHandler cgi-script .cgi .pl

To eliminate the possibility of suexec being the cause of this issue I've moved it from /usr/sbin/suexec to another file name.

Httpd is running as user "apache" which is in group "apache"

[root@localhost cgi-test]# ps -Af | grep httpd
root     12298     1  0 16:56 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   12299 12298  0 16:56 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   12300 12298  0 16:56 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   12301 12298  0 16:56 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   12302 12298  0 16:56 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   12303 12298  0 16:56 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
root     12342 12260  0 17:20 pts/0    00:00:00 grep --color=auto httpd
[root@localhost cgi-test]# groups apache
apache : apache

I have tried running the script as apache, it works without any problem.

[root@localhost cgi-test]# su -s /bin/bash apache
bash-4.2$ perl /var/www/html/cgi-test/first.pl 
Content-type: text/html

Hello, World.bash-4.2$

Presumably I'm hitting some security precaution of Apache. Lots of conflicting advice about what this might be. Any help much appreciated.

  • Assuming you are running on linux, did you check is selinux is on/off? – alphamikevictor May 12 '15 at 16:52
  • As already mentioned, when you get permission denied errors 99% you have wrong permissions on your file to be accessed by Apache. Do you have SElinux enabled? – giomanda May 13 '15 at 9:11
  • SELinux was indeed enabled and preventing access. I'm new to SELinux so it's not clear to me how the SELinux config interacts with the unix permissions. – oggotron May 13 '15 at 9:17
  • I do often come across with SELinux issues with apache. Generally SElinux can save the day if your server is compromised but until it properly configured and handled it can make your life nightmare – giomanda May 13 '15 at 9:21
  • You're welcome! – alphamikevictor May 13 '15 at 9:32

I see from your own answer that it was a SELinux permissions issue due to trying to run CGI scripts from within apache in a non-standard directory.

The proper way to solve the permissions issue while maintaining SELinux in 'enforcing' mode, and thus improving your server's security is to apply the proper context to the files in your custom CGI script directory. If it is to be a permanent directory, you should change the selinux policy to automatically create new files with the proper permissions.

You can check the selinux policy for the cgi-bin directory with the command:

$ semanage fcontext --list | grep cgi-bin 
/var/www/[^/]*/cgi-bin(/.*)?                       all files          system_u:object_r:httpd_sys_script_exec_t:s0
/var/www/html/[^/]*/cgi-bin(/.*)?                  all files          system_u:object_r:httpd_sys_script_exec_t:s0

This means that every file created inside the standard cgi-bin directories for apache will be automatically given the SELinux type httpd_sys_script_exec_t and be executable by httpd, so this is what your files in the cgi-test directory should have as well.

NOTE: the examples shown below are based on CentOS/RHEL6, it should work just the same for RHEL7 with the eventual tweak.

Temporary solution

You can simply change your perl script's SELinux context with:

$ chcon -t httpd_sys_script_exec_t /var/www/html/cgi-test/first.pl

Check the file's SELinux attributes with ls -laZ:

$ ls -laZ /var/www/html/cgi-test/first.pl
-rwxr-xr-x. root root system_u:object_r:httpd_sys_script_exec_t:s0 /var/www/html/cgi-test/first.pl

However, if there's a SELinux relabel operation on this filesystem, the attributes will be reverted to the defaults and it will stop working again. It will also have to be done every time a new CGI script is added.

Definite solution

You can change the SELinux policy by adding a rule for your custom CGI directory and all contained subdirectories and files.

This is done via the semanage command (available in the policycoreutils-python RPM package):

$ semanage fcontext -a -t httpd_sys_script_exec_t "/var/www/html/cgi-test(/.*)?"

This will take a while to run. After changing the policy any new files created in your custom directory will have the new context. For the ones already there, you can apply the policy manually with:

$ restorecon -R -v /var/www/html/cgi-test

You can check your newly-added rule with:

$ semanage fcontext --list | grep cgi-test

The answer was that SELinux was blocking access to the script.

Thanks to alphamikevictor for suggesting this answer.

By default on CentOS 7 SELinux is enabled and blocks perl / CGI. I confirmed it was enabled using /usr/bin/sestatus

On the old server SELinux had been disabled completely.


Make sure that you have added ScriptAlias in the apache configuration like below

ScriptAlias /cgi-test/ /var/www/html/cgi-test/

Also try adding FollowSymLinks to the Options directive.

  • 2
    Neither of these were necessary. I don't need ScriptAlias because I'm telling Apache that there are scripts in a certain directory already. FollowSymLinks is not required because symlinks are not involved here. – oggotron May 13 '15 at 9:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.