Thursday, February 28, 2013

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

http://www.eschrade.com/page/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.

No comments:

Post a Comment