Security with Apache htaccess Tutorial

Apache Security tips and tricks for securing Apache Web Servers using htaccess, httpd.conf, and other built-in techniques to thwart attackers. This really should be required reading for any Apache admin or user because these little tricks are so easy to do.

| .htaccess Tutorial Index |

Security with Apache htaccess

CHMOD your files

Chmod your .htpasswd files 640, .htaccess files 644. Chmod php files 600, chmod files that you really dont want people to see as 400 (wp-config.php, config.php) and NEVER chmod 777, if something requires write access use 766 first then 775

Prevent access to .htaccess and .htpasswd files

You will almost never have to do this unless you are working with your config file for the whole server. Anyway its easy to test if you need this.

<Files ~ "^.ht">
Order allow,deny
Deny from all

Show Source Code instead of executing

If you'd rather have .pl, .py, or .cgi files displayed in the browser as source rather than be executed as scripts

RemoveHandler cgi-script .pl .py .cgi

Securing directories: Remove the ability to execute scripts

This is cool, you are basically categorizing all files that end in certain extensions so that they fall under the jurisdiction of the -ExecCGI command, which also means -FollowSymLinks. And the opposite is also true, +ExecCGI also turns on +FollowSymLinks

AddHandler cgi-script .php .pl .py .jsp .asp .htm .shtml .sh .cgi
Options -ExecCGI

ErrorDocuments in htaccess

Note: 401 Errors only redirect to a local file, not an external file. Also, When you use an external link, don't do an external link to your site or you will cause a loop that will hurt your SEO.

Article: ErrorDocument Info and Examples, HTTP Status Codes

Common STATUS Codes and ErrorDocument Implementations

You will most definately want to check out and use the Google 404 Error Page.

ErrorDocument 400 /cgi-bin/e400.php

ErrorDocument 401 /cgi-bin/e401.php

ErrorDocument 403 /cgi-bin/e403.php

ErrorDocument 404 /cgi-bin/e404.php

ErrorDocument 405 /cgi-bin/e405.php

ErrorDocument 408 /cgi-bin/e408.php

ErrorDocument 410 /cgi-bin/e410.php

ErrorDocument 411 /cgi-bin/e411.php

ErrorDocument 412 /cgi-bin/e412.php

ErrorDocument 413 /cgi-bin/e413.php

ErrorDocument 414 /cgi-bin/e414.php

ErrorDocument 415 /cgi-bin/e415.php

ErrorDocument 500 /cgi-bin/e500.php

ErrorDocument 501 /cgi-bin/e501.php

ErrorDocument 502 /cgi-bin/e502.php

ErrorDocument 503 /cgi-bin/e503.php

ErrorDocument 506 /cgi-bin/e506.php

When using CGI PHP, php 404 Error example

You may also have to send a "Status" header in addition to the HTTP 1/1 header.

header('HTTP/1.1 404 Not Found');
header('Status: 404 Not Found');

Error message

An example 404 Error page in perl cgi


print "Status: 503 Service Temporarily Unavailablen";
print "Content-Type: text/html; charset=UTF-8;n";
print "Retry-After: 3600rnrn";
print "
<title>503 Service Temporarily Unavailable</title>
print "
<h1>Service Temporarily Unavailable</h1>
<p>The server is temporarily unable to service yourn";
print "request due to maintenance downtime or capacitynproblems. Please try again later.</p>

ErrorDocuments generated by Apache

Have a top.html

<!--#if expr="! $CONTENT_LANGUAGE"
<!--#set var="CONTENT_LANGUAGE" value="en"
<!--#if expr="! $CHARACTER_ENCODING"
<!--#set var="CHARACTER_ENCODING" value="ISO-8859-1"
<?xml version="1.0" encoding="<!--#echo var="CHARACTER_ENCODING" -->"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
<html xmlns="" lang="<!--#echo var="CONTENT_LANGUAGE" -->
" xml:lang="
<!--#echo var="CONTENT_LANGUAGE" -->
    <!--#echo encoding="none" var="TITLE" -->
    <link rev="made" href="mailto:<!--#echo encoding="url" var="SERVER_ADMIN" -->
