Showing posts with label Apache. Show all posts
Showing posts with label Apache. Show all posts

Wednesday, March 27, 2013

Scheduling awstats report generation

Scheduling awstats report generation


We've looked at running awstats reports, but only manually. Let's automate report generation so all you need to worry about is looking at those sweet, sweet numbers.




Automating awstats


In the previous article in this series we set up awstats for your site and ran an update of the reports manually. That's all well and good, but the command to update is big and ugly, and it would be kind of a pain to have to run it every time you want to view updated stats.

Fortunately Linux is chock full of ways to automate stuff. One way is to create a cron script to do all the updating, but since we're shooting for simple we should use a tool that's already processing your web logs on a regular basis. We can piggyback our updates onto logrotate's regular rotation tasks.

Scheduling reports with logrotate


With this approach we'll take advantage of the fact that logrotate is already performing regular log rotation for your domain. I mean, you do have your logs rotating automatically, right?

If not, visit this article series on logrotate and follow the directions there to set up log rotation for your virtual host. Log rotation keeps those logs from becoming giant disk-space-eating behemoths, so it's a very good idea. Really, do it now. I'll wait.

Now that we're certain you have logrotate managing your web logs, let's add a step to what logrotate does when it performs the rotation.

Editing the logrotate entry


Let's look at the logrotate.d file for a virtual host that's just a modification of the default entry for apache on Ubuntu:
/home/demo/public_html/example.com/logs/*.log {
weekly
missingok
rotate 52
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
postrotate
if [ -f "`. /etc/apache2/envvars ; echo ${APACHE_PID_FILE:-/var/run/apache2.pid}`" ]; then
/etc/init.d/apache2 reload > /dev/null
fi
endscript
}

Don't worry too much about most of those entries if you haven't seen them before. There are only a couple important bits to note here.

First, take a look at the "weekly" directive in that logrotate file. That's okay for simple log rotation, but you probably want your web traffic stats updating daily. In that case you'd want to change that to "daily" so the rotate script runs more frequently, and possibly modify the "rotate 52" entry to keep more archived log files.

Next we look at what we want to add to the logrotate process. See that "postrotate" block up there? Don't worry about what's inside, just note that it's there. What that does is run some stuff after the log rotation is done (in this case, tell apache to reload if it's running). The reason we're looking at it is because there's also a "prerotate" directive that we can use to have awstats run through a log file before it's rotated.

The "prerotate" directive should run the stats update and generate the reports. And it should run just like the command we ran to get our reports. We would create a prerotate block like:
prerotate
/usr/local/awstats/tools/awstats_buildstaticpages.pl -update -config=www.example.com -dir=/home/demo/public_html/example.com/webstats -awstatsprog=/usr/local/awstats/wwwroot/cgi-bin/awstats.pl > /dev/null
endscript

The "> /dev/null" bit redirects the normal output of the command so it doesn't get sent to the console or emailed to root.

Inserted into the existing logrotate file for our virtual host, the whole thing would look like:
/home/demo/public_html/example.com/logs/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
prerotate
/usr/local/awstats/tools/awstats_buildstaticpages.pl -update -config=www.example.com -dir=/home/demo/public_html/example.com/public/webstats -awstatsprog=/usr/local/awstats/wwwroot/cgi-bin/awstats.pl > /dev/null
endscript
postrotate
if [ -f "`. /etc/apache2/envvars ; echo ${APACHE_PID_FILE:-/var/run/apache2.pid}`" ]; then
/etc/init.d/apache2 reload > /dev/null
fi
endscript
}

And with that, every time the logs get rotated, the web stats get updated too.

Why use logrotate?


Instead of using logrotate to run the web stats update we could just schedule the stats to update through cron. So why insert the commands into logrotate?

The main reason is accuracy. By sticking a web stats update into the log rotation process we make sure that awstats is looking at log entries up until the last possible moment, when the log is rotated. If you have the web server reloading instead of restarting there might be a couple log entries missed (since the old log file would still be used as the old connections finish). The information lost in that case is negligible and not worth the downtime required to fully restart the web server.

If you want the web traffic reports to update more often than the web logs are rotated, you can use cron to run the update and report-building scripts on a more frequent schedule (like hourly). Awstats will recognize log entries that have already been processed and skip them when analyzing the data.

Monthly reports


Something you'll notice if you leave awstats running the way we've set it up is that the reports being generated only go into detail for the current month. Dividing the information up by month is a decent interval, but you may want to look at previous months in detail instead of only the current one.

So it's entirely optional, but if you'd like to keep monthly reports around it's really just a matter of tailoring a shell script to fit your site.

A cron script


We'll name the script after the awstats config file for the site in question and put it in the cron monthly directory. Using "www.example.com", we would create the file:
/etc/cron.monthly/awstats.www.example.com

Inside the file put the following script:
#!/bin/sh
#
# Run the awstats build script to generate a report for last month.
#

# Modify these 3 variables for your environment:
#
# The location of the awstats installation
AWSTATSDIR=/usr/local/awstats

# Your main domain, as you reported it to awstats
DOMAIN=www.example.com

# The directory where you're storing the reports for this domain
REPORTDIR=/home/demo/public_html/example.com/public/webstats

LASTMONTH=`date -d "last month" +%B`
LASTMONTHNUM=`date -d "last month" +%m`
LASTMONTHDIR=$REPORTDIR/$LASTMONTH

mkdir -p $LASTMONTHDIR
cp -Rf $AWSTATSDIR/wwwroot/icon $LASTMONTHDIR/awstatsicons

$AWSTATSDIR/tools/awstats_buildstaticpages.pl -month=$LASTMONTHNUM -update -config=$DOMAIN -dir=$LASTMONTHDIR -awstatsprog=$AWSTATSDIR/wwwroot/cgi-bin/awstats.pl > /dev/null

Change the values of "AWSTATSDIR", "DOMAIN", and "REPORTDIR" to match your environment and site.

After saving the file make your new script executable:
sudo chmod a+x /etc/cron.monthly/awstats.www.example.com

You can even run that script now to test it (and to get a detailed report for last month's data, if you have some). If you do, be sure to run it using sudo.

What it does


When the script runs it creates (if it doesn't already exist) a directory named after last month. If the current month is September, the directory will be named "August" (or the equivalent for your machine's locale setting). The awstats icons directory will be copied there, then a report will be generated for last month and put in the month's directory.

To look at a monthly report you'll use a similar URL to viewing the current report, but you'll insert the name of the month you want to view in front of the page name. The first letter of the monthly directory names is capitalized, so it will have to be capitalized in the URL used to visit a monthly report as well.

To take our example URL from earlier and use it to look at the August statistics we would change it to:
http://www.example.com/webstats/August/awstats.www.example.com.html

Keeping more monthly reports


Note that the way the script is written the monthly reports get replaced every year (since they're only made available by month name, not by year).

If you want monthly reports to never get overwritten you can modify the script to add the year to the directory names. Change the line that defines "LASTMONTH" above to something like:
LASTMONTH=`date -d "last month" +%B-%Y`

If the month were September, the above line would cause the script to use the directory name "August-2010".

Further reading


You have a solid but basic installation of awstats now. If you want to get more out of awstats there are a few advanced features you can look into.

For starters, you can browse the awstats documentation online.

If you want to generate reports on the fly you can do so by setting up the main awstats.pl script to run as a CGI script from a web browser. It would be a good idea to still run the stats update through a schedule. You'll want to be familiar with using CGI with a web server, and be aware of the risks that can be involved (for both performance and security). Then check theawstats install docs and take a look at the script they provide for automating aspects of that configuration.

If you want to extend awstats there are plugins available on the project's web site. A couple of the more interesting ones let awstats determine the country of origin of visitor IP addresses without launching a lot of cumbersome DNS lookups.

With a little tweaking and help from the documentation, awstats can also be used to build reports for mail and FTP servers.

Digging through all the options in the config file will give you an idea of what sorts of changes you can make to reports and their formatting.

Generating and viewing awstats reports

Generating and viewing awstats reports


Now that awstats is installed we take a look at actually running the analysis and viewing the reports.




Awstats in action


If you followed along with the first part of this series you should have awstats installed and configured for your site. In this article we'll look at a simple approach to report generation from the command line.

This approach will create static html pages to display your web traffic.

Build a report


Time to tell awstats to generate your reports. Fortunately for our "start with something simple" approach, there's a script that rolls generating several reports into one step.

awstats_buildstaticpages.pl


We're going to use a script that's included with awstats, "awstats_buildstaticpages.pl". This script updates the stats and generates a bunch of standard reports, using the main "awstats.pl" script behind the scenes. For a closer look at what reports this script will build, check the awstats online documentation.

For our example the command would look like:
sudo /usr/local/awstats/tools/awstats_buildstaticpages.pl -update -config=www.example.com -dir=/home/demo/public_html/example.com/public/webstats -awstatsprog=/usr/local/awstats/wwwroot/cgi-bin/awstats.pl

Okay, yeah. I admit that's kind of long. But it's not as scary as it seems, honest. Especially since you won't have to memorize it.

Let's break that down so you know what to put where.

The script itself


/usr/local/awstats/tools/awstats_buildstaticpages.pl

This part is the script we're running, "awstats_buildstaticpages.pl". If you installed to a location other than "/usr/local/awstats" you'll want to change this part to point to the actual location of the script on your machine.

The -update option


-update

Including "-update" at the beginning of the options tells the script to update the stats analysis before generating the reports.

The -config option


-config=www.example.com

The "config" value should be the main domain name for the site. Note that this domain matches up with the name of the config file you created in the first part of this series. The name of your config file should have "awstats." before the main domain name, and ".conf" after it, since that's pretty much what this script will be looking for.

In short, replace "www.example.com" with your main domain name.

The -dir option


-dir=/home/demo/public_html/example.com/public/webstats

The "-dir" option refers to the directory where you want awstats to create its reports. That directory should contain an "awstatsicons" directory containing awstats' standard image files.

The -awstatsprog option


-awstatsprog=/usr/local/awstats/wwwroot/cgi-bin/awstats.pl

For "-awstatsprog" you'll want the value to be the location of the "awstats.pl" script, which is the main awstats script. If you installed awstats someplace other than "/usr/local/awstats", adjust accordingly.

The script's results


Once you run that big command (all on one line) you should see that the script launches the awstats update process, then tells you about every one of the 20 reports it's generating.

If the script encountered an error it should give you some troubleshooting advice (like making sure you used the right "config" identifier).

Note that the last line, the "Main HTML page" line, gives the main page of the report.

If you take a look in your reports directory you should now see a bunch of html files there:
$ ls /home/demo/public_html/example.com/public/webstats                              
awstats.articles.slicehost.com.alldomains.html
awstats.articles.slicehost.com.allhosts.html
...

View the report


Now we get to see the results of our hard work. Point your browser to the "main html file" that was identified by the script we ran to generate the report.
http://www.example.com/webstats/awstats.www.example.com.html

The important part here is working out the address you'll use to view the reports. If you discover at this point that you created the reports in a directory you can't see from a browser, you may want to make a new reports directory. Edit your awstats config file accordingly, then run the report generation again to make sure it works with the new directory.

If all goes well you'll see something like:

Awstats example

(Without the smudges, of course.)

You might see less than a day's worth of traffic in this initial report, or perhaps a week, depending on how often your web logs are rotated. So not a lot that's interesting just yet, but enough to make sure the reports were generated properly.

Visits, hits, pages and bandwidth


There are a bunch of reports available, linked at the top of your main report's page. The main statistics you'll see at the beginning of the report bear some quick explanation, just so you know what you're looking at.

Unique visitors


The "unique visitors" stat tracks the number of different visitors your site received. For awstats this mostly means the number of unique IP addresses it saw in your web logs. This number isn't perfectly accurate, since visitors behind proxy servers and home routers can throw it off a bit (since those visitors would only appear in your web logs under the IP addresses belonging to the proxies or routers).

Number of visits


This stat tracks how many times visitors came back to the site. A "visit" for these purposes will encompass all page hits from a visitor within an hour or so of each other. If the same IP address appears in the web logs the next day that would count as a second visit.

Pages


A "page", in web traffic terms, is the main page of a visited URL. This would be the HTML or PHP file that was requested by the visitor. If a page includes the contents of other HTML files, only the main page is counted as a "page" in the traffic stats.

Hits


Pretty much everything a web browser asks for from a site is a "hit". The main page, headers and footers, images, videos — everything the browser has to ask for is a hit. A complex site will produce a fair number of hits per page visit.

Bandwidth


In the combined log format the web server records the size of all the requests and responses that get sent between the browser and the server. The total of all the outgoing response sizes is the "bandwidth" statistic in awstats. This is not necessarily the total bandwidth used by the site — it's just the total bandwidth that got recorded in your web server's access logs.

A note about referer spam


You may notice that the "referer" information in your reports contains links to referring web sites. This is useful for checking out sites that are linking to you but there's a potential drawback to putting this information on a web page, and that's "referer spam".

There's a school of thought among less-reputable web admins that encourages doing whatever you can to increase your search engine ratings. One of those tactics involves finding a site with a publicly-accessible web stats page and then running a script that visits the site a bunch of times using their web site as a referrer. The theory is that search engines will count the stats page as another site linking to their site.

In practice it doesn't work that well (most major search engines are wise to the practice and account for it), but that doesn't mean we should encourage the inconsiderate jerks to keep trying it.

The preferred method to keeping the stats pages from being used for spamming purposes is to protect the stats directory from unauthorized access. You can do that by password-protecting that part of the site, or by restricting access to that site to just localhost and using ssh tunneling to view your stats.

If you want to keep your stats public you should at least modify your site's "robots.txt" file to tell the major search engines not to index your stats pages. If you don't have a robots.txt file in the document root of your site this is a good time to create one.

Inside the robots.txt file you just need to add a "Disallow" rule for the web stats directory. If you don't have a robots.txt file already, you can use something like the following:
User-agent: *
Disallow: /webstats/

That would tell any robot that complies with the robots.txt file not to index the "webstats" part of the site. That way your stats site won't show up on major search engines at all, defeating the purpose of any efforts to manipulate your referers report.

If you want your web stats to show up on search engines for some reason, then at least tell robots not to index the referer page report:
User-agent: *
Disallow: /webstats/awstats.www.example.com.refererpages.html

Customizing apache web logs

Customizing apache web logs


You can create your own custom formats for apache web logs, to record more information or to make them easier to read. Here's how.




Changing the log format


If you know how to read web logs then you may have an idea of how you would want to write them differently — maybe add a little here, trim a little out there, switch the order around a bit. Luckily, you can do that with the access logs through a couple built-in commands and a handful of log variables.

LogFormat


Apache's "LogFormat" directive is what lets you define your own access log setup. Let's look at how that directive would be used to define the combined log format (CLF):
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

That first argument, in quotes, is the string that describes the log format. The last argument, "combined", gives a nickname to the format that can be used by CustomLog later on.

That format string contains a bunch of placeholders that describe the data to be included in the log. That first one, for example, is "%h" and represents the IP address of the visitor (the identifier for their host). A bit further on, "%t" represents the time of the request.

Components of the CLF


Let's look at that CLF format string side-by-side with an access log entry in the format:
%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"
123.65.150.10 - - [23/Aug/2010:03:50:59 +0000] "POST /wordpress3/wp-admin/admin-ajax.php HTTP/1.1" 200 2 "http://www.example.com/wordpress3/wp-admin/post-new.php" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.25 Safari/534.3"

Okay, they don't look too pretty together, but there is a correlation between each element in the format string and the components of the log entry below it. Breaking down what the stuff in the format string means:
%h      The remote host
%l The remote logname (usually just "-")
%u The authenticated user (if any)
%t The time of the access
\"%r\" The first line of the request
%>s The final status of the request
%b The size of the server's response, in bytes
\"%{Referer}i\" The referrer URL, taken from the request's headers
\"%{User-Agent}i\" The user agent, taken from the request's headers

So reading along, we see that in place of "%h" is "123.65.150.10" - the remote host. And after that, "%l" becomes "-" for the remote log host, "%u" turns into "-" for the remote user (since this connection didn't require authentication), "%t" is replaced with "[23/Aug/2010:03:50:59 +0000]" because it's the time the request was sent, and so on.

Note that the places in the log format where a quote character (") was used, it was escaped in the format string with a backslash (\"). The escape is there because if it were a quote symbol by itself, LogFormat would think the format string was complete at that point. The backslash tells it to keep reading.

The last two parts of the format, the referrer and the user agent, use a format component that requires an argument — in this case which header should be extracted from the request by %i. The referrer and user agent headers are, appropriately, named "Referer" and "User-Agent", respectively.

Well, mostly appropriately. "Referer" is misspelled. That's the spelling of the header name in the HTTP standards, however, so it is "Referer" for all time when talking about web link referrers. A bit of lexicographical trivia for you there. Enjoy.

Other format components


Apart from what we saw in our breakdown of the combined log format, there are other components you can include in a LogFormat entry. Some commonly-used components are:
%{cookie}C

The contents of the cookie named "cookie" for the request.
%{header}i

The contents of the HTTP header named "header" for the request.
%{VAR}e

The contents of the environment variable "VAR" for the request.
%k

The number of keepalive requests handled by the connection that spawned the logged request. The first time a request is sent the keepalive value will be zero, but each subsequent request that uses the same keepalive connection will increase that number by one. This can be handy for seeing how many requests a keepalive connection handles before it's terminated.

If keepalives aren't enabled this value will always be zero.

If you only see very low numbers for the keepalives value in a log but have a long keepalive timeout set, then it may be worth trying a much shorter timeout for keepalives. That way apache won't be maintaining connections in memory for longer than it needs to.
%T

How long the server took to serve the request, in seconds.
%v

The ServerName of the virtual host the request was sent to. This format code can be handy if you're writing more than one virtual hosts' accesses to the same log file.

For a full list of format components see the apache documentation for LogFormat.

Make your own log format


While the LogFormat entry is useful for interpreting what appears in the logs, it can also be used to create your own formats.

If you want your log to add the length of time it takes to serve requests to its access entries, you might make a LogFormat directive that looks like:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %T" timed_combined

All we have to do is add a "%T" to the end of the format string, then give it a new nickname — for our example, "timed_combined".

Using the new log format


Now, if you want to tell your virtual host to make an access log using the new format, you can include in the virtual host definition:
CustomLog /var/log/apache2/timed.log timed_combined

To recap: A LogFormat directive takes a format you give it and assigns it a nickname you choose. Then you use CustomLog to tell apache to write the access log using the new format by telling it where to write the log and the nickname of your log format.

Adding more custom logs


You can have more than one CustomLog directive for a virtual host. If you already have a CustomLog using the "combined" format, you don't have to remove it when adding your "timed_combined" log. This can be useful if you want to maintain one log in CLF that a web log analyzer program can read and another log file with just the information you care about when you're skimming the entries.

So if you wanted another log with just the stuff you wanted in it, you might take that "timed_combined" format and remove the things you feel are distractions. If you decided to remove the remote log entry, the user entry, and the user agent entry, you could create that format with:
LogFormat "%h %t \"%r\" %>s %b \"%{Referer}i\" %T" slim

And then add a new CustomLog directive to use the "slim" format:
CustomLog /var/log/apache2/slim.log slim

Precedence


Note that any logs defined in a virtual host will override log directives in the main apache config file. So if the main config file has the CustomLog entry:
CustomLog /var/log/apache2/access.log combined

And the virtual host has another CustomLog entry:
CustomLog /var/log/apache2/example.com.log combined

Then the virtual host will log its accesses to the "example.com.log" file, but not to the "access.log" file. If you wanted accesses to be logged to both files, you would need to include a line for the main access.log file in the virtual host definition, as in:
CustomLog /var/log/apache2/access.log combined
CustomLog /var/log/apache2/example.com.log combined

Rotating new logs


When you create any new logs, you should remember to configure logrotate to rotate them regularly. Otherwise they may grow and grow until they eat all your disk space right up. Any logs in the default apache log directory should get rotated under apache's default rules, but if you put a new log in another directory you may need to add a rule to logrotate.

Interpreting common status codes in web logs

Interpreting common status codes in web logs


The status codes you find in your web logs are useful troubleshooting tools, but only if you know what they mean.




Status codes


When a web browser talks to a web server, the server lets the client know the status of its request by sending a "status code". This status code will show up in the access logs of the server as a number. There are a lot of different status codes that can be passed to a web client, and you can view the full list at w3's website.

Fortunately there are only a few status codes that you're likely to see in your access logs, so consider the following descriptions to be highlights from the full list of status codes.

200 - OK


The 200 status code indicates that the request was successful. This is the one you want to see in your logs. At its most basic it means that when a web browser asked for a file, the server was able to find it and send it back to the browser.

403 - Forbidden


The 403 status code indicates that the server is not allowed to respond to the web client's request.

One circumstance that can cause a 403 status is if you do not have "Indexes" enabled for a directory, and the directory doesn't have an index file in it that the server can access. In other words, the client asked for a directory, and the server doesn't find anything there it can show to the client.

A more common circumstance is that the permissions on the file or directory being requested don't allow access by the web server's user. If the web server is running as user "www-data", any files you want the web server to serve will have to be accessible by the user "www-data". For example, if a directory's permissions look like:
drwx------ 5 root     root     4096 2009-12-18 01:39 wordpress

Then the user "www-data" will not be able to access any of the files inside. Requests sent to the server that ask for the "wordpress" directory or any of its contents will yield 403 status codes instead of serving the file requested.

For more information on how Linux file permissions work, you can read this article series. In a nutshell, the web server user needs to have read permission for files in order to serve them, and it has to have read and execute permissions for directories in order to see files inside them.

404 - Not found


A 404 status code means that the requested file could not be found. If you see this error often you should check the links on your site to make sure they're pointing to the right places.

Since the filesystem is case-sensitive you should also make sure the capitalization matches between the request in the URL and the name of the file on the disk. For example, if a file is named "File.txt" and the URL requests "file.txt", the file won't be found by the web server. Either the URL or the file name would need to be changed so the capitalization matches in both instances.

A couple commonly-requested files are worthy of note.

robots.txt


If you see 404 errors connected to a file named "robots.txt", that's the result of a spider program (like web search engines use) checking to see what your preferences are for indexing your site.

If you don't want to restrict the access of web spider robots to your site, you can just create an empty robots.txt file and the 404 errors will go away.

The robots.txt file can be useful if there are parts of the site that you want search engines to ignore. If you don't want search engines to record anything in the "orders" or "scripts" directories on your site, for example, you could use the following robots.txt file:
User-agent: *
Disallow: /orders/
Disallow: /scripts/

A slash at the end of a disallow will let the search engine robot know that it refers to a directory.

The "User-agent" part of the file describes what user agent the robots.txt would apply to. The "*" means that you want the rule to apply to everybody. You can have more than one User-agent entry in a robots.txt file, as in:
User-agent: EvilSearch
Disallow: /

User-agent: *
Disallow:

In that file, the EvilSearch engine's robot would be asked not to record anything on the site (thus the "/"), while everything else will be allowed to record anything they can find (which is what the empty argument to Disallow means).

Note that the robots.txt instructions aren't enforced in any way. A spider can freely ignore them. The better search engines (the ones you've heard of) tend to obey the robots.txt file, while spiders used by spammers and email harvesters will ignore robots.txt entirely.

favicon.ico


Any 404 errors connected to "favicon.ico" are the result of a web browser checking for a favorites icon for the site. That's another file not found error that can be safely ignored if you don't want to make a favorites icon for the site.

The favorites icon is often used by modern browsers both as an icon in a bookmarks list and as an identifying icon in a tabbed interface. If you've noticed that bringing up a site puts an image associated with the site next to your address bar or in the tab for that page, the favicon.ico file is where your browser got that image.

There are ways to point a browser to another file for the favorites icon, but if you want to make a quick-and-dirty favorites icon there are several utilities on the web that either allow you to create your own or convert an image file. Once you've generated the favicon.ico file you can upload it to the document root of your site and the associated 404 errors should stop appearing in your log.

500 - Internal server error


The 500 status code is kind of a catch-all error code for when a module or external program doesn't do what the web server was expecting it to do. If you have a module that proxies requests back to an application server behind your web server, and the application server is having problems, then the server could return a 500 error to web clients.

503 - Service unavailable


The 503 status code appears when the web server can't create a new connection to handle an incoming request. If you see this status code in your logs it usually means that you're getting more web traffic than can be handled by your current web server configuration. You'll then need to look into increasing the number of clients the server can handle at one time in order to be rid of this status code.

Reading apache web logs

Reading apache web logs


Whether you're dealing with web server difficulties or just want to see what apache is up to, your best bet is to look in its logs.




Keeping tabs on your web server


Sooner or later you'll want to know more about what your web server is up to. Luckily, apache (like many other server applications) keeps a diary of sorts called a "log".

Well, actually, more than one log, so the analogy isn't terribly good. Unless you think of your web server as a very organized diary-writer, maintaining different diaries for different kinds of events that have happened throughout the day.

Still not a great analogy, but it will do. In plainer terms: Logs are where apache records events like visitors to your site and problems it's encountered.

By default apache writes stuff about its activities in two types of logs — the error log and the access log.

Error log


The error log is where your web server records anything it doesn't think is quite right. Much of the time what gets recorded there are actual errors, like a visiting web client requesting a file that doesn't exist. Sometimes you'll also see warnings in there that don't indicate that a problem has occurred yet, but advise you that a particular event or configuration could cause problems later.

If you're having trouble with your web server this is the place to go first. For example, if you try to start your web server and it fails without telling you anything on the command line, it may be recording a reason in its error log. There you may find out about a misconfiguration or learn that it couldn't bind to the address or port it's configured for (possibly because some other program is already using the port).

Access log


The access log is where your web server records all the visitors to your site. There you can see what files users are accessing, how the web server responded to requests, and other information like what kind of web browsers visitors are using.

The access log can be used with programs called "traffic analyzers" to track the site's usage over time.

It can also be used to watch for unusual client behaviors that indicate someone is looking for a vulnerability they can exploit to hack your machine. If someone is sending unusual requests to an application you're running on your web server (like phpmyadmin or WordPress), it's usually a good idea to make sure you're running the latest version of the software.

Where to find your web logs


Before you can read your logs you'll need to find them. The most straightforward way to do that is to look for the configuration directives that tell apache where to create them.

Error log


To find the error log look in your main apache config file. The error log should be defined there with the "ErrorLog" directive. For example:
ErrorLog /var/log/apache2/error.log

Note that a lot of systems will restrict the permissions for apache's log directory to just root, so you may need to use the sudo command to look at the error log. For instance:
sudo cat /var/log/apache2/error.log

Access log


The access log is typically defined inside a virtual host block but can sometimes have a default defined in the main apache config file. You'll want to look for the "CustomLog" directive:
CustomLog /var/log/apache2/access.log combined

The first argument to CustomLog gives the file's location. The second argument ("combined") defines the format of the log. We'll get into what that means and how to change it later.

If a default CustomLog is defined in the main apache configuration and a different CustomLog is defined within a virtual host, the access log (or logs) defined in the virtual host will replace the default access log for just that virtual host.

Reading the logs


Now that you know where to find the logs, let's look at what's inside each. And most importantly, let's look at what they can tell you about your web server.

Error log


The error log is where the server will log, well, errors. These are usually errors the program encountered when trying to start a process or use a module, but they can also be errors that were sent to web clients, like a "file not found" error.

An error log entry for a file not found error would look something like:
[Mon Aug 23 15:25:35 2010] [error] [client 80.154.42.54] File does not exist: /var/www/phpmy-admin

In this case, a web client tried to visit a page in a "phpmy-admin" directory that didn't exist. Fortunately I happen to know that I don't have phpmy-admin installed, so it's not a broken link I need to fix. It's just some script kiddie looking for an exploitable version of that software. It's a good indication that I should install a program like fail2ban to block people like him.

Error log components


The first part of the log entry is the date and time (server time) when the event occurred. Apart from just being informative, that time can be useful for looking for entries in other logs at the same time. In this case I could check the access log to see the full URL that the web client tried to visit. If it were an error that indicated a module had trouble talking to a database, then I could look in the database server's logs at the same time to see what prevented the connection from happening.

The next part, "[error]", describes the level of the alert. This will often be "error", but sometimes other levels will indicate that the message logged is just a warning, or it may represent a critical error that caused the web server to shut down or fail to start.

The next part, "[client 80.154.42.54]", shows the source of the error. In this case the source is a web client, so the visitor's IP address was logged.

The last part of the log entry is the error itself.

Combined log format


The most common format for web log entries (and the default for most modern web servers) is the "combined" format, also referred to as "CLF" (Combined Log Format). A log entry in combined log format might look like this:
123.65.150.10 - - [23/Aug/2010:03:50:59 +0000] "POST /wordpress3/wp-admin/admin-ajax.php HTTP/1.1" 200 2 "http://www.example.com/wordpress3/wp-admin/post-new.php" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.25 Safari/534.3"

There's a lot of stuff there, but when you break the log entry down it contains a standard set of information in a standard order.

Combined log components


The first entry is the IP address of the web client accessing your server.

The second entry above is "-", which is what gets logged when there's nothing to put in that part of the log. In this case, the entry would represent the name of a remote log, if one were being used. You'll pretty much always see "-" here.

The third entry above is another "-". That slot contains the username the web client was authorized under, if any. If you enabled password protection for a file or directory, then the username the visitor used to log in would be recorded here.

The next entry is the date and time of the access.

The next entry is the first line of the request the web client sent to the server. In this case it's:
POST /wordpress3/wp-admin/admin-ajax.php HTTP/1.1

That entry means the web client sent a "POST" request (a submission of information) to the file at "/wordpress3/wp-admin/admin-ajax.php". That's a relative location, which means that if you wanted to find that file you'd start at the document root of that virtual host. If your document root was "/var/www", then the file being accessed above would be at "/var/www/wordpress3/wp-admin/admin-ajax.php". The last entry describes the protocol used for the request, in this case HTTP version 1.1.

The next entry tells us the status code that was returned for the request. The code above, "200", is hopefully one you'll see most often in your access logs — it means that the file was found and served to the client. Other common status codes are "403" (access forbidden) and "404" (file not found). We go into more detail about status codes in another article, but for a full list you can visit the official w3 website's list of status codes.

The next number is the size of the response your server sent, in bytes. In this case it was a very small response (2 bytes), so it was likely just an acknowledgement from the server rather than a full page access.

The next entry is the "referrer URL". In this case the entry is:
http://www.example.com/wordpress3/wp-admin/post-new.php

That's the page the web client visited before sending the recorded access request. Usually that means it's the page that linked to the one they accessed. The referrer can be useful information if you're wondering where people are finding links to your site (from a Google search, or a link from a partner site), or if you want to find the page that contained a bad link if the access entry was an error.

The last entry is called the "user agent". Most of the time that just means it's the identifier used by the web browser the visitor used. In this case, the user agent was:
Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.25 Safari/534.3

The user agent is pretty specific sometimes. In this entry the web browser told the server not only what its name was (Chrome, in this case), but also what operating system it's running on (Mac OS X), the version of the browser and the system, and the components that the web browser uses from the operating system. It's usually a lot more than you need, but if you know your site will display differently in different browsers, all that information can be used by a web application to tailor the page it returns to look best on that particular visitor's browser.

Putting them together


Whew! Lots of stuff there, but it's useful stuff. To give another example, let's look in again on our would-be intruder from earlier. Looking in the error log I saw what time he tried to access the non-existent directory. By looking at the same time in the access log I can see more information about what he tried to access:
80.154.42.54 - - [23/Aug/2010:15:25:35 +0000] "GET /phpmy-admin/scripts/setup.php HTTP/1.1" 404 347 "-" "ZmEu"

There's the same IP address and the same time. So we can see that his script used a "GET" method (a request for a page) to ask for the setup script for php-myadmin. The "404" status means that the file wasn't found. And while the user agent entry certainly isn't any web browser on the market, some web searches will turn up other people who have been hit by what is probably the same script. So even if that user agent isn't the browser, it can be useful in determining the type of attack your site was experiencing, and how many 404s you can expect your server to have to handle when it hits you.

Common log format


It isn't used a lot anymore, but you may run into a CustomLog directive that uses the log format of "common" in some older configurations of apache. The "common" format is similar to "combined", but omits the referrer and user agent information at the end of the log entry. Otherwise it can be read the same way as a "combined" format log.

Useful commands


Here are a few commands that can make browsing log files a little quicker or easier. These are very basic overviews of the commands in question. For more information on each you can check their respective man pages.

cat


The "cat" command simply displays the contents of a file. To see the whole error log all at once, you might run:
sudo cat /var/log/apache2/error.log

less


If the log you want to look at is particularly large, you probably don't want to look at the whole thing at once. To browse through a file you can use the "less" command:
sudo less /var/log/apache2/error.log

While less is displaying a file you can hit the space bar to page down, and the up and down arrow keys on your keyboard to scroll up or down one line at a time.

tail


The "tail" command returns lines from the end of a file. By default it displays the last ten lines of the file, so this command would display the last ten lines of an access log:
sudo tail /var/log/apache2/access.log

To specify the number of lines to grab, use the "-n [number]" option. To display the last 100 lines of the access log, you could run:
sudo tail -n 100 /var/log/apache2/access.log

You can also save yourself a little typing by just using "-[number]" instead of "-n [number]", as in:
sudo tail -100 /var/log/apache2/access.log

The tail command is useful if you're just looking for recent activity in a log. If you want to watch the end of a file for changes as they happen, you can use the "-f" option:
sudo tail -f /var/log/apache2/access.log

With this version of tail running, when a new line is added to the log file you'll see it on your screen too. To get out of tail when it's in this mode use control-C.

grep


If you're looking for a particular item in a web log (like a certain IP address, or any "404" responses), skimming through the log manually can be tiresome. It's easier to let the "grep" command do the work for you.

The grep command will look through its input, or a file, and return any lines that contain the search term sent to it. To look for the term "404" in an access log, you might run:
sudo grep 404 /var/log/apache2/access.log

The first argument is the text grep is searching for, and the second argument is the file to search.

If you want to look for a phrase, you can do that by enclosing the phrase in quotes. To look for requests for a particular file, you could run:
sudo grep "GET /images/butterfly.jpg" /var/log/apache2/access.log

By default grep's searches are case-sensitive. If you specify capital letters like "GET", then lines with "get" in lowercase letters won't be returned as hits. To make the search case-insensitive, pass grep the "-i" option, as in:
sudo grep -i "get /images/butterfly.jpg" /var/log/apache2/access.log

You can combine tail and grep by using what's called a "pipe":
sudo tail -n 100 /var/log/apache2/access.log | grep 404

The first part of that statement just lists the last 100 lines of the access log. The next character, "|", is the "pipe". It redirects the output of the last command and sends it to the next command in the statement. In this case that second command is "grep", searching for 404. So the above command would return any 404 errors found in the last 100 access log entries.

Apache Virtual Hosts - permissions

Apache Virtual Hosts - permissions


One thing that can cause concern and configuration headaches is virtual hosts permissions.

Using the multiple hosts layout in this article is a good way of keeping your domains in one place and with easy access. Let's take a look at the permissions of the folders.




Reminder


A quick reminder of the layout used in these articles:

All the vhosts directories are place in the user's home directory under public_html. Each domain has it's own directory with a 'standard' set of folders such as public, private, logs, cgi-bin, backup and so on.

The multiple hosts layout article has some images to show the layout in detail.

Users


We have to think of who will want access to the folders. There are two users to consider.

The first is the Slice user - in this case my username is 'demo'. It is in demo's home directory that the public_html directory is placed.

Secondly is the Apache user. On Debian based systems it is usual for Apache to be 'www-data' and be in the 'www-data' group. Other Linux distributions use the user 'nobody' or even 'apache'.

Access


Now we need to think about what actions will need to be taken by what user.

Apache needs to be able to enter the public_html folder and to be able to read the public and private directories.

The logs are written by the 'parent' Apache process which is root so we don't have to worry about that (root will have access to the logs directory anyway - all other Apache processes are completed by the child process which will be 'www-data' or 'nobody').

Write access


OK, but what happens if Apache needs write access? An example of this would be uploading images or files.

One way would be to allow world write access to the folder (setting the permissions at 777). The problem with this is that the world has write access to the folder.

Even if you had a specific upload folder and were careful with your scripts, it still leaves a 777 folder exposed to the public.

Groups


Remember the layout being used is for single user Slices. I host many websites on one Slice and I am the only one who has access to it.

What if we placed the main user (demo) in the 'www-data' group? That way, any folder that Apache needs to write to, such as the image upload folder, would only need 770 permissions.

There is no need for any other system user to access those folders. This also stops the practice of having a folder with 777 permissions.

User setup


So let's start by adding the main user to the Apache user group:
sudo usermod -a -G www-data demo

That adds the user 'demo' to the 'www-data' group. Do ensure you use both the -a and the -G options with the usermod command shown above.

You will need to log out and log back in again to enable the group change.

Check the groups now:
groups
...
# demo www-data

So now I am a member of two groups: My own (demo) and the Apache group (www-data).

Folder setup


Now we need to ensure the public_html folder is owned by the main user (demo) and is part of the Apache group (www-data).

Let's set that up:
sudo chgrp -R www-data /home/demo/public_html

As we are talking about permissions I'll add a quick note regarding the sudo command: It's a good habit to use absolute paths (/home/demo/public_html) as shown above rather than relative paths (~/public_html). It ensures sudo is being used in the correct location.

If you have a public_html folder with symlinks in place then be careful with that command as it will follow the symlinks. In those cases of a working public_html folder, change each folder by hand.

Setgid


Good so far, but remember the command we just gave only affects existing folders. What about anything new?

We can set the ownership so anything new is also in the 'www-data' group.

The first command will change the permissions for the public_html directory to include the "setgid" bit:
sudo chmod 2750 /home/demo/public_html

That will ensure that any new files are given the group 'www-data'. If you have subdirectories, you'll want to run that command for each subdirectory (this type of permission doesn't work with '-R'). Fortunately new subdirectories will be created with the 'setgid' bit set automatically.

If we need to allow write access to Apache, to an uploads directory for example, then set the permissions for that directory like so:
sudo chmod 2770 /home/demo/public_html/domain1.com/public/uploads

The permissions only need to be set once as new files will automatically be assigned the correct ownership.

DRY


This may get a bit tedious when you add new domains to your public_html folder.

In that case, I would create a 'skeleton' domain structure with the relevant permissions and simply copy it to the new domain:
cp -a /home/demo/public_html/skeleton /home/demo/public_html/my_new_domain

That way you have a nice and new set of folders with the correct permissions in one simple command.

Summary


Permissions are a sensitive topic and you do have to be careful with them. Don't just enter a 'chmod -R' command without thinking about the contents and symlinks.

I hope this helps alleviate concerns about vhosts and permissions.

I'd also like to thank Placid for his help in proof-reading this article - it was one I did not want to get wrong!

PickledOnion.

Addendum


A couple additions without changing too much of PickledOnion's article, based on user feedback:

Permissions in general


If you have more questions about how permissions work in Linux, you might take a look at this series on file permissions. It's a bit long, but thorough.

Setting a umask


The setgid bit allows files to inherit group ownership from the parent directory, but it doesn't affect their permissions. If you want new files to be created group-writeable by default instead of set that way manually, you need to change the umask.

You can find more information on using the umask command in this article. To give an example of a umask, "umask 002" would add group write permissions to new files.

If you want to set the umask for new files created by the web server, the file to change depends on the distribution and the software package. On Ubuntu or Debian you can add the umask line for apache in the file:
/etc/apache2/envvars

If you're running apache on CentOS, RHEL, or Fedora, you would add the umask command to:
/etc/sysconfig/httpd

Multiple hosts layout

Multiple hosts layout


During these articles I will be talking about different operating systems, different web servers and different, er, stuff.

To make things easier to understand and reference between articles and systems, I will use a 'standard' layout for hosting multiple sites (virtual hosts). Let me explain the layout I use.




Differences


As you know, there are several differences between distributions and not just in libraries, package management and so on.

The default directory locations for serving domains also differs. Some default to /var/www/ and some to /srv/www/. Some organisations recommend /srv/domain.com/.

Consistency


The way I will use is not the only way, nor is it special or the 'Slicehost' way.

It is, however, one way of organising your domains in one place and will work across different operating systems and web servers.

I will also mention that if you are using a shared hosting environment on your Slice then do stick with the OS defaults. My method places the domain directories in my home directory.

I am the only user of my Slice and this is, as far as I am aware, the case with the vast majority of Slice users, so I do not have to be concerned about other users logging in and accessing my home directory (apart from the usual security measures that is).

Layout


My domains are laid in their own directories in my /home/demo/public_html folder (demo is my main user name).

In each domain, I have a standard set of folders including logs, cgi, private, public, backup and so on but feel free to add/delete directories as you see fit.

One advantage of this layout is consistency between technologies. A standard Ruby on Rails application will have many directories with the main content being served from the 'public' directory.

My layout coincides with this so my plain html and dynamic PHP content are also served from the domain.com/public directory.

I can't incorporate every technology into one layout but I think this covers most eventualities. Specific articles (such as using Capistrano) will note the differences.

Folders


Let's take a look at the folders in use throughout these articles:

Multiple Domain Layout

So in this example I have three domains - each in their own directory. I always class subdomains as separate from the 'main' domain. After all, they have different content.

Details


Let's look in detail at the domain1.com folder:

Domain Layout

The layout is quite simple once you get used to all the connections:

public: where publicly served files, images, etc are placed.

private: used for files you do not want in the public domain such as PHP mysql connection files.

cgi-bin: umm, the cgi-bin

logs: place domain logs here - it keeps them separate and easily accessible.

backup: I place daily database backups here - makes for easier slice backups.

Change


Naturally, add/delete folders as you see fit. However, this is the layout that will be used throughout the articles when it comes to domain configurations.

Also, do use the OS default if you feel more comfortable doing so. Simply adjust the paths used in the demonstrations.

PickledOnion.

 

Introduction to Virtual Hosts

Introduction to Virtual Hosts


The term Virtual Hosts is used a great deal when setting up your Slice. Let's take a look at what Virtual Hosts are and why they are important.

It is also worth noting that the term Virtual Hosts is used with all web servers. Once you have the principle clear in your mind, it makes setting them up on any web server much easier.




Process


Let's take a quick look at what happens when you type a web address into your browser.

This is not a technical document so forgive the simplicity but the basics are quite simple:

  • Your browser sends an HTTP request to your ISP.

  • The request is routed, via your ISP and other servers, to where the IP address is located (i.e. your Slice).

  • The Slice web server processes the HTTP request and returns the HTML representation of the request to your browser.

  • Your browser interprets the HTML representation and parses the page for you.

  • You click 'next' and the whole process is repeated.


Single IP address


In this simple outline there is an important issue to note in that the web address you type into the browser is turned into an IP address.

So, for example, www.bbc.co.uk is turned into 212.58.227.75 (try it, put both into your browser, they both display the same thing).

We use names as humans are pretty bad at remembering numbers.

I've only got 1 IP address


So if my Slice only has one IP address can I only host one domain?

Nope. This is where virtual hosts come in...

You can allocated many domains to one IP address.

So lets say you have three domains: domain1.com, domain2.com and domain3.com and all of them have been configured to point to your Slice's IP address.

Using exactly the same (simplified) routing shown above, when any of your 3 domains are entered into a browser, the requests end up at your Slice.

Web Server


Enter the Web Server. It doesn't really matter what Web Server we are talking about here, it could be Apache, Litespeed, Nginx, lighttpd or any of the dozens of Web Servers available.

The point is that the request for 3 different domains has arrived at a single point: Your Slice and your Web Server.

Virtual Hosts


You see Web Servers are quite cool.

They are able to look at those requests and say "Hey, this guy wants an HTML representation of domain1.com and look, this guy wants one of domain2.com".

The server knows which file to parse and what to send back to the browser because you defined different settings and file locations for each domain in your Virtual Hosts file.

Each web server differs slightly in how to set up the Virtual Hosts file but they all have the same base settings:

Virtual Host Name: The name of the domain being requested - e.g. domain3.com.

Domain Directory: The location of the files for the requested domain.

Virtual Host Routing

Is that it?


Pretty much.

Of course, there are loads of settings you can customise for each Virtual Host but the basics remain the same: Receive a request for a domain, located the requested domain's directory and, depending on what was requested, serve the requested files.

What if I enter the IP address?


Ah. Now you've hit on the weak spot of name based Virtual Hosts: If all the domains have the same IP address, what happens when I enter the IP address?

There is usually a 'default' domain. You may not have expressly set it up that way, but if the IP address is entered it is 'usual' for the first domain (alphabetically) to be served.

As an example, in the Debian Etch Apache install, the default Virtual Host is named '000-default'. So if you have not expressly defined a Virtual Host for the IP address, then 000-default will be used.

It is worth saying that different web servers do act differently here. The main point is that, in this example, there is only one IP address so only one domain can be served.

However, it is relatively rare for people to browse around using IP addresses. Take this site, did you enter the IP address or the domain name?

How to serve multiple domains

How to serve multiple domains


Most people serve more than one domain on their Slice(s).

Whether for different domain names or different subdomains of the same domain, the procedure is the same.




Question


I am often asked by people how to use their Slice to serve multiple domains.

The question often surprises me as they may have setup their Slice, installed a web server (Apache, Nginx, etc) and even created a virtual host to serve their main domain.

Don't get me wrong, the question itself is good (I like questions as it makes me feel useful!), but the answer is always so simple:

Create another vhost.

Outline


There may not be a great deal I can add to the answer but let me outline the process of setting up a Slice and creating a virtual host (I won't go into any details of the installation and creation process - please see the relevant articles for detailed help).

When your Slice is first created it is a minimal Linux install (it doesn't matter what OS you choose).

You SSH into the Slice and update and secure it.

Then you install your preferred web server (Apache, Nginx, Litespeed, etc).

Then the detailed stuff begins. It doesn't matter if your site is PHP based or Rails based or something else entirely. You install the language and framework basics (say Ruby and rubygems or mod_php and so on).

Once that is all done, you come to the part that allows you to server your site: creating virtual hosts.

Procedure


Greatly simplified the procedure for serving a website is as follows:

A browser send a request to your Slice IP asking for the contents of 'domain.com' (your domain name).

Your web server jumps into action and says 'yes! I have something for you'. The web server does its 'thing' and serves up an http representation of your site which is sent to the browser.

The browser then translates the http and parses it to a human form of the web site (something like this one).

All jolly good but how does your web server know what to send?

Virtual Hosts


This is where name based virtual hosts come in.

One of the first lines in any virtual host contains the domain name that is related to the vhost.

Something like this for Apache:
<VirtualHost *:80>

ServerName domain1.com
ServerAlias www.domain1.com

and something like this for Nginx:
server {

server_name www.domain1.com;
rewrite ^/(.*) http://domain1.com/$1 permanent;

Each one starts slightly differently but the same principle applies - that particular virtual host will respond to queries for 'domain1.com' and 'www.domain1.com'.

Multiple domains


So, to serve different content for different domains is as simple as adding another virtual host.

Let's say you have a subdomain called 'blog.domain1.com' serving a blog (I know, shocking originality!).

The basic creation process would be to create a folder in your public_html folder with the relevant files (let's say a Wordpress install).

A virtual host would be created with the server_name or ServerName as 'blog.domain1.com' which would be configured to point to the blog files and folders in your public_html folder.

Done.

Language and frameworks


It doesn't matter what language or framework your domain uses.

To serve multiple Rails applications for example requires the same setup for each application.

Of course, there would be some differences such as port numbers for the mongrel or thin instances. Virtual hosts can't share ports.

For example, you may create a mongrel cluster to run from port 8000 - 8002 for one domain and another running from 8010 - 8012 for your blog and so on.

Summary


Once the Slice has been setup and the web server has been installed, serving multiple domains is very easy:

Simply create more vhosts...

PickledOnion

Enabling and using apache's mod_status on Gentoo

Apache-Server.-http-httpd-raleche-corp (1)Enabling and using apache's mod_status on Gentoo


Apache's mod_status module allows it to display a web page containing statistics about the web server's current state, including worker processes and active connections.




What is mod_status?


While you can get information on the connections being made to apache by checking its access log, if you want more immediate information about what apache is up to you can enable mod_status. This module allows you to view a detailed status page for your web server. This information can be useful for watching your web server's performance during load testing or for allowing a monitoring program like munin or mrtg to gather activity data for later aggregation.

The Apache Project keeps their server status page available to the general public. To get a look at a fairly busy web site's status page, visit:
http://www.apache.org/server-status

Enable mod_status


To enable mod_status you'll need to add the "STATUS" keyword to apache's start options. Open the configuration file at:
/etc/conf.d/apache2

Edit the list of keywords in the APACHE2_OPTS setting to include "-D STATUS", similar to:
APACHE2_OPTS="-D DEFAULT_VHOST -D INFO -D SSL -D SSL_DEFAULT_VHOST -D LANGUAGE -D PHP5 -D STATUS"

That keyword is used to tell apache to load the configuration file for mod_status, which is:
/etc/apache2/modules.d/00_mod_status.conf

Configure access


In the mod_status config file:
/etc/apache2/modules.d/00_mod_status.conf

Look for the following section:
# Allow server status reports generated by mod_status,
# with the URL of http://servername/server-status
<Location /server-status>
SetHandler server-status
Order deny,allow
Deny from all
Allow from 127.0.0.1
</Location>

If you haven't set up any virtual hosts in apache (i.e. you're just using the default configuration), you'll be editing this configuration block in place.

If you have created one or more virtual hosts, you'll want to paste this configuration for the /server-status location into the default virtual host for any domains configured in apache (basically, any that could return something if you request "domainname.com/server-status"). Then you would make needed changes to the Location configuration block in each file. When that's done, comment out the Location block in the 00modstatus.conf file.

The "Allow from .example.com" line should reflect what host or range of hosts you want to allow to view your server status page. We recommend setting that permission to localhost only (the default "127.0.0.1") so outsiders can't see detailed information about your web server. We'll talk about how you'll be able to view the status page from the slice itself in a later section.

ExtendedStatus


The ExtendedStatus setting adds more information to the status page apache returns, like CPU use and requests per second. Enabling ExtendedStatus makes apache do a little extra work when it gets a status request, so you might weigh the extra information gained against the potential performance hit to a busy server.

Many monitoring applications that record performance over time, like munin, require that ExtendedStatus be enabled before they can monitor apache.

The ExtendedStatus setting must be set at the server level and applies to all virtual hosts running under apache. It's best to set it in the mod_status config file, which once again is:
/etc/apache2/modules.d/00_mod_status.conf

It will likely be enabled by default:
# ExtendedStatus controls whether Apache will generate "full" status
# information (ExtendedStatus On) or just basic information (ExtendedStatus
# Off) when the "server-status" handler is called.
ExtendedStatus On

Comment out that last line to disable ExtendedStatus, changing the configuration block to:
# ExtendedStatus controls whether Apache will generate "full" status
# information (ExtendedStatus On) or just basic information (ExtendedStatus
# Off) when the "server-status" handler is called.
#ExtendedStatus On

Restart apache


Now that we've made sure the apache server status page is enabled and configured the way we want it we'll need to restart apache:
sudo /usr/sbin/apache2ctl restart

Install lynx


With apache's server status page restricted to localhost-only access we won't be able to see the page from our desktop's web browser. Luckily the server status page is just a bunch of text with no graphics, letting us use a simple approach: Run a text-based web browser while logged into the slice itself.

To try this option out we'll need to install a browser on the slice first. The browser we'll use is called "lynx", and you can install it with the following command:
sudo emerge --sync
sudo emerge lynx

No configuration is necessary, but lynx is keyboard-controlled so it's handy to know a few basic keystrokes when using it. There is a list of the most frequently-used commands at the bottom of the screen while lynx is running. If you visit a site with lynx you can navigate with the up and down keys and follow a highlighted link by hitting enter. Hit "q" to quit (and hit "y" to confirm the quit). Hit "h" to access lynx's documentation.

View the status page


The URL of the apache status page will be your domain name with "/server-status" tacked onto the end. In this section we're assuming you've configured your default server instance or virtual host to accept connections from the localhost only. Tell lynx to view your apache status page with the following command:
lynx http://localhost/server-status

You will see something like the following page if you have ExtendedStatus enabled (the example server was running Ubuntu Hardy, but it should look similar for all recent versions of Linux and Apache). With ExtendedStatus disabled the page will look similar, but with a few lines missing.
                                                                     Apache Status (p1 of 2)
Apache Server Status for localhost

Server Version: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.10 with Suhosin-Patch
Server Built: Nov 13 2009 21:58:02
______________________________________________________________________________

Current Time: Friday, 26-Mar-2010 19:04:17 UTC
Restart Time: Friday, 19-Mar-2010 20:21:03 UTC
Parent Server Generation: 1
Server uptime: 6 days 22 hours 43 minutes 14 seconds
Total accesses: 386 - Total Traffic: 317 kB
CPU Usage: u0 s0 cu0 cs0
.000643 requests/sec - 0 B/second - 840 B/request
1 requests currently being processed, 9 idle workers

W_________......................................................
................................................................
................................................................
................................................................

Scoreboard Key:
"_" Waiting for Connection, "S" Starting up, "R" Reading Request,
"W" Sending Reply, "K" Keepalive (read), "D" DNS Lookup,
"C" Closing connection, "L" Logging, "G" Gracefully finishing,
"I" Idle cleanup of worker, "." Open slot with no current process

Srv PID Acc M CPU SS Req Conn Child Slot Client VHost Request
0-1 3425 0/36/45 W 0.00 0 0 0.0 0.04 0.04 127.0.0.1 www.example.com GET
/server-status HTTP/1.0
1-1 3426 0/44/58 _ 0.00 4277 0 0.0 0.04 0.05 173.65.120.190 www.example.com GET
/favicon.ico HTTP/1.1

-- press space for next page --
Arrow keys: Up and Down to move. Right to follow a link; Left to go back.
H)elp O)ptions P)rint G)o M)ain screen Q)uit /=search [delete]=history list

If you get an error, troubleshoot accordingly. A "not found" error would indicate that mod_status isn't properly enabled. A "forbidden" error would mean that the Location configuration for /server-status is restricting you from accessing the page. A "connection refused" error means apache isn't listening on port 80 and may not be running.

Following the "Scoreboard Key" will be a list of current connections, including the source addresses and the file being accessed.

Most of the server status page should be pretty self-explanatory. The apache version is listed at the top, along with some of the modules loaded in apache. Some important stats following that include how long the apache server has been running since its last restart, the traffic it's handled since then, how much CPU is being used by apache at that moment, and how frequently apache is serving requests.

The section between the number of current requests and the "Scoreboard Key" is a representation of apache's "workers" and their status. The "worker" is essentially apache's request handler. Apache keeps several active at a time.

The Scoreboard Key shows what each symbol in the worker list means. In the example you can see the apache server isn't very busy — several "_" characters show a handful of workers waiting for requests, while a lone "W" shows a worker replying to a request (the request for the server status page, in fact). All the periods show slots that could be filled with workers if the server gets busy enough to need them.

If you visit the Apache Project's server status page you can see a more active server and a greater variety of worker states, like "K" (keep-alive, a reused connection waiting for a new request from a browser), "R" (reading a request from a browser), and "C" (closing a terminated connection).

Yes, but what does it all mean?


Most often the mod_status output is used by other tools to chart the server's activity over time. Viewed directly, the status page is handy for a quick overview of what your server is doing at a given moment. Some of the displayed data can indicate problems that merit investigation.

Examples of items to watch for on the status page include:

• A high CPU usage for apache could indicate a problem with an application being run through a module like mod_php.

• While it's normal to see several keep-alives being handled by apache's workers, if they constitute a vast majority of the worker statuses you see then your web server might be keeping old connections alive too long. You may want to look into reducing the amount of time connections are kept alive by apache via the KeepAliveTimeout directive.

• If you see very few inactive workers (represented by "." characters) you may want to increase the MaxClients value for your apache server. Making sure you have idle workers ready to handle new requests can improve the web server's responsiveness (assuming you have enough memory on the slice available to handle the extra connections).

Summary


Apache's status module is a handy complement to a monitor program, and the snapshot it provides of a web server's activity can highlight problems that would be otherwise difficult to isolate using standard system tools like top and lsof. Even if you're only enabling mod_status for a monitoring tool, knowing how to access and read the status page can help you be a more effective web server administrator.

For more information about mod_status and the details it reports check the Apache mod_status documentation.

Sunday, March 24, 2013

How to make your website faster - Apache / HTML optimization

Apache-Server.-http-httpd-raleche-corp (1)How to make your website faster - Apache / HTML optimization

First off I'd like to say welcome, here I will discuss effictive ways to speed up your website. First I will list server side hacks that will increase content delivery.

  • GZIP:


You can enable GZIP on a page with PHP, a quick example is placing this at the top of your static HTML page:
<?php 
[COLOR=Blue]ob_start("ob_gzhandler");[/COLOR]
?>

Here, say your HTML page is 210 KBs, with GZIP your page will now be around 19KBs!, Your client will see the exact same page, their browser will simply uncompress it.

You can do the same thing to JS/CSS pages, an example of including them will be:
<link rel="stylesheet" type="text/css" [B][COLOR=#ff0000]href="dynamic_css.php"[/COLOR][/B]/>

Catch: It increases processor usage lightly, consider if you're pushing 80K page views a day on a non-vps.
=========================================

  • Expires Tags:


If you are not actively changing content on static images, files or scripts you may use .htaccess to add automatic expirey dates, which means the file will not be re-downloaded unless it is after the expirey date, this is called caching.
<IfModule mod_headers.c>
# 1 Year
<FilesMatch "\.(ico|gif|jpg|jpeg|png|flv|pdf)$">
Header set Cache-Control "max-age=29030400"
</FilesMatch>
# 1 Week
<FilesMatch "\.(js|css|swf)$">
Header set Cache-Control "max-age=604800"
</FilesMatch>
# 45 Minutes
<FilesMatch "\.(html|htm|txt)$">
Header set Cache-Control "max-age=2700"
</FilesMatch>
</IfModule>

Edit entries to your need, this is a cost-efficient method to define expires headers in seconds.

=========================================
And now we move onto client side optimization
=========================================

  • IMG tags, a neat trick


With <img> tags always remember to add proper height/width attributes, this will tell the browser to make room before the image is actually downloaded. This will make the layout download smoothely and not jump and expand when it's not finished yet.
<img src="/logo/site.jpg" width="120" height="80"/>


  • Images, what can you do to make them smaller?


Images can be larger than the page itself, so we need to take caution in their sizes. Simple images, especially with a lot of the same colour should be in .png format, if you have any non-animated images in .gif format you can save 20-60% image size. JPEG images always have compression quality, you can open up your favourite image editor such as GIMP and re-save the image, fiddle with quality with preview on and pick what looks best. You most likely reduced page load by a few seconds there.

  • JS/CSS: Combine them!


Always keep your JS and CSS files as little as possible, remember each JS/CSS file = 1 new HTTP request can = 1 more second to lookup. A good recommendation would be to minify your CSS/JS files for your production site and keep a copy of them handy, as comments and whitespace are a good way to lag loading and functioning if you need speed to be snappy.

A good set of minifier is this:
JS: Online JavaScript Compressor (YUI and Microsoft Ajax Minifier), courtesy of Lottery Post
CSS: Online YUI Compressor

ENDING:


And that is all for now! If you have any comments, questions or think I should add something to the list feel free to comment.

 

How to make your website faster - Apache / HTML optimization

Apache-Server.-http-httpd-raleche-corp (1)How to make your website faster - Apache / HTML optimization

First off I'd like to say welcome, here I will discuss effictive ways to speed up your website. First I will list server side hacks that will increase content delivery.

  • GZIP:


You can enable GZIP on a page with PHP, a quick example is placing this at the top of your static HTML page:
<?php 
[COLOR=Blue]ob_start("ob_gzhandler");[/COLOR]
?>

Here, say your HTML page is 210 KBs, with GZIP your page will now be around 19KBs!, Your client will see the exact same page, their browser will simply uncompress it.

You can do the same thing to JS/CSS pages, an example of including them will be:
<link rel="stylesheet" type="text/css" [B][COLOR=#ff0000]href="dynamic_css.php"[/COLOR][/B]/>

Catch: It increases processor usage lightly, consider if you're pushing 80K page views a day on a non-vps.
=========================================

  • Expires Tags:


If you are not actively changing content on static images, files or scripts you may use .htaccess to add automatic expirey dates, which means the file will not be re-downloaded unless it is after the expirey date, this is called caching.
<IfModule mod_headers.c>
# 1 Year
<FilesMatch "\.(ico|gif|jpg|jpeg|png|flv|pdf)$">
Header set Cache-Control "max-age=29030400"
</FilesMatch>
# 1 Week
<FilesMatch "\.(js|css|swf)$">
Header set Cache-Control "max-age=604800"
</FilesMatch>
# 45 Minutes
<FilesMatch "\.(html|htm|txt)$">
Header set Cache-Control "max-age=2700"
</FilesMatch>
</IfModule>

Edit entries to your need, this is a cost-efficient method to define expires headers in seconds.

=========================================
And now we move onto client side optimization
=========================================

  • IMG tags, a neat trick


With <img> tags always remember to add proper height/width attributes, this will tell the browser to make room before the image is actually downloaded. This will make the layout download smoothely and not jump and expand when it's not finished yet.
<img src="/logo/site.jpg" width="120" height="80"/>


  • Images, what can you do to make them smaller?


Images can be larger than the page itself, so we need to take caution in their sizes. Simple images, especially with a lot of the same colour should be in .png format, if you have any non-animated images in .gif format you can save 20-60% image size. JPEG images always have compression quality, you can open up your favourite image editor such as GIMP and re-save the image, fiddle with quality with preview on and pick what looks best. You most likely reduced page load by a few seconds there.

  • JS/CSS: Combine them!


Always keep your JS and CSS files as little as possible, remember each JS/CSS file = 1 new HTTP request can = 1 more second to lookup. A good recommendation would be to minify your CSS/JS files for your production site and keep a copy of them handy, as comments and whitespace are a good way to lag loading and functioning if you need speed to be snappy.

A good set of minifier is this:
JS: Online JavaScript Compressor (YUI and Microsoft Ajax Minifier), courtesy of Lottery Post
CSS: Online YUI Compressor

ENDING:


And that is all for now! If you have any comments, questions or think I should add something to the list feel free to comment.

 

Install PHP Modules for a FreeBSD Production Web Server

php (1)Install PHP Modules for a FreeBSD Production Web Server

Let's say we want to build a FreeBSD Production Web Server with a list of php modules that will be installed after we install PHP, Apache and MySQL. Here is a script to do that, very usefull if we will install many servers in the future.
So in order to install a FreeBSD Production Web Server we will have to:

1. Install FreeBSD
---------------------
Get the last IMAGE of FreeBSD from ftp.freebsd.org. You can use USB version which is easely to install. Install it, then cvsup to the stable version.

You can do that using this tutorial: http://www.freebsdonline.com/content/view/460/476/


2. Install PHP and Appache
-----------------------------
  cd /usr/ports/www/apache22
make install clean

Add the following lines to /usr/local/etc/apache22/httpd.conf (to load index.php and support PHP)

(If you already have dir_module section, just add ther index.php)

<IfModule dir_module>
DirectoryIndex index.html index.php
</IfModule>

AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps


Then, compile php.

cd /usr/ports/lang/php5
make install clean


Don't forget check APACHE MODULE before compiling, then compile and install, so your Apache would be able to run PHP scripts.

You must configure Apache to run PHP.


3. Install MySQL Server and Client
-----------------------------------
To install MySQL Server and Client use:

cd /usr/ports/databases/mysql51-client
make install clean

cd /usr/ports/databases/mysql51-server
make install clean


4. Install PHP modules
--------------------------
Well this is the interesting part of this tutorial. We will use a list of PHP modules, saved in php_modules.conffile.
And then we will have a script: compile_php_modules.sh that will do the task of compiling php modules from ports and installing on our system.

# ------- php_modules.conf ------------

# php modules
php5-bz2
php5-bcmath
php5-ctype
php5-curl
php5-dom
php5-extensions
php5-filter
php5-gd
php5-gettext
php5-hash
php5-iconv
php5-json
php5-ldap
php5-mbstring
php5-mcrypt
php5-mysql
php5-openssl
php5-pdo
php5-pdo_sqlite
php5-posix
php5-session
php5-simplexml
php5-soap
php5-sockets
php5-sqlite
php5-tokenizer
php5-xml
php5-xmlreader
php5-xmlwriter
php5-zip
php5-zlib

# ------- eof php_modules.conf -------

And our script compile_php_modules.sh :

#!/bin/sh

MAKE_PARAMS=""

confFile="php_modules.conf"
confFileWithPath="php_modules_with_path.conf"
confLine=""
confLineWithPath=""
check_if_installed=""

rm php_modules_with_path.conf

while [ 1 ]
do
read confLine || break
if [ "`echo $confLine | cut -c1`" = "#" ] ; then
echo "Processing: "$confLine
else
echo "Generating path for:"$confLine
confLineWithPath=`whereis $confLine | cut -d" " -f2`
err=$?
if [ "$err" -ne 0 ]
then
echo "Error running make install, err:"$err"--->"$confLineWithPath
echo "Error running make install "$err"-"$confLineWithPath >>error_php_modules_with_path.log
exit 1;
fi
check_if_installed=`pkg_info | grep $confLine`
err=$?
if [ "$err" -ne 0 ]
then
echo "$confLineWithPath" >>php_modules_with_path.conf
else
echo "Package installed. Skipping... "$err"--->"$confLineWithPath
echo "Package installed. Skipping... "$err"-"$confLineWithPath >>error_php_modules_with_path.log
fi
fi
done < $confFile

while [ 1 ]
do
read confLine || break
if [ "`echo $confLine | cut -c1`" = "#" ] ; then
echo "$confLine"
else
cd $confLine
echo "Compile & Install: "$confLine
make  install $MAKE_PARAMS BATCH=yes
err=$?
if [ "$err" -ne 0 ]
then
echo "Error running make install, err:"$err"--->"$confLine
echo "Error running make install "$err"-"$confLine >>error_php_modules.log
exit 1;
fi
fi
done < $confFileWithPath


After installing PHP modules, don't forget to restart Apache.

If you want more modules you can add them to php_modules.conf file.

Install Apache in FreeBSD

Apache-Server.-http-httpd-raleche-corp (1)Install Apache in FreeBSD

Installing and configuring the Apache 2.2.x web server in FreeBSD.




    • Install

    • Configure

    • Log files









 

Install


To begin the Apache installation process, log in like the superuser and enter the following commands:
cd /usr/ports/www/apache22
make install clean

A menu should appear to show options for Apache. Here you may leave default options, but If you need to build and install Apache with special modules then you have to choose them now. For example if you want your Apache to support the working with MySQL databases then you need to select it in the menu by pressing 'space'. After choosing needed options you can start to install Apache by pressing 'tab' and 'enter'.

Configure

We are going to configure Apache to start automatically at boot time. To do so, edit the /etc/rc.conf file and add the following line to the end of file:
apache22_enable="YES"

After this we will make base configuration which is enough to run Apache. Open the file/usr/local/etc/apache22/httpd.conf. In this file you need to find and set values for two options:
ServerAdmin admin@example.com
ServerName host.example.com:80

Where options:

  • ServerAdmin is email address of the person who will be maintaining the server.

  • ServerName is hostname of your server


If you don't have hostname which pointed to server's IP then you may set any hostname, but then you will need to set this hostname and IP in your local PC which you use for browsing current server.
You need to add following line to file:

  • for Windows C:\WINDOWS\system32\drivers\etc\hosts

  • for Linux /etc/hosts


192.168.0.100 host.example.com

Where 192.168.0.100 is IP for your server where you are installing Apache.

After you completed configuring Apache you can check your config file for syntax errors by running this command:
/usr/local/etc/rc.d/apache22 configtest

If it returns "Syntax OK", continue below. If it finds a problem, it will list the filename, line number, and possible reasons for the error. Be sure to resolve any issues prior to continuing below.

After these you can run Apache by command:
/usr/local/etc/rc.d/apache22 start

Now you can open any browser and check how it works. For this example "host.example.com".

Log files

  • /var/log/httpd-access.log contains a log of IP addresses, times, and activity on the HTTP server

  • /var/log/httpd-error.log contains a log of error messages produced by the HTTP server

Install Apache in FreeBSD

Apache-Server.-http-httpd-raleche-corp (1)Install Apache in FreeBSD

Installing and configuring the Apache 2.2.x web server in FreeBSD.




    • Install

    • Configure

    • Log files









 

Install


To begin the Apache installation process, log in like the superuser and enter the following commands:
cd /usr/ports/www/apache22
make install clean

A menu should appear to show options for Apache. Here you may leave default options, but If you need to build and install Apache with special modules then you have to choose them now. For example if you want your Apache to support the working with MySQL databases then you need to select it in the menu by pressing 'space'. After choosing needed options you can start to install Apache by pressing 'tab' and 'enter'.

Configure

We are going to configure Apache to start automatically at boot time. To do so, edit the /etc/rc.conf file and add the following line to the end of file:
apache22_enable="YES"

After this we will make base configuration which is enough to run Apache. Open the file/usr/local/etc/apache22/httpd.conf. In this file you need to find and set values for two options:
ServerAdmin admin@example.com
ServerName host.example.com:80

Where options:

  • ServerAdmin is email address of the person who will be maintaining the server.

  • ServerName is hostname of your server


If you don't have hostname which pointed to server's IP then you may set any hostname, but then you will need to set this hostname and IP in your local PC which you use for browsing current server.
You need to add following line to file:

  • for Windows C:\WINDOWS\system32\drivers\etc\hosts

  • for Linux /etc/hosts


192.168.0.100 host.example.com

Where 192.168.0.100 is IP for your server where you are installing Apache.

After you completed configuring Apache you can check your config file for syntax errors by running this command:
/usr/local/etc/rc.d/apache22 configtest

If it returns "Syntax OK", continue below. If it finds a problem, it will list the filename, line number, and possible reasons for the error. Be sure to resolve any issues prior to continuing below.

After these you can run Apache by command:
/usr/local/etc/rc.d/apache22 start

Now you can open any browser and check how it works. For this example "host.example.com".

Log files

  • /var/log/httpd-access.log contains a log of IP addresses, times, and activity on the HTTP server

  • /var/log/httpd-error.log contains a log of error messages produced by the HTTP server