Nginx support for cpanel awstats

When using cpanel as a control panel for shared hosting, you’re by default forced to use Apache. The reason behind this is that it’s the most common webserver, but Apache is often using a lot of memory when you could lower this. A good choice is to put nginx in front. There’s multiple options for this. It could be nginxcp, cPnginx, ApacheBooster etc. The problem that happens when using one of the above, is often the logformat will change a little, which will break awstats. So I’ll show you how to make nginx support for cpanel awstats.

Why do I say ‘cpanel awstats’? The reason is that the normal awstats works perfectly, but because the stats for the domains is depending on how awstats and cpanel work together, cpanel have come up with a pretty bad way to handle the main configuration file for awstats. Which results in, that it’s not possible to add your own log format in the main config, so it will work for all your domains.

So I made a small cpanel hook, that is using their hook system. The hook itself is a small python script, which hooks into ‘Whostmgr::Accounts::Create’, reads the data from stdin to get information from the accounts getting created in cpanel, it generates a awstats config file in the users home directory, which will get loaded for that specific user.

By default cpanel doesn’t allow custom configuration for awstats, which means you need to log into WHM, go to ‘Statistics Software Configuration’ and enable ‘Awstats include file’.

Also you’ll need to change the log_format in the nginx config to the following:

log_format timed_combined '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time';

The log_format above is matching the normal apache logformat, except the very last parameter, which is the request time. The request time itself, is the time it takes nginx to process the request. Since we’re running nginx in front of Apache this means, it’s only the time spend in the proxy itself, so all dynamic processing will still be handled by apache. So that time is not a part of the request time.

The code below is the hook you need to save to /opt/makeawstats/

import sys, os

rawData = sys.stdin.readlines()

hookdata = eval(rawData[0].replace(':null', ':None'))

data = hookdata['data']
username = data['user']

if not os.path.exists('/home/%s/tmp/awstats' % username):
    os.makedirs('/home/%s/tmp/awstats' % username)

f = open('/home/%s/tmp/awstats/awstats.conf.include' % username, 'w')
f.write('LogFormat="%host %other %logname %time1 %methodurl %code %bytesd %refererquot %uaquot %extra1"\n')
f.write('ExtraSectionName1="Time to serve requests (seconds)"\n')
f.write('ExtraSectionFirstColumnTitle1="Number of seconds to serve the request"\n')

if os.path.exists('/home/%s/tmp/awstats' % username):
    os.system('chown -R %s:%s /home/%s/tmp/awstats' % (username, username, username))

The code reads stdin, replaces :null with :None. This is done to support python2.4 which is default on centos5 machines. So we don’t need any json module for this to work.
It then creates a directly in the users tmp directory called awstats.
Creates a file called awstats.conf.include, and writes 7 lines of configuration to this file, and save it.

First line written is the log format for awstats, this is used by awstats to process the access_logs from the user itself.
The next 6 lines is a custom section for awstats, this will process the request time, and put it into a section in awstats itself. The very last line, is used for track more rows, since the processing of the logs will break if this number is too low.
If you only process logs every 24 hours, you might need to increase this limit. This is also based on the amount of visitors you have.

After you’ve saved the file, you need to add the hook itself, you can do that by running following command from the terminal:

chmod +x /opt/makeawstats/ && /usr/local/cpanel/bin/manage_hooks add script /opt/makeawstats/ --stage post --category Whostmgr --event Accounts::Create --manual

Update: Added –manual to the hook registration, this will avoid the script from returning IndexError on registration.

You’ll also be able to fork the hook from github.