Restricting access to directories on Apache websites

There are many ways to go about restricting access to specific content within Apache. This article is simply going to show a couple of examples.

Some of these examples will show you how to restrict access to a directory with a username and password. For this guide, the htpasswd file will be placed in /etc/httpd/example-htpasswd. You can create a username and password for it by simply using the external third party site http://www.htaccesstools.com/htpasswd-generator or you can use the built in tool htpasswd.

A basic example for password protecting an entire website is below:

[root@web01 ~]# vim /etc/httpd/vhost.d/example.com.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/vhosts/example.com
<Directory /var/www/vhosts/example.com>
	Options -Indexes +FollowSymLinks -MultiViews
	AllowOverride All

        # Password protect site
	AuthType Basic
	AuthName "Restricted"
	AuthUserFile /etc/httpd/example-htpasswd
	Require valid-user
</Directory>
...

If you wanted to only allow in specific IP’s or networks without a password and require everyone else on the internet to have a username/password:

[root@web01 ~]# vim /etc/httpd/vhost.d/example.com.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/vhosts/example.com
<Directory /var/www/vhosts/example.com>
	Options -Indexes +FollowSymLinks -MultiViews
	AllowOverride All

	# Password protect site
	Allow from 127.0.0.1
	Allow from 1.2.3.4
	Allow from 192.168.1.0/24

	AuthType Basic
	AuthName "Restricted"
	AuthUserFile /etc/httpd/example-htpasswd
	Require valid-user

	# Allow password-less access for allowed IPs
	Satisfy any
</Directory>
...

Below is an example for password protecting WordPress’s wp-admin page via an .htaccess file:

[root@web01 ~]# vim /var/www/vhosts/example.com/wp-admin/.htaccess
# Password protect wp-admin
<Files admin-ajax.php>
    Order allow,deny
    Allow from all
    Satisfy any
</Files>
AuthType Basic
AuthName "Restricted"
AuthUserFile /etc/httpd/example-htpasswd
Require valid-user

Here is one to restrict access to a directory by only allowing in specific IP’s within example.com/admin:

[root@web01 ~]# vim /var/www/vhosts/example.com/admin/.htaccess
order deny,allow
deny from all
allow from 1.2.3.4
allow from 192.168.1.0/24

On Apache 2.4, here is how you can password protect an entire website excluding one URI. This is useful if you use something like CakePHP or Laravel where the physical directory doesn’t exist, it all just filters through the index.php file. In this example, any requests to example.com/test will not require a password, but anything else on example.com will require a username and password:

[root@web01 ~]# vim /etc/httpd/vhost.d/example.com.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/vhosts/example.com/current/public
<Directory /var/www/vhosts/example.com/current/public>
	Options -Indexes +FollowSymLinks -MultiViews
	AllowOverride All
</Directory>

<Location "/">
	# Password protect site
	AuthType Basic
	AuthName "Restricted"
	AuthUserFile /etc/httpd/example-htpasswd
	Require valid-user

	# If the request goes to /test: bypass basic auth
	SetEnvIf Request_URI ^/test$ noauth=1
	Allow from env=REDIRECT_noauth
	Allow from env=noauth

	Order Deny,Allow
	Satisfy any
	Deny from all
</Location>
...

What happens when you want to password protect an aliased site? For instance, I have 2 domains, www.example1.com and www.example2.com. The www.example2.com is simply a ServerAlias defined within /etc/httpd/vhost.d/www.example1.com.conf. How can you go about password protecting www.example2.com without affecting www.example1.com? Simply add the following to the bottom of the .htaccess:

[root@web01 ~]# vim /var/www/vhosts/www.example.com/.htaccess
...
SetEnvIfNoCase Host example2\.com$ require_auth=true

AuthUserFile /etc/httpd/example2.com-htpasswd
AuthName "Password Protected"
AuthType Basic
Require valid-user
Order Deny,Allow
Satisfy any
Deny from all

Allow from env=!require_auth
...

Summarizing the MySQL Slow Query log

The MySQL slow query log is great for being able to tell you which queries are intensive on the CPU’s, which is most likely impacting your websites performance. When looking at the slow query log, it can appear like its an unorganized mess of random queries. So how do you summarize this list so you can know what queries to start fixing first? Welcome mysqldumpslow!

By default, MySQL comes with a tool called mysqldumpslow. Its primary purpose is to parse the slow query log and group similar queries together. This allows you to quickly see which queries need your attention in the slow query log.

If the slow query log is not already enabled, follow the older article I have here:
https://www.stephenrlang.com/2016/02/mysql-slow-query-log/

Assuming the slow query log is already enabled, locate the slow query log by running:

[root@db01 ~]# grep slow-query-log-file /etc/my.cnf
slow-query-log-file = /var/lib/mysql/slow-log

Queries that return a lot of rows and run often are typically the cause of CPU contention within MySQL. These CPU heavy queries may be able benefit from either a LIMIT clause, a table index or just needs to be rewritten so it returns less rows.

To display the top 10 queries that returned the highest amount of rows, run:

[root@db01 ~]# mysqldumpslow -a -s c -t 10 /var/lib/mysql/slow-log

Sometimes you just want to see which queries are constantly in the slow query log. Many times this could be from something unexpected and easily fixable by the developers.

To display the top 10 queries without any other sorting options, run:

[root@db01 ~]# mysqldumpslow -a -s r -t 10 /var/lib/mysql/slow-log

The data returned will show:

Count - How many times the query has been logged
Time - Both the average time and the total time in the ()
Lock - Table lock time
Rows - Number of rows returned

Below is one entry returned by the mysqldumpslow command that should give you a rough idea what information will be presented to you:

[root@db01 ~]# mysqldumpslow -a -s c -t 10 /var/lib/mysql/slow-log
...
Count: 4201  Time=68.34s (278868s)  Lock=0.00s (0s)  Rows=998512 (4194748912), root[root]@localhost
  SELECT PL.pl_title, P.page_title
  FROM page P
  INNER JOIN pagelinks PL
  ON PL.pl_namespace = P.page_namespace
  WHERE P.page_namespace = N
...