" />
<style type="text/css">
    body { color: #000000; background-color: #FFFFFF; }
    a:link { color: #0000CC; }
    p, address {margin-left: 3em;}
    span {font-size: smaller;}
    <!--#echo encoding="none" var="TITLE" -->


<!--#include virtual="../contact.html.var" -->

    <!--#echo encoding="none" var="REDIRECT_STATUS" -->
<a href="">
<!--#echo var="SERVER_NAME" -->
</a><br />
<!--#config timefmt="%c" -->
<!--#echo var="DATE_LOCAL" -->
<br />
<!--#echo var="SERVER_SOFTWARE" -->


errordocument 401 /error/401.html
AuthType Basic
AuthName "site"
AuthUserFile /path/to/.htpasswd
AuthGroupFile /dev/null
Require valid-user
SetEnvIf Request_URI "^/(error/401.html¦robots.txt)$" allow_all
Order allow,deny
Allow from env=allow_all
Satisfy any

Mod_python Security Considerations

Many of the same security issues that apply to other embedded interpreters apply to mod_python. Here are some brief descriptions. Rather than list the solutions in detail, use them as a basis to search the mailing list or web:

Store sensitive data in modules outside of the DocumentRoot of your site. This prevents modules from being exposed if mod_python isn't running.

Because the embedded interpreter runs applications as the apache user, all other applications may have access to the same files. This can have serious implications in a multiuser environment, and applies to PHP, SSI, CGI, etc., as well.

Avoid any kind of dependency on the PATH environment variable. It can easily be changed by other applications, causing your own to fail. If you must call system programs, declare the full path explicitly, always.

Debugging information can be essential when developing an application. Take pains to ensure that error messages don't reveal sensitive data if they are returned to the browser. Review your code, and use try/except statements to catch errors when appropriate.

Learn about Python's mechanisms to restrict what gets exported by a module.

If your database and application are on the same machine, don't let the database listen on a port exposed to the Internet.

Understand the quirks of mod_python.publisher if you use it as a handler. For example, add a leading underscore to objects if you do not want them to be directly accessible via HTTP. (4)

_fo = "secret password"

If using mod_python.publisher and the legacy importer PythonOption mod_python. (or mod_python version < 3.3), don't use the same name for Python code files (module names) in multiple places. This is because there are bugs in its implementation which can cause cross contamination of modules, the loading of the wrong module and other issues. See: MODPYTHON-9MODPYTHON-10MODPYTHON-11

If you use PythonAutoReload, realise that modules are simply reloaded on top of the already loaded modules. This is in part shown in the MODPYTHON-11 bug report. It is mentioned here as a separate item because it means that if you rename a variable/function or remove it, that doesn't actually mean it is no longer accessible. The only way to guarantee that old variables/functions are no longer accessible is to restart Apache. Thus, if you heed (4) and rename variables to have a leading underscore, make sure you restart Apache at the same time.

If multiple people share the same web server, use PythonInterpreter to assign your area of the web site its own instance of the Python interpreter. This isn't fool proof as another user could do the same thing and use the same name. When working though, it will keep your stuff separate from others and avoid them being able to more easily see and fiddle with the internals of your running application.

When using the mod_python.psp handler with PythonDebug On the source code for your PSP pages can be visible by virtue of putting an underscore on the end of the extension. You need to add the .psp_ extension to your AddHandler configuration directive to turn on this feature.

AddHandler mod_python .psp .psp_
PythonHandler mod_python.psp
PythonDebug On

It is thus a good idea not to have PythonDebug enabled for production and/or public web site if using mod_python.psp. If you really need PythonDebug to be on, only enable it for requests coming from your own client machine in some way.

If Apache has write access to directories, it can write .pyc files into the directories for modules loaded. This extension isn't generally protected and people can download the ".pyc" files and potentially work out what your code is. Use something like:

<Files *.pyc>
deny from all

Depending on platform, you may have to block access to .pyo files as well.

<Files *.pyo>
deny from all

Use the FilesMatch directive to disallow access to important types of files, such as *.pyc, *.pyo, *~, etc.

Turn off automatic directory indexing for Apache on directories which use mod_python. This is generally applicable to any web site, but potentially more so for mod_python if you forget to do (3). Thus don't use "Indexes" option.

If using an extension such as ".html" with AddHandler to map to handler code in actual directory, ensure you block access to ".py" extension if need be.

<Files *.py>
deny from all

Don't store backup files where they can be accessed via HTTP. Honestly, I'm sure I could gather about a thousand database passwords in a day if I simply created a bot that crawled dynamically driven sites and appended ~ to every file name it finds.

PythonDebug in general can reveal stack traces to a client when something goes wrong. In the worst case, this may reveal secret information.

Try to avoid putting source code in actual directories visible to Apache. Especially do not put sensitive information in such files. The reason here is that it only takes one mistake in Apache configuration and all your code would be visible.

When writing a custom handler and returning apache.DECLINED, make sure you understand what it does. Specifically, it will cause the builtin default Apache handler to still run, which will serve up static files. Like above, you may need to deny access to certain files as a result.

External Apache htaccess security Articles

htaccess Guide Sections

| .htaccess Tutorial Index |