Running CGI's from User Home Directories
by Walt Stoneburner
"This one took me two years to figure out."
You're trying to get CGI scripting to work on Apache from a user's
home directory, and you're having problems. You may even be plagued by
[error] (13)Permission denied: exec of /home/user/public_html/junk.pl failed
[error] [client xxx.xxx.xxx.xxx] Premature end of script
This article can help you past this problem, though it does assume
you are at least familiar with basic Apache configuration concepts.
First the Obvious
Here are some common errors, all very important to check out.
- Is Apache even running, use apachectl to be sure.
- Does the script contain any illegal control characters?
This can easily happen if you edit on Windows and move the
file over to Unix.
- Is the program specified by #! existing?
use the where command to be sure.
- Are you emitting a Content-type line with a blank
line separating from your page's content?
- Does the web server account have permission to read the file?
Use su to become the web account and try it out.
- Does the file have execute permissions on it? It should.
use the chmod command to remedy.
A Sample CGI Script
Here's a CGI script that works. Put it in your public_html
print "Content-type: text/html\n\n";
print "<html><body><h1>Hello World</h1></body></html>\n";
Enter $ where perl and edit the above script
to use that path instead.
Making Sure httpd.conf is Right
file contains a section that looks like
AllowOverride FileInfo AuthConfig Limit
Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
<Limit GET POST OPTIONS PROPFIND>
Allow from all
<LimitExcept GET POST OPTIONS PROPFIND>
Deny from all
line must also contain the word
. If you are using the
directive, this does not
as well, you must
For example, the line above would become:
Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec ExecCGI
This simply lets Apache know that anything in the
/home/*/public_html directory is allowed to run CGI
Next, we have to tell Apache what handler to use for various cgi extensions.
Find a line that looks like this:
AddHandler cgi-script .cgi
This line says, "if you see a .cgi program, then invoke the
cgi-script handler." We need to add other extensions. Modify the
line to read:
AddHandler cgi-script .cgi .pl
You can also append other extensions like .sh, .py, and
If you want to have default index.html-like files that are
really CGI, then you'll also need to modify another directive.
Look for this:
This lists, in order from left to right, the default filenames to try
if none is specified. Change it to something like this:
DirectoryIndex index.cgi index.html
You may also want to add index.pl, index.py,
index.sh, etc. PHP users may want index.php,
and Microsoft users may want index.htm which is different
from index.html on Unix.
500 Internal Server Error
If you are browsing to your home page,
and you see something like the following, your problem may be suexec
Apache/1.3.26 Server at www.yourdomain.com Port 80
Internal Server Error
The server encountered an internal error or
misconfiguration and was unable to complete
Please contact the server administrator,
firstname.lastname@example.org and inform them of the time the error occurred,
and anything you might have done that may have
caused the error.
More information about this error may be available
in the server error log.
If you find that your CGI's work when you're not using suexec
but don't when you are, then keep reading. It's impotant you know
How to tell if you are using suexec
Go to your /usr/local/apache/logs
directory, find error_log
and look inside and see if this string appears:
[notice] suEXEC mechanism enabled (wrapper: /usr/local/apache/bin/suexec)
It will, whenever Apache is started.
In the above example, the suexec program is located at
/usr/local/apache/bin/suexec, your location may vary.
Where should your copy be? Execute this command to find out (the
part you're looking for is the response highlighted below):
# cd /usr/local/apache/bin
# ./httpd -V | grep SUEXEC_BIN
A quick word about suexec
If you use the httpd.conf
directives above to enable CGI mode,
there are two ways the server can invoke user based
- Without suexec
The CGI program will run as the web server user. This
means a user can construct scripts and have access to anything
the web server does.
- With suexec
The CGI program will run as the user who's account it's in.
The catch here is that there are a pile of pre-conditions that
absolutely must take place, or suexec won't let you run
The correct permissions are:
# chown root suexec
# chgrp root suexec
# chmod 4711 suexec
If you're willing to go with option #1, simply rename the suexec
program to something else and restart
the webserver. User based
CGI problems will run now, but they'll run as the web server:
# cd /usr/local/apache/bin
# mv suexec suexec.disabled
# ./apachectl stop
# ./apachectl start
The advantages to using method #2 are two fold. First of all, the user
can't access files accessable by the web server (such as .php files with
database passwords in them), and secondly the user's scripts are
capable of accessing files the web server might not normally have
permission to access. Note, however, there are a pile of preconditions
that must be met before this will work. Violate any one of them
and you'll get a 550 error.
The Apache developers have listed all of these preconditions and installation
steps in the document:
Bonehead Problem That Plauged Me
Here's a list of the problems that were preventing me from running
. Be sure you've addressed everything the developers
- suexec must exist.
I had renamed my copy to suexec.disabled, which forced the
system to "work," though not securely. When I upgraded Apache,
that installation saw there was no suexec in the expected
place and put it back. This created the illusion that Apache had
broken my CGI. Is hadn't, it had made it more secure as it should
have been to start with.
- suexec must be runable by the web server's account.
I had the right file permissions, but I had locked down the
/usr/local/apache/bin directory so that only root could run
programs there. This is why Apache may make a
/usr/local/apache/sbin directory for suexec.
Simply su to the web server's account
as root, cd to the directory with suexec in
it, and try to run it.
- I didn't configure suexec when I compiled it.
I thought the out of the box configuration would work. For advanced
configurations, it may not.
suexec must be run by the web server account. How
does it know which one that is? You edit a #define in its
suexec.h file before compiling. It default to www,
but I was using web.
Note, suexec can not run as root, which
means your web server can not run as root. (Nor should
it, especially if you are security concious.)
There are a lot of other options, such as the mortal user account's
UID number must be over a specified value (so people can't hack
a system account). Most of your accounts in /etc/passwd will
have low numbers for system tasks, and much larger numbers for
real people. Same for GIDs.
- Apache will only look in one place for suexec.
Don't think you can move it. Use the
# ./httpd -V | grep SUEXEC_BIN trick to see
where it's got to go. Advanced users can twiddle with Apache's
configuration options to change the locations.
- suexec needs a place to log.
Again, I had locked down the log directory so that only the
web server could log there. This prevented suexec,
which was running as setuid root from writing a log file.
My solution was to make Apache's log directory owned by the
web server and the group be root, owner and group were given
Once I did this, suexec was writing a more descript
file in cgi.log about failures.
- The UID/GID of the user must match that of public_html,
which must also match that of the CGI program.
A while back I set things up so that my public_html was owned
by me, but the web server had group access. The reason I
did this was because I wasn't running my web server as group
users, because I didn't want users pawing through
my raw web files.
The cgi.log file was more than happy to point this
info: (target/actual) uid: (wls/wls) gid: (users/users) cmd: junk.pl
error: target uid/gid (503/100) mismatch with directory (503/300) or program (503/100)
Note that all three of these values didn't match. Fix 'em, and you're
This begs the question then, if Apache doesn't handle group permissions
correctly, how does one fix it?
The problem is that if you physically login to the web server account,
you get the UID/GID of the web server and any additional
groups listed in /etc/group. This is not the case when
one uses the User and Group directives in
The solution is to compile Apache with the MULTIPLE_GROUPS
preparser directive. While the capability exists, it certainly
isn't clearly docmented how to do this in the Apache documentation.
But enabling the feature does allow for all sorts of collaberation
via groups that aren't normally possible.
To turn it on, do your ./configure,
edit the file
append this to the end of the file:
Then compile apache as normal via make
and make install
I've written two news group threads on the subject of using MULTIPLE_GROUPS: