Why you should not use .htaccess (AllowOverride All) in production

Commonly known as .htaccess, AllowOverride is a neat little feature that allows you to tweak the server’s behavior without modifying the configuration file or restarting the server.  Personally, I think this is great for development purposes.  It allows you to quickly test various server configurations without needing to mess with restarting the server.  It helps you be more (buzzword alert!) agile.

Beyond the obvious security problems of allowing configuration modifications in a public document root there is also a performance impact.  What happens with AllowOverride is that Apache will do an open() call on each parent directory from the requested file onward.

To demonstrate this I used a program called strace which checks for system calls and gives you a list of each system call that is made.

First we’ll take a look at the strace with AllowOverride set to None.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
semop(1638426, {{0, -1, SEM_UNDO}}, 1) = 0
epoll_wait(42, {{EPOLLIN, {u32=3507213864, u64=139813282633256}}}, 2, 10000) = 1
accept4(4, {sa_family=AF_INET6, sin6_port=htons(55755), inet_pton(AF_INET6, "::ffff:192.168.0.212", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28], SOCK_CLOEXEC) = 43
semop(1638426, {{0, 1, SEM_UNDO}}, 1) = 0
getsockname(43, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:192.168.0.212", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
fcntl(43, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(43, F_SETFL, O_RDWR|O_NONBLOCK) = 0
read(43, "GET /test.txt HTTP/1.0\r\nHost: ma"..., 8000) = 87
gettimeofday({1361542861, 683952}, NULL) = 0
stat("/var/www/magento.loc/test.txt", {st_mode=S_IFREG|0644, st_size=12, ...}) = 0
open("/var/www/magento.loc/test.txt", O_RDONLY|O_CLOEXEC) = 44
fcntl(44, F_GETFD) = 0x1 (flags FD_CLOEXEC)
fcntl(44, F_SETFD, FD_CLOEXEC) = 0
mmap(NULL, 12, PROT_READ, MAP_SHARED, 44, 0) = 0x7f28cfc74000
writev(43, [{"HTTP/1.1 200 OK\r\nDate: Fri, 22 F"..., 267}, {"hello world\n", 12}], 2) = 279
munmap(0x7f28cfc74000, 12) = 0
write(12, "192.168.0.212 - - [22/Feb/2013:0"..., 79) = 79
shutdown(43, 1 /* send */) = 0
poll([{fd=43, events=POLLIN}], 1, 2000) = 1 ([{fd=43, revents=POLLIN|POLLHUP}])
read(43, "", 512) = 0
close(43) = 0
read(6, 0x7fff79e2d7cf, 1) = -1 EAGAIN (Resource temporarily unavailable)
close(44) = 0
semop(1638426, {{0, -1, SEM_UNDO}}, 1

Now let’s take a look at the strace results with AllowOverride set to All.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
semop(1736730, {{0, -1, SEM_UNDO}}, 1) = 0
epoll_wait(42, {{EPOLLIN, {u32=3392874024, u64=140410168747560}}}, 2, 10000) = 1
accept4(4, {sa_family=AF_INET6, sin6_port=htons(55795), inet_pton(AF_INET6, "::ffff:192.168.0.212", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28], SOCK_CLOEXEC) = 43
semop(1736730, {{0, 1, SEM_UNDO}}, 1) = 0
getsockname(43, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:192.168.0.212", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
fcntl(43, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(43, F_SETFL, O_RDWR|O_NONBLOCK) = 0
read(43, "GET /test.txt HTTP/1.0\r\nHost: ma"..., 8000) = 87
gettimeofday({1361543373, 140117}, NULL) = 0
stat("/var/www/magento.loc/test.txt", {st_mode=S_IFREG|0644, st_size=12, ...}) = 0
open("/var/www/.htaccess", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/var/www/magento.loc/.htaccess", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/var/www/magento.loc/test.txt/.htaccess", O_RDONLY|O_CLOEXEC) = -1 ENOTDIR (Not a directory)
open("/var/www/magento.loc/test.txt", O_RDONLY|O_CLOEXEC) = 44
fcntl(44, F_GETFD) = 0x1 (flags FD_CLOEXEC)
fcntl(44, F_SETFD, FD_CLOEXEC) = 0
mmap(NULL, 12, PROT_READ, MAP_SHARED, 44, 0) = 0x7fb3c8bf9000
writev(43, [{"HTTP/1.1 200 OK\r\nDate: Fri, 22 F"..., 267}, {"hello world\n", 12}], 2) = 279
munmap(0x7fb3c8bf9000, 12) = 0
write(12, "192.168.0.212 - - [22/Feb/2013:0"..., 79) = 79
shutdown(43, 1 /* send */) = 0
poll([{fd=43, events=POLLIN}], 1, 2000) = 1 ([{fd=43, revents=POLLIN|POLLHUP}])
read(43, "", 512) = 0
close(43) = 0
read(6, 0x7fff95abfc1f, 1) = -1 EAGAIN (Resource temporarily unavailable)
close(44) = 0
semop(1736730, {{0, -1, SEM_UNDO}}, 1

You can clearly see the additional open() calls being made to try and discover the .htaccess file.  In this case the calls are completely superfluous because we have nothing there.  But even so we have a significant impact on static file throughput.

AllowOverride None

1
2
3
4
5
6
7
8
9
10
11
Concurrency Level: 10
Time taken for tests: 2.441 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 2790279 bytes
HTML transferred: 120012 bytes
Requests per second: 4096.02 [#/sec] (mean)
Time per request: 2.441 [ms] (mean)
Time per request: 0.244 [ms] (mean, across all concurrent requests)
Transfer rate: 1116.12 [Kbytes/sec] received

AllowOverride All

1
2
3
4
5
6
7
8
9
10
11
Concurrency Level: 10
Time taken for tests: 3.922 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 2790558 bytes
HTML transferred: 120024 bytes
Requests per second: 2549.42 [#/sec] (mean)
Time per request: 3.922 [ms] (mean)
Time per request: 0.392 [ms] (mean, across all concurrent requests)
Transfer rate: 694.76 [Kbytes/sec] received

The requests where AllowOverride was turned off were executed at 60% of the time of the ones where AllowOverride was turned on.

And remember, this is just the impact of file operations and does not take into account the time to reconfigure Apache during the course of these requests.

So the data would clearly show that there is a negative impact to having AllowOverride turned on in a production environment.  Instead it will generally be better to take those changes in .htaccess and place them in your httpd configuration file.

[UPDATE]

In fact Mike Willbanks says you should never do it.  I  agree with him, but I wouldn’t make as big a stink in dev as I would in prod.

 

11 comments
cags
cags

If you have an application where the bottleneck is a 1.5 second delay across 10,000 requests (0.00015/request) handled by apache, I applaude your optimisation skills.

wimg
wimg

There's 2 other Apache options that have the same effect : FollowSymlinks and its friend SymLinksIfOwnerMatch. Unless you know there are symbolic links in your document root path somewhere, disabling them is a good idea.

DrewWoz
DrewWoz

Kevin, thanks for your investigation of this important server config option. As an admin of a shared vhost with no access to httpd.conf, I use .htaccess to blacklist bothersome visitors. Can you think of an alternate to .htaccess that offers better performance?

JHirvine
JHirvine

It is okay to have one in your root folder and there set it to none right? Or do you want to put all the alias/ expires/ rewrite stuff in the vhost? o.0 At least give us an alternative.

kschroeder
kschroeder moderator

If you have no access to httpd.conf then your options are pretty limited. That said, I don't know what your requirements are for running a shared host, but I have the cheapest Linode VM which gives me full access for $20 a month and I love it.

wimg
wimg

I tested it more than 2 years ago, in preparation for my Caching & Tuning tutorial at phpBenelux and it made anywhere between 20-40% difference. Haven't tested it since though.

JHirvine
JHirvine

Thanks for your reply, but my system manager tells me otherwise. He tells me to put everything per site in a .htaccess instead the httpd.conf. We have like over 250+ vhosts. In this situation, is it still prefered to put everything in httpd.conf?

JHirvine
JHirvine

Hard to disagree on that fact. Thanks. Very nice article!

kschroeder
kschroeder moderator

If throughput is a concern. As we saw in the benchmark, disabling AllowOverride caused a 40% improvement in performance for static files. There is also the inherent security issue of allowing items in your document root change the configuration of your web server on the fly.

Trackbacks

  1. […] it in the configuration file and save time? the benchmark shown by the excellent post by ESchrade http://www.eschrade.com/page/why-you-should-not-use-htaccess-allowoverride-all-in-production/ is very convincing. This idea is also reinforced by this post […]

Post Navigation

Web Analytics