Archive

Ten SEO Mistakes Made on Database Driven Websites

Search engine friendly websites is one of those often heard phrases, both from web site development companies and from their clients. Everyone knows that this is important to have, and yet it is one of the things that is actually often overlooked.

Search engine optimisation companies actually spend a lot of their time analysing a website and removing barriers to the search engines ranking a site highly. At the web development level, it is possible to build a site that is perfectly search engine friendly. One of the hardest types of sites to get right though are database driven websites. Listed below are ten of the most common issues that are created, often unknowingly, in the development process of a dynamically generated web site.

1. Pages with duplicate content – not enough differential areas within the pages, so that only small areas of the page change from page to page. It is essential that enough of the page text changes for the search engines to see an appreciable difference between one page and the next.

2. Pages with duplicate page titles – the page title is a great indicator to the search engines of the primary content of the page. Whilst this is often unique on sites such as e-commerce websites, it is often overlooked in other sites, particularly where small areas of the site are generated from a database, such as news pages.

3. Pages with duplicate meta descriptions – again, this is easy to overlook and set a global or category level meta description. These give the search engines a reason to penalise your site for not giving them enough information, and again, creating a unique meta description for every page is an essential SEO task.

4. Using auto-generation of pages as a shortcut instead of creating good content. This is linked quite closely to point 1, where it is possible to create pages that have only a tiny percentage difference between them. Databases are fantastic ways of storing information, but you still need to put the work in to fill them with content. Unique information about the subject of the page will immensely help both the long tail and the ability of the search engines to determine that a page is valuable.

5. Creating pages that are hidden behind form submissions or javascript postbacks that cannot be accessed by a search engine crawler. This is far more common that is generally realised. For instance .NET creates postback links by default instead of proper links – potentially making huge sections of a site unreachable. Likewise, it is easy to hide lovely content rich areas of your site behind a drop down selector in a form that means certain areas of the site are not visible.

6. Too many query strings – this is a common bugbear of the professional SEO, where complicated database selections create deep levels of pages, but with seven or eight &id= type strings. Additionally, some bad development methodology can leave pages with null query strings that appear in every URL but don’t do anything. The answer to this is generally URL rewrites, creating much more search engine friendly and user-friendly URLs!

7. Putting query strings in different orders when accessed through different places – this can create duplicate content issues, which can cause major penalties.

8. Not using user language to generate automated pages – if you are going to create a database driven website that uses words in the query strings (or better in rewritten URLs) make sure that you use words that will help you with SEO – if you sell widgets, make sure you are using the word widgets somewhere in the URL instead of just product= or id= – keyword research can assist with this.

9. Not allowing the meta data and title to be edited easily after the site build. It is possible to hardcode the generation of meta information into a database that doesn’t allow it to be edited later. Creating a mechanism for modifying this information initially helps everyone at a later stage when the information needs changing without shoehorning it into an already developed structure.

10. Creating keyword stuffed pages by using auto-generation. Once upon a time, search engines quite liked pages with high densities of your keywords, but now these are likely to get you marked down rather than up. So be aware when creating pages that long pages with lots of your products on can create too high a density. For instance listing blue widgets, light blue widgets, navy blue widgets, sky blue widgets is going to create a page with a very dense page for the phrase “blue widgets”.

These are just 10 of the most common potential optimisation pitfalls when creating dynamic websites. There are many more facets to producing a great database driven site, including user friendliness, speed, performance and security, but they all add together to make the best solution to your needs.

About the Author: Mark Stubbs is a freelance writer who specialises in internet marketing and web site development. For more information on database driven websites he suggests that you visit www.obs-group.co.uk.

Source

How To Use MySQL Query Profiling

What is the MySQL slow query log?

The MySQL slow query log is a log that MySQL sends slow, potentially problematic queries to. This logging functionality comes with MySQL but is turned off by default. What queries are logged is determined by customizable server variables that allow for query profiling based on an application’s performance requirements. Generally the queries that are logged are queries that take longer than a specified amount of time to execute or queries that do not properly hit indexes.

Setting up profiling variables

The primary server variables for setting up the MySQL slow query log are:

slow_query_log			G 
slow_query_log_file			G 
long_query_time			G / S
log_queries_not_using_indexes	G
min_examined_row_limit		G / S

NOTE: (G) global variable, (S) session variable

slow_query_log – Boolean for turning the slow query log on and off.

slow_query_log_file – The absolute path for the query log file. The file’s directory should be owned by the mysqld user and have the correct permissions to be read from and written to. The mysql daemon will likely be running as `mysql` but to verify run the following in the Linux terminal:

 ps -ef | grep bin/mysqld | cut -d' ' -f1

The output will likely display the current user as well as the mysqld user. An example of setting the directory path /var/log/mysql:

cd /var/log
mkdir mysql
chmod 755 mysql
chown mysql:mysql mysql

long_query_time – The time, in seconds, for checking query length. For a value of 5, any query taking longer than 5s to execute would be logged.

log_queries_not_using_indexes – Boolean value whether to log queries that are not hitting indexes. When doing query analysis, it is important to log queries that are not hitting indexes.

min_examined_row_limit – Sets a lower limit on how many rows should be examined. A value of 1000 would ignore any query that analyzes less than 1000 rows.

The MySQL server variables can be set in the MySQL conf file or dynamically via a MySQL GUI or MySQL command line. If the variables are set in the conf file, they will be persisted when the server restarts but will also require a server restart to become active. The MySQL conf file is usually located in `/etc or /usr`, typically `/etc/my.cnf` or `/etc/mysql/my.cnf`. To find the conf file (may have to broaden search to more root directories):

find /etc -name my.cnf
find /usr -name my.cnf

Once the conf file has been found, simply append the desired values under the [mysqld] heading:

[mysqld]
….
slow-query-log = 1
slow-query-log-file = /var/log/mysql/localhost-slow.log
long_query_time = 1
log-queries-not-using-indexes

Again, the changes will not take affect until after a server restart, so if the changes are needed immediately then set the variables dynamically:

mysql> SET GLOBAL slow_query_log = 'ON';
mysql> SET GLOBAL slow_query_log_file = '/var/log/mysql/localhost-slow.log';
mysql> SET GLOBAL log_queries_not_using_indexes = 'ON';
mysql> SET SESSION long_query_time = 1;
mysql> SET SESSION min_examined_row_limit = 100;

To check the variable values:

mysql> SHOW GLOBAL VARIABLES LIKE 'slow_query_log';
mysql> SHOW SESSION VARIABLES LIKE 'long_query_time';

One drawback to setting MySQL variables dynamically is that the variables will be lost upon server restart. It is advisable to add any important variables that you need to be persisted to the MySQL conf file.

NOTE: The syntax for setting variables dynamically via SET and placing them into the conf file are slightly different, e.g. `slow_query_log` vs. `slow-query-log`. View MySQL’s dynamic system variables page for the different syntaxes. The Option-File Format is the format for the conf file and System Variable Name is the variable name for setting the variables dynamically.

Generating query profile data

Now that the MySQL slow query log configurations have been outlined, it is time to generate some query data for profiling. This example was written on a running MySQL instance with no prior slow log configurations set. The example’s queries can be run via a MySQL GUI or through the MySQL command prompt. When monitoring the slow query log, it is useful to have two connection windows open to the server: one connection for writing the MySQL statements and one connection for watching the query log.

In the MySQL console tab, log into MySQL server with a user who has SUPER ADMIN privileges. To start, create a test database and table, add some dummy data, and turn on the slow query log. This example should be run in a development environment, ideally with no other applications using MySQL to help avoid cluttering the query log as it is being monitored:

$> mysql -u <user_name> -p

mysql> CREATE DATABASE profile_sampling;
mysql> USE profile_sampling;
mysql> CREATE TABLE users ( id TINYINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) );
mysql> INSERT INTO users (name) VALUES ('Walter'),('Skyler'),('Jesse'),('Hank'),('Walter Jr.'),('Marie'),('Saul'),('Gustavo'),('Hector'),('Mike');mysql> SET GLOBAL slow_query_log = 1;
mysql> SET GLOBAL slow_query_log_file = '/var/log/mysql/localhost-slow.log';
mysql> SET GLOBAL log_queries_not_using_indexes = 1;
mysql> SET long_query_time = 10;
mysql> SET min_examined_row_limit = 0;

There is now a test database and table with a small amount of test data. The slow query log was turned on but the query time was intentionally set high and the minimum row examined flag kept off. In the console tab for viewing the log:

cd /var/log/mysql
ls -l

There should be no slow query log in the folder yet, as no queries have been run. If there is, that means that the slow query log has been turned on and configured in the past, which may skew some of this example’s results. Back in the MySQL tab, run the following SQL:

mysql> USE profile_sampling;
mysql> SELECT * FROM users WHERE id = 1;

The query executed was a simple select using the Primary Key index from the table. This query was fast and used an index, so there will be no entries in the slow query log for this query. Look back in the query log directory and verify that no log was created. Now back in your MySQL window run:

mysql> SELECT * FROM users WHERE name = 'Jesse';

This query was run on a non indexed column – name. At this point there will be a query in the log with the following info (may not be exactly the same):

/var/log/mysql/localhost-slow.log

# Time: 140322 13:54:58
# [email protected]: root[root] @ localhost []
# Query_time: 0.000303  Lock_time: 0.000090 Rows_sent: 1  Rows_examined: 10
use profile_sampling;
SET timestamp=1395521698;
SELECT * FROM users WHERE name = 'Jesse';

The query has been successfully logged. One more example. Raise the minimum examined row limit and run a similar query:

mysql> SET min_examined_row_limit = 100;
mysql> SELECT * FROM users WHERE name = 'Walter';

No data will be added to the log because the minimum of 100 rows was not analyzed.

NOTE: If there is no data being populated into the log, there are a couple of things that can be checked. First the permissions of the directory in which the log is being created in. The owner/group should be the same as the mysqld user (see above for example) as well as have correct permissions, chmod 755 to be sure. Second, there may have been existing slow query variable configurations that are interfering with the example. Reset the defaults by removing any slow query variables from the conf file and restarting the server, or set the global variables dynamically back to their default values. If the changes are made dynamically, logout and log back into MySQL to ensure the global updates take effect.

 

Analyzing query profile information

Looking at the query profile data from the above example:

# Time: 140322 13:54:58
# [email protected]: root[root] @ localhost []
# Query_time: 0.000303  Lock_time: 0.000090 Rows_sent: 1  Rows_examined: 10
use profile_sampling;
SET timestamp=1395521698;
SELECT * FROM users WHERE name = 'Jesse';

The entry displays:

  • Time at which the query was ran
  • Who ran it
  • How long the query took
  • Length of the lock
  • How many rows where returned
  • How many rows where examined

This is useful because any query that violates the performance requirements specified with the server variables will end up in the log. This allows a developer, or admin, to have MySQL alert them when a query is not performing as well as it should [opposed to reading through source code and trying to find poorly written queries]. Also, the query profiling data can be useful when it is profiled over a period of time, which can help determine what circumstances are contributing to poor application performance.

Using mysqldumpslow

In a more realistic example, profiling would be enabled on a database driven application, providing a moderate stream of data to profile against. The log would be continually getting written to, likely more frequently than anybody would be watching. As the log size grows, it becomes difficult to parse through all the data and problematic queries easily get lost in the log. MySQL offers another tool, mysqldumpslow, that helps avoid this problem by breaking down the slow query log. The binary is bundled with MySQL (on Linux) so to use it simply run the command and pass in the log path:

mysqldumpslow -t 5 -s at /var/log/mysql/localhost-slow.log

There are various parameters that can be used with the command to help customize output. In the above example the top 5 queries sorted by the average query time will be displayed. The resulting rows are more readable as well as grouped by query (this output is different from the example to demonstrate high values):

 

Count: 2  Time=68.34s (136s)  Lock=0.00s (0s)  Rows=39892974.5 (79785949), root[root]@localhost
  SELECT PL.pl_title, P.page_title
  FROM page P
  INNER JOIN pagelinks PL
  ON PL.pl_namespace = P.page_namespace
  WHERE P.page_namespace = N
…

The data being displayed:

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

The command abstracts numbers and strings, so the same queries with different WHERE clauses will be counted as the same query (notice the page_namespace = N). Having a tool like mysqldumpslow prevents the need to constantly watch the slow query log, instead allowing for periodic or automated checks. The parameters to the mysqldumpslow command allow for some complex expression matching which help drill down into the various queries in the log.

There are also 3rd party log analysis tools available that offer different data views, a popular one being pt-query-digest.

Query breakdown

One last profiling tool to be aware of is the tool which allows for a complex break down of a query. A good use case for the tool is grabbing a problematic query from the slow query log and running it directly in MySQL. First profiling must be turned on, then the query is ran:

mysql> SET SESSION profiling = 1;
mysql> USE profile_sampling;
mysql> SELECT * FROM users WHERE name = 'Jesse';
mysql> SHOW PROFILES;

After profiling has been turned on, the SHOW PROFILES will show a table linking a Query_ID to a SQL statement. Find the Query_ID corresponding to the query ran and run the following query (replace # with your Query_ID):

mysql> SELECT * FROM INFORMATION_SCHEMA.PROFILING WHERE QUERY_ID=#;

Sample Output:

SEQ STATE DURATION
1 starting 0.000046
2 checking permissions 0.000005
3 opening tables 0.000036

The STATE is the “step” in the process of executing the query, and the DURATION is how long that step took to complete, in seconds. This isn’t an overly useful tool, but it is interesting and can help determine what part of the query execution is causing the most latency.

For a detailed outline of the various columns:http://dev.mysql.com/doc/refman/5.5/en/profiling-table.html

For a detailed overview of the various “steps”:http://dev.mysql.com/doc/refman/5.5/en/general-thread-states.html

NOTE: This tool should NOT be used in a production environment rather for analyzing specific queries.

Slow query log performance

One last question to address is how the slow query log will affect performance. In general it is safe to run the slow query log in a production environment; neither the CPU nor the I/O load should be a concern ¹ ². However, there should be some strategy for monitoring the log size to ensure the log file size does not get too big for the file system. Also, a good rule of thumb when running the slow query log in a production environment is to leave long_query_time at 1s or higher.

IMPORTANT: It is not a good idea to use the profiling tool, SET profiling=1, nor to log all queries, i.e. the general_log variable, in a production, high workload environment.

Conclusion

The slow query log is extremely helpful in singling out problematic queries and profiling overall query performance. When query profiling with the slow query log, a developer can get an in-depth understanding of how an application’s MySQL queries are performing. Using a tool such as mysqldumpslow, monitoring and evaluating the slow query log becomes manageable and can easily be incorporated into the development process. Now that problematic queries have been identified, the next step is to tune the queries for maximum performance.

How To Use Nginx As Reverse Proxy Server

Nginx is an open source Web server and a reverse proxy server. You can use nginx for a load balancing and/or as a proxy solution to run services from inside those machines through your host’s single public IP address such as 202.54.1.1. In this post, I will explain how to install nginx as reverse proxy server for Apache+php5 domain called www.example.com and Lighttpd static asset domain called static.example.com. You need to type the following commands on vm00having an IP address 192.168.1.1 only.

DNS Setup

Make sure both www.example.com and static.example.com point to public IP address 202.54.1.1.

Install nginx server

Type the following command to install nginx web server:
$ cd /tmp
$ wget http://nginx.org/packages/rhel/6/noarch/RPMS/nginx-release-rhel-6-0.el6.ngx.noarch.rpm
# rpm -iv nginx-release-rhel-6-0.el6.ngx.noarch.rpm
# yum install nginx

Sample outputs:

Loaded plugins: rhnplugin
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package nginx.x86_64 0:1.2.1-1.el6.ngx will be installed
--> Finished Dependency Resolution
Dependencies Resolved
=========================================================================
 Package      Arch          Version                   Repository    Size
=========================================================================
Installing:
 nginx        x86_64        1.2.1-1.el6.ngx           nginx        331 k
Transaction Summary
=========================================================================
Install       1 Package(s)
Total download size: 331 k
Installed size: 730 k
Is this ok [y/N]: y
Downloading Packages:
nginx-1.2.1-1.el6.ngx.x86_64.rpm                  | 331 kB     00:00
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
Warning: RPMDB altered outside of yum.
  Installing : nginx-1.2.1-1.el6.ngx.x86_64                          1/1
----------------------------------------------------------------------
Thanks for using NGINX!
Check out our community web site:
* http://nginx.org/en/support.html
If you have questions about commercial support for NGINX please visit:
* http://www.nginx.com/support.html
----------------------------------------------------------------------
  Verifying  : nginx-1.2.1-1.el6.ngx.x86_64                          1/1
Installed:
  nginx.x86_64 0:1.2.1-1.el6.ngx
Complete!

Configure the nginx web server as reverse proxy

Edit /etc/nginx/conf.d/default.conf, enter:
# vi /etc/nginx/conf.d/default.conf
Add/correct as follows:

 
## Basic reverse proxy server ##
## Apache (vm02) backend for www.example.com ##
upstream apachephp  {
      server 192.168.1.11:80; #Apache1
}

## Lighttpd (vm01) backend for static.example.com ##
upstream lighttpd  {
      server 192.168.1.10:80; #Lighttpd1
}

## Start www.example.com ##
server {
    listen       202.54.1.1:80;
    server_name  www.example.com;

    access_log  /var/log/nginx/log/www.example.access.log  main;
    error_log  /var/log/nginx/log/www.example.error.log;
    root   /usr/share/nginx/html;
    index  index.html index.htm;

    ## send request back to apache1 ##
    location / {
     proxy_pass  http://apachephp;
     proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
     proxy_redirect off;
     proxy_buffering off;
     proxy_set_header        Host            $host;
     proxy_set_header        X-Real-IP       $remote_addr;
     proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
   }
}
## End www.example.com ##

## START static.example.com ##
server {
   listen      202.54.1.1:80;
   server_name static.example.com;
   access_log  /var/log/nginx/log/static.example.com.access.log  main;
   error_log   /var/log/nginx/log/static.example.com.error.log;
   root        /usr/local/nginx/html;
   index       index.html;

   location / {
        proxy_pass  http://lighttpd;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;
        proxy_buffering off;
        proxy_set_header        Host            static.example.com;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
## END static.example.com  ##

Turn on Nginx

Type the following commands:
# chkconfig nginx on
# service nginx start

Configure firewall

Set firewall as follows:

  • Drop all INPUT/OUTPUT chain traffic by default.
  • Only open tcp port 202.54.1.1:80 and/or 443 on eth0 only.
  • Set eth1 as trusted device so that communication take place between nginx reverse proxy and Apache/Lighttpd backend servers.

Run the following command to set and customize firewall as described above:
# system-config-firewall-tui
You can edit /etc/sysconfig/iptables manually and set the firewall too.

/etc/sysctl.conf

Edit /etc/sysctl.conf as follows:

 
# Execshild
kernel.exec-shield = 1
kernel.randomize_va_space = 1

# IPv4 settings
net.ipv4.ip_forward = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Increase system file descriptor limit to
fs.file-max = 50000

# Increase system IP port limits
net.ipv4.ip_local_port_range = 2000 65000

# Ipv6
net.ipv6.conf.default.router_solicitations = 0
net.ipv6.conf.default.accept_ra_rtr_pref = 0
net.ipv6.conf.default.accept_ra_pinfo = 0
net.ipv6.conf.default.accept_ra_defrtr = 0
net.ipv6.conf.default.autoconf = 0
net.ipv6.conf.default.dad_transmits = 0
net.ipv6.conf.default.max_addresses = 1

Load new Linux kernel settings, run:
# sysctl -p

How To Setup WHMCS – One Installation, Multiple Domains

These 6 installations where all on separate servers. Maintaining each one and having to manually upgrade was ridiculous. So I used brain power and combined them into one installation.

For this example, I will be using domain1.com and domain2.com. This setup can handle as many as you need. I am assuming you have root access to a basic Linux server.

Step By Step

– Create a user on the server “whmcs”
– – This user should be pointed to /home/whmcs

# adduser -d /home/whmcs whmcs

– Extract the WHMCS script into /home/whmcs/master
– – This will be the single install of WHMCS and it’s root web directory – Create symbolic links
– – This is used for DOCUMENT_ROOT reference

# ln -s /home/whmcs/master domain1.com
# ln -s /home/whmcs/master domain2.com

– Edit /home/whmcs/master/configuration.php to the following
– – We are creating logic to include a different configuration file depending on which domain is being visited.

if ($_SERVER["DOCUMENT_ROOT"] == '/home/whmcs/domain1.com') {
        include('configuration_domain1.com.php');
}
if ($_SERVER["DOCUMENT_ROOT"] == '/home/whmcs/domain2.com') {
        include('configuration_domain2.com.php');
}

– Create and edit your new configuration files
– – Edit the settings as necessary, but make sure to use domain1.com and domain2.com where specified

/home/whmcs/configuration_domain1.com.php

$license="";
$db_host = "";
$db_username = "";
$db_password = "";
$db_name = "whmcs_domain1";
$cc_encryption_hash = ""; 
$templates_compiledir = "/home/whmcs/domain1.com/templates_domain1_c";
$api_access_key = "";
session_name("WHMCS_DOMAIN1");
$display_errors = true;

/home/whmcs/configuration_domain2.com.php

$license="";
$db_host = "";
$db_username = "";
$db_password = "";
$db_name = "whmcs_domain2";
$cc_encryption_hash = ""; 
$templates_compiledir = "/home/whmcs/domain2.com/templates_domain2_c";
$api_access_key = "";
session_name("WHMCS_DOMAIN2");
$display_errors = true;

– Create template caching dirs for the domains, then chmod 777
– – Due to the WHMCS’s devs wisdom, they require the directory to be 777.

# mkdir /home/whmcs/master/templates_domain1_c
# mkdir /home/whmcs/master/templates_domain2_c
# chmod 777 /home/whmcs/master/templates_domain1_c
# chmod 777 /home/whmcs/master/templates_domain2_c

 

Now your configuration and directory structure is completed. WHMCS is now ready for both domains to work from one installation.

The easiest part of this is setting up the web server. You can use anything, Nginx, Apache, etc. All you do is setup the document root to the symbolic links you created.

Your directory structure should look something like this.

# ll /home/whmcs
drwxr-xr-x 21 whmcs whmcs    4096 Jan 21 13:49 master
lrwxrwxrwx  1 root  root        7 Dec 11 11:41 domain1 -> master/
lrwxrwxrwx  1 root  root        7 Dec 11 11:41 domain2 -> master/

 

Just to give you an idea, this is what an Nginx configuration could look like.

server {
    listen       123.456.789.1:443;
    server_name  domain1.com;
    root /home/whmcs/domain1.com;
    access_log /var/log/nginx/domain1.com-access.log;
    error_log /var/log/nginx/domain1.com-error.log;
    ...more stuff below...
}
server {
    listen       123.456.789.2:443;
    server_name  domain2.com;
    root /home/whmcs/domain2.com;
    access_log /var/log/nginx/domain2.com-access.log;
    error_log /var/log/nginx/domain2.com-error.log;
    ...more stuff below...
}

 

Now when you visit domain1.com or domain2.com they will be accessing the same WHMCS installation. You can change the themes and templates as needed through the admin interface.

For cron job setup, make sure to use WGET with the full domain name so that each configuration is populated correctly.

Cron example:

0 8 * * *  /usr/bin/wget -O /dev/null https://domain1.com/admin/cron.php >/dev/null 2>&1
0 9 * * *  /usr/bin/wget -O /dev/null https://domain2.com/admin/cron.php >/dev/null 2>&1

 

This setup works flawlessly. We’ve had zero problems and have gone through two WHMCS updates already.

Source

Linux Static IP Address Configuration

How do I configure the Internet Protocol version 4 (IPv4) properties of a network connection with a static IP address for servers running Linux operating systems? How do I configure static IP address under Debian Linux or Redhat / RHEL / Fedora / Redhat Enterprise Linux server?

You need to update and/or edit the network configuration files. This tutorial provides procedures to configure a static IP address on a computer running the following operating systems:

  1. RHEL / Red hat / Fedora / CentOS Linux eth0 config file – /etc/sysconfig/network-scripts/ifcfg-eth0
  2. RHEL / Red hat / Fedora / CentOS Linux eth1 config file – /etc/sysconfig/network-scripts/ifcfg-eth1
  3. Debian / Ubuntu Linux – /etc/network/interfaces

Sample Setup: Linux Static TCP/IP Settings

In this example you will use the following Internet Protocol Version 4 (TCP/IPv4) Properties including IP, default gateway, and preferred DNS servers:

  • IP address: 192.168.1.10
  • Netmask: 255.255.255.0
  • Hostname: server1.shineservers.com
  • Domain name: shineservers.com
  • Gateway IP: 192.168.1.254
  • DNS Server IP # 1: 192.168.1.254
  • DNS Server IP # 2: 8.8.8.8
  • DNS Server IP # 3: 202.54.2.5

RHEL / Red hat / Fedora / CentOS Linux Static IP Configuration

For static IP configuration you need to edit the following files using a text editor such as vi. Edit /etc/sysconfig/network as follows, enter:
# cat /etc/sysconfig/network
Sample static ip configuration:

 
NETWORKING=yes
HOSTNAME=server1.shineservers.com
GATEWAY=192.168.1.254

Edit /etc/sysconfig/network-scripts/ifcfg-eth0, enter:
# cat /etc/sysconfig/network-scripts/ifcfg-eth0
Sample static ip configuration:

 
# Intel Corporation 82573E Gigabit Ethernet Controller (Copper)
DEVICE=eth0
BOOTPROTO=static
DHCPCLASS=
HWADDR=00:30:48:56:A6:2E
IPADDR=192.168.1.10
NETMASK=255.255.255.0
ONBOOT=yes

Edit /etc/resolv.conf and setup DNS servers, enter:
# cat /etc/resolv.conf
Sample static IP configurations:

 
search shineservers.com
nameserver 192.168.1.254
nameserver 8.8.8.8
nameserver 202.54.2.5

Finally, you need to restart the networking service, enter:
# /etc/init.d/network restart
To verify new static ip configuration for eth0, enter:
# ifconfig eth0
# route -n
# ping 192.168.1.254
# ping google.com

Debian / Ubuntu Linux Static IP Configuration

Edit /etc/hostname, enter:
# cat /etc/hostname
Sample ip config:

 
server1.cyberciti.biz

Edit /etc/network/interfaces, enter:
# cat /etc/network/interfaces
Sample static ip config:

 
iface eth0 inet static
     address 192.168.1.10
     network 192.168.1.0
     netmask 255.255.255.0
     broadcast 192.168.1.255
     gateway 192.168.1.254

Edit /etc/resolv.conf and setup DNS servers, enter:
# cat /etc/resolv.conf
Sample dns static IP configurations:

 
search shineservers.com
nameserver 192.168.1.254
nameserver 8.8.8.8
nameserver 202.54.2.5

Finally, you need to restart the networking service under Debian / Ubuntu Linux, enter:
# /etc/init.d/networking restart

Type the following commands to verify your new setup, enter:
# ifconfig eth0
# route -n
# ping google.com

How to install VNC server on Ubuntu Server 12.04

VNC is a protocol that is used to share the desktop with other users/computers over the network/Internet.In order to share a desktop, VNC server must be install and configure on the computer and VNC client must be run on the computer that will access the shared desktop.

When we install the fresh copy of Ubuntu Server, it only gives us the “Command Line” interface.

But some people prefer GUI instead and for this they install Full version of Gnome on Ubuntu Server. Actually there is a better way and that is to install VNC. VNC provides a lightweight virtual desktop than full blown version of Gnome.

To install the core components of gnome, use this command:

sudo apt-get install gnome-core

To install a virtual desktop, use this command:

sudo apt-get install vnc4server

In order to use VNC, we need to setup a password using the following command:

vncserver

To make a tweak in startup script, we need to kill the session that we just created:

vncserver -kill :1

Now open up the file that we need to edit:

cd ~
nano .vnc/xstartup

And Modify the file so it looks like this:

#!/bin/sh
# Uncomment the following two lines for normal desktop:
unset SESSION_MANAGER
#exec /etc/X11/xinit/xinitrc
gnome-session --session=gnome-classic &

[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
#x-terminal-emulator -geometry 1280x1024+10+10 -ls -title "$VNCDESKTOP Desktop" &
#x-window-manager &

Next, create the VNC Session once more:

vncserver -geometry 1024x600

Now, download VNCViewer onto our desktop computer from which we want to access the shared desktop. Connect using ServerIP/Name:1 (:1 is for the VNC server window), In my case it is tendo:1.

Enter the password that we created using the vncserver command:

We now have GUI access to our server.

After reboot the server, we will not be able to connect to the server with VNC, this is because the “vncserver -geometry 1024×600” command that we typed above is not persistent. To solve this problem, we will use an excellent script ofJustin Buser.

As sudo user create the file (and directory if it doesn’t exist):

sudo mkdir -p /etc/vncserver
sudo touch /etc/vncserver/vncservers.conf
sudo nano /etc/vncserver/vncservers.conf

Add servers as needed for each user by adding something like the following to the vncservers.conf file we just created:

VNCSERVERS="1:arbab"
VNCSERVERARGS[1]="-geometry 1024x600 -depth 24"

Next, create an empty init script and make it executable:

sudo touch /etc/init.d/vncserver
sudo chmod +x /etc/init.d/vncserver
sudo nano /etc/init.d/vncserver

Add the following to /etc/init.d/vncserver:

#!/bin/bash
unset VNCSERVERARGS
VNCSERVERS=""
[ -f /etc/vncserver/vncservers.conf ] && . /etc/vncserver/vncservers.conf
prog=$"VNC server"
start() {
 . /lib/lsb/init-functions
 REQ_USER=$2
 echo -n $"Starting $prog: "
 ulimit -S -c 0 >/dev/null 2>&1
 RETVAL=0
 for display in ${VNCSERVERS}
 do
 export USER="${display##*:}"
 if test -z "${REQ_USER}" -o "${REQ_USER}" == ${USER} ; then
 echo -n "${display} "
 unset BASH_ENV ENV
 DISP="${display%%:*}"
 export VNCUSERARGS="${VNCSERVERARGS[${DISP}]}"
 su ${USER} -c "cd ~${USER} && [ -f .vnc/passwd ] && vncserver :${DISP} ${VNCUSERARGS}"
 fi
 done
}
stop() {
 . /lib/lsb/init-functions
 REQ_USER=$2
 echo -n $"Shutting down VNCServer: "
 for display in ${VNCSERVERS}
 do
 export USER="${display##*:}"
 if test -z "${REQ_USER}" -o "${REQ_USER}" == ${USER} ; then
 echo -n "${display} "
 unset BASH_ENV ENV
 export USER="${display##*:}"
 su ${USER} -c "vncserver -kill :${display%%:*}" >/dev/null 2>&1
 fi
 done
 echo -e "\n"
 echo "VNCServer Stopped"
}
case "$1" in
start)
start [email protected]
;;
stop)
stop [email protected]
;;
restart|reload)
stop [email protected]
sleep 3
start [email protected]
;;
condrestart)
if [ -f /var/lock/subsys/vncserver ]; then
stop [email protected]
sleep 3
start [email protected]
fi
;;
status)
status Xvnc
;;
*)
echo $"Usage: $0 {start|stop|restart|condrestart|status}"
exit 1
esac

We’ll need to run vncserver command AT LEAST ONCE AS EACH USER that want to login as. I put that in caps because if you skip that step none of it will work.

Finally, do the following:

sudo update-rc.d vncserver defaults 99

Now, restart the service by typing:

sudo service vncserver restart

Ability to connect for multiple users:

Create a local user, using the following command:

sudo adduser hussain

Switch to the newly created user and run vncserver command for it:

su hussain
vncserver

Move to the home directory and edit the xstartup file:

cd ~
nano .vnc/xstartup

Modify the file so it looks like this:

 
#!/bin/sh
# Uncomment the following two lines for normal desktop:
unset SESSION_MANAGER
#exec /etc/X11/xinit/xinitrc
gnome-session --session=gnome-classic &[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
#x-terminal-emulator -geometry 1280x1024+10+10 -ls -title "$VNCDESKTOP Desktop" &
#x-window-manager &

Now open up the /etc/vncserver/vncservers.conf file as sudo user:

sudo nano /etc/vncserver/vncservers.conf

Add servers for newly created user by adding something like this:

VNCSERVERS="1:arbab 2:hussain"
VNCSERVERARGS[1]="-geometry 1024x600 -depth 24"
VNCSERVERARGS[2]="-geometry 1024x600 -depth 24"

Restart the service:

sudo service vncserver restart

Connect with newly created user using tendo:2, Where tendo is my server name:

Enter the password that we created using the vncserver command:

We now have GUI access to our server for newly created user.

Preventing Gnome to start on boot on the server:

Gnome is automatically started on boot in Ubuntu 12.04 LTS, if we connect a monitor to our server we will see that GUI sitting there waiting for us to log in.

To prevent it, edit the gdm.conf file:

sudo nano /etc/init/gdm.conf

Comment these six lines:

#start on ((filesystem
# and runlevel [!06]
# and started dbus
# and (drm-device-added card0 PRIMARY_DEVICE_FOR_DISPLAY=1
# or stopped udev-fallback-graphics))
# or runlevel PREVLEVEL=S)

Reboot the server and that GUI log-in screen will no longer appear:

VNC encrypted through the ssh tunnel:

By default, VNC is not secure protocol.VNC uses encryption during initial connection and login (passwords are not sent in plain-text). Once, we connected then all the VNC data is unencrypted and hacker could sniff our VNC session. It is better (safer) to start VNC server only on 127.0.0.1(localhost) and tunnel it over secure SSH tunnel (For this,there are options in Putty).

On Ubuntu, edit /etc/vncserver/vncservers.conf:

sudo nano /etc/vncserver/vncservers.conf

Add the option “-localhost“:

VNCSERVERS="1:arbab 2:hussain"
VNCSERVERARGS[1]="-geometry 1024x600 -depth 24 -localhost"
VNCSERVERARGS[2]="-geometry 1024x600 -depth 24 -localhost"

Restart the service:

sudo service vncserver restart

Here is visual, how to connect to VNC Server through PuTTY(SSH) from Windows Machine.

Run PuTTY,enter the IP address or hostname of the VNC Server:

On the left-hand panel, Go to Connection -> SSH -> Tunnels:

Source Port:590x(Where x is a value that we set in vncservers.conf,like 1 for arbab)
Destination:localhost:590x(Same x value that we used above in source port)

Click Open button in order to connect to the Server via SSH:

Login to the Ubuntu (VNC Server) with username and password:

Upon successful connection to VNC Server, we’ll find port 5901 is in listening mode on localhost:

netstat -a

Run VNC Viewer and enter the localhost:1(:1 is for arbab user, that we defined in vncservers file):

Enter the password, in order to connect to the VNC Server:

Now, we are connected to remote VNC Server through ssh tunnel:

Hope this will help you!

Protect Apache using Mod_evasive on RHEL/CentOS & Fedora

How to Install Mod_Evasive in RHEL/CentOS & Fedora

As we already installed required dependency packages above, so let’s install the mod_evasive module.

Step 1: Installing Mod_Evasive

Just run the following commands to install mod_evasive.

## For RHEL/CentOS 6.2/6.1/6/5.8 ##
# cd /usr/src 
# wget http://www.zdziarski.com/blog/wp-content/uploads/2010/02/mod_evasive_1.10.1.tar.gz
# tar xzf mod_evasive_1.10.1.tar.gz
# cd mod_evasive
# apxs -cia mod_evasive20.c## For Fedora 17,16,15,14,13,12 ##
# cd /usr/src 
# wget http://www.zdziarski.com/blog/wp-content/uploads/2010/02/mod_evasive_1.10.1.tar.gz
# tar xzf mod_evasive_1.10.1.tar.gz
# cd mod_evasive
# apxs -cia mod_evasive20.c

Step 2: Configuring Mod_Evasive

By default installation adds the following line of mod_evasive configuration to your Apache configuration file. Please verify that it should be there like similar to below. If you can’t see this below line, then add this to your httpd.conf file.

LoadModule evasive20_module   /usr/lib/httpd/modules/mod_evasive20.so

Now add the mod_evasive configuration parameters to your Apache configuration at the end. Replace [email protected] with your Email Id to get email alerts.

<IfModule mod_evasive20.c>
DOSHashTableSize    3097
DOSPageCount        2
DOSSiteCount        50
DOSPageInterval     1
DOSSiteInterval     1
DOSBlockingPeriod   60
DOSEmailNotify [email protected]
</IfModule>

Next restart the Apache service to update changes.

# /etc/init.d/httpd restart

For more additional information visit the mod_evasive Home Page.

Please drop your comments for any queries on installation, we will love to help you out and don’t forget to Subscribe to our Updates.

Protect Apache using Mod_Security on RHEL/CentOS & Fedora

These two great security modules protect Apache server from brute force attacks and DOS attacks. Before, moving for further installation guide, we would like to provide you a little description on these tow modules.

What is Mod_Security?

Mod_Security is an open source web application firewall (WAF) and intrusion detection and prevention system for web applications. It is used to protect and monitor real time HTTP traffic and web applications from brute fore attacks.

What is Mod_Evasive?

Mod_Evasive is an open source evasive maneuvers system for Apache server to provide evasive action in the event of an HTTP brute force, Dos or DDos attack. It was designed to use as a network traffic detection and network management tool and can be easily configured and integrated into firewalls, ipchains, routers etc. Presently, it sends abuses reports via email and syslog facilites.

Install Mod_Security and Mod_evasive on RHEL 6.2/6.1/6/5.8CentOS 6.2/6.1/6/5.8 and Fedora 17,16,15,14,13,12

How to Install Mod_Security on RHEL/CentOS & Fedora

Step 1: Installing Dependencies for mod_security

Firstly, we required to install some dependency packages for mod_security. Run the following commands on your selected OS.

## For RHEL/CentOS 6.2/6.1/6/5.8 ##
# yum install gcc make
# yum install libxml2 libxml2-devel httpd-devel pcre-devel curl-devel

## For Fedora 17,16,15,14,13,12 ##
# yum install gcc make
# yum install libxml2 libxml2-devel httpd-devel pcre-devel curl-devel

Step 2: Installing Mod_Security

As I said above that we use source code to install mod_security. Run the following commands as root.

## For RHEL/CentOS 6.2/6.1/6/5.8 ##
# cd /usr/src
# wget http://www.modsecurity.org/download/modsecurity-apache_2.6.6.tar.gz
# tar xzf modsecurity-apache_2.6.6.tar.gz
# cd modsecurity-apache_2.6.6
# ./configure
# make install
# cp modsecurity.conf-recommended /etc/httpd/conf.d/modsecurity.conf

## For Fedora 17,16,15,14,13,12 ##
# cd /usr/src
# wget http://www.modsecurity.org/download/modsecurity-apache_2.6.6.tar.gz
# tar xzf modsecurity-apache_2.6.6.tar.gz
# cd modsecurity-apache_2.6.6
# ./configure
# make install
# cp modsecurity.conf-recommended /etc/httpd/conf.d/modsecurity.conf

Step 3: Downloading OWASP Mod_Security Core Rule Set

Mod_Security requires OWASP (Open Web Application Security Project) core rules for base configuration, these rules are used to protect from unknown vulnerabilities which often found on web applications. So, here we are going to download and install rule set for mod_security. Run the following commands.

## For RHEL/CentOS 6.2/6.1/6/5.8 ##
# cd /etc/httpd/
# wget http://pkgs.fedoraproject.org/repo/pkgs/mod_security_crs/modsecurity-crs_2.2.5.tar.gz/aaeaa1124e8efc39eeb064fb47cfc0aa/modsecurity-crs_2.2.5.tar.gz
# tar xzf modsecurity-crs_2.2.5.tar.gz
# mv modsecurity-crs_2.2.5 modsecurity-crs
# cd modsecurity-crs
# cp modsecurity_crs_10_setup.conf.example modsecurity_crs_10_config.conf## For Fedora 17,16,15,14,13,12 ##
# cd /etc/httpd/
# wget http://pkgs.fedoraproject.org/repo/pkgs/mod_security_crs/modsecurity-crs_2.2.5.tar.gz/aaeaa1124e8efc39eeb064fb47cfc0aa/modsecurity-crs_2.2.5.tar.gz
# tar xzf modsecurity-crs_2.2.5.tar.gz
# mv modsecurity-crs_2.2.5 modsecurity-crs
# cd modsecurity-crs
# cp modsecurity_crs_10_setup.conf.example modsecurity_crs_10_config.conf

Step 4: Configuring Mod_Security

Now, you need to modify your Apache configuration file to load the mod_security module.

# vi /etc/httpd/conf/httpd.conf

Search for the line LoadModule in your httpd.conf and add this below line at the bottom.

LoadModule security2_module modules/mod_security2.so

Now set the basic rule set in your httpd.conf file. Add the following lines of code at the end of the file.

<IfModule security2_module>
    Include conf.d/modsecurity.conf
 </IfModule>

Next, restart the Apache service to enable mod_security module and their rules.

# /etc/init.d/httpd restart

For more information on this topic visit the following links for your reference.

  1. ModSecurity Home Page
  2. OWASP ModSecurity Core Rule Set

The above installation is tested on CentOS 5.6 and successfully worked for me, I hope it will also work for you, now let’s move further installation of mod_evasive module.

Optimise cPanel/WHM Server

In the optimization process we will go over the Apache core configuration and modules that are part of Apache core. We think that with the correct settings of Apache and MySQL you can get excellent results and the correct level of resource use without installing third-party proxy and cache modules. So let’s start,

 

In the first stage we run the Easy Apache and selected the following:

* Apache Version 2+

* PHP Version 5.3+

* In step 5 “Exhaustive Options List” select

– Deflate

– Expires

– MPM Prefork

– MPM Worker

After Easy Apache finished go to your WHM » Service Configuration » Apache Configuration » “Global Configuration” and set the values by the level of resources available on your server.

Apache Directive 	 	(From 2GB memory or less and up to 12GB memory) 	 	

StartServers 	 	 	4 	 	8 	 	16 	
MinSpareServers 	 	4 	 	8 	 	16 	
MaxSpareServers 	 	8 	 	16 	 	32 	
ServerLimit 	 	 	64 	 	128 	 	256 	
MaxClients 	 	 	50 	 	120 	 	250 	
MaxRequestsPerChild 	 	1000 	 	2500 	 	5000 
Keep-Alive			On		On		On
Keep-Alive Timeout	 	5	 	5	 	 5
Max Keep-Alive Requests		50	 	120	 	120
Timeout				30		60		60

 

Now go to WHM » Service Configuration » Apache Configuration » Include Editor » “Pre VirtualHost Include” and allow users minimal cache and data compression to allow the server to work less for the same things by pasting the code below into the text field.

# Cache Control Settings for one hour cache
<FilesMatch ".(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">
Header set Cache-Control "max-age=3600, public"
</FilesMatch>

<FilesMatch ".(xml|txt)$">
Header set Cache-Control "max-age=3600, public, must-revalidate"
</FilesMatch>

<FilesMatch ".(html|htm)$">
Header set Cache-Control "max-age=3600, must-revalidate"
</FilesMatch>

# Mod Deflate performs data compression
<IfModule mod_deflate.c>
<FilesMatch ".(js|css|html|php|xml|jpg|png|gif)$">
SetOutputFilter DEFLATE
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4.0[678] no-gzip
BrowserMatch bMSIE no-gzip
</FilesMatch>
</IfModule>

 

For MySQL you need to update the configuration file that usually in /etc/my.cnf

Best config base on 1 core & 2GB memory MySQL 5.1:

[mysqld]
    local-infile = 0
    max_connections = 250
    key_buffer = 64M
    myisam_sort_buffer_size = 64M
    join_buffer_size = 1M
    read_buffer_size = 1M
    sort_buffer_size = 2M
    max_heap_table_size = 16M
    table_cache = 5000
    thread_cache_size = 286
    interactive_timeout = 25
    wait_timeout = 7000
    connect_timeout = 15
    max_allowed_packet = 16M
    max_connect_errors = 10
    query_cache_limit = 2M
    query_cache_size = 32M
    query_cache_type = 1
    tmp_table_size = 16M
[mysqld_safe]
    open_files_limit = 4096
[mysqldump]
    quick
    max_allowed_packet = 16M
[myisamchk]
    key_buffer = 64M
    sort_buffer = 64M
    read_buffer = 16M
    write_buffer = 16M
[mysqlhotcopy]
    interactive-timeout

 

Best config base on 8 core & 12GB memory MySQL 5.1:

[mysqld]
local-infile=0
safe-show-database
default-character-set=utf8
default-collation=utf8_general_ci
max_connections = 600
max_user_connections=100
key_buffer_size = 512M
myisam_sort_buffer_size = 64M
read_buffer_size = 1M
table_open_cache = 5000
thread_cache_size = 384
wait_timeout = 20
connect_timeout = 10
tmp_table_size = 256M
max_heap_table_size = 128M
max_allowed_packet = 64M
net_buffer_length = 16384
max_connect_errors = 10
concurrent_insert = 2
#table_lock_wait_timeout only for mysql5
table_lock_wait_timeout = 10
read_rnd_buffer_size = 786432
bulk_insert_buffer_size = 8M
query_cache_limit = 5M
query_cache_size = 128M
query_cache_type = 1
query_prealloc_size = 262144
query_alloc_block_size = 65536
transaction_alloc_block_size = 8192
transaction_prealloc_size = 4096
max_write_lock_count = 8
slow_query_log
external-locking=FALSE
open_files_limit=50000

[mysqldump]
quick
max_allowed_packet = 16M

[isamchk]
key_buffer = 384M
sort_buffer = 384M
read_buffer = 256M
write_buffer = 256M

[myisamchk]
key_buffer = 384M
sort_buffer = 384M
read_buffer = 256M
write_buffer = 256M#### Per connection configuration ####
sort_buffer_size = 1M
join_buffer_size = 1M
thread_stack = 192K

 

Then restart MySQL and the last step is limiting the use of resources.

Go to WHM » Service Configuration » “PHP Configuration Editor” and set the parameters according to your needs:

– memory_limit

– max_execution_time

– max_input_time

 

Install CSF (ConfigServer Security & Firewall) at: http://configserver.com/free/csf/install.txt

And go to WHM » Plugins » ConfigServer Security & Firewall » “Firewall Configuration” and set the parameters according to your needs:

PT_USERMEM=180

PT_USERTIME=180

PT_USERKILL=1

PT_USERKILL_ALERT=1 (Optional)

 

Now enjoy your new fast and more effective server.

Optimise and Tweak High-Traffic Servers

Summary
If you are reaching the limits of your server running Apache serving a lot of dynamic content, you can either spend thousands on new equipment or reduce bloat to increase your server capacity by anywhere from 2 to 10 times. This article concentrates on important and poorly-documented ways of increasing capacity without additional hardware.

Problems
There are a few common things that can cause server load problems, and a thousand uncommon. Let’s focus on the common:
Drive Swapping – too many processes (or runaway processes) using too much RAM
CPU – poorly optimized DB queries, poorly optimized code, runaway processes
Network – hardware limits, moron attacks

Solutions: The Obvious
Briefly, and for completeness, here are the most obvious solutions:

Use “TOP” and “PS axu” to check for processes that are using too much CPU or RAM.
Use “netstat -anp | sort -u” to check for network problems.

Solutions:

Apache’s RAM Usage
First and most obvious, Apache processes use a ton a RAM. This minor issue becomes a major issue when you realize that after each process has done its job, the bloated process sits and spoon-feed data to the client, instead of moving on to bigger and better things. This is further compounded by a bit of essential info that should really be more common knowledge:

If you serve 100% static files with Apache, each httpd process will use around 2-3 megs of RAM.
If you serve 99% static files & 1% dynamic files with Apache, each httpd process will use from 3-20 megs of RAM (depending on your MOST complex dynamic page).

This occurs because a process grows to accommodate whatever it is serving, and NEVER decreases again unless that process happens to die. Quickly, unless you have very few dynamic pages and major traffic fluctuation, most of your httpd processes will take up an amount of RAM equal to the largest dynamic script on your system. A smart web server would deal with this automatically. As it is, you have a few options to manually improve RAM usage.

Reduce wasted processes by tweaking KeepAlive
This is a tradeoff. KeepAliveTimeout is the amount of time a process sits around doing nothing but taking up space. Those seconds add up in a HUGE way. But using KeepAlive can increase speed for both you and the client – disable KeepAlive and the serving of static files like images can be a lot slower. I think it’s best to have KeepAlive on, and KeepAliveTimeout very low (like 1-2 seconds).

Limit total processes with MaxClients
If you use Apache to serve dynamic content, your simultaneous connections are severely limited. Exceed a certain number, and your system begins cannibalistic swapping, getting slower and slower until it dies. IMHO, a web server should automatically take steps to prevent this, but instead they seem to assume you have unlimited resources. Use trial & error to figure out how many Apache processes your server can handle, and set this value in MaxClients. Note: the Apache docs on this are misleading – if this limit is reached, clients are not “locked out”, they are simply queued, and their access slows. Based on the value of MaxClients, you can estimate the values you need for StartServers, MinSpareServers, & MaxSpareServers.

Force processes to reset with MaxRequestsPerChild
Forcing your processes to die after a while makes them start over with low RAM usage, and this can reduce total memory usage in many situations. The less dynamic content you have, the more useful this will be. This is a game of catch-up, with your dynamic files constantly increasing total RAM usage, and restarting processes constantly reducing it. Experiment with MaxRequestsPerChild – even values as low as 20 may work well. But don’t set it too low, because creating new processes does have overhead. You can figure out the best settings under load by examining “ps axu –sort:rss”. A word of warning, using this is a bit like using heroin. The results can be impressive, but are NOT consistent – if the only way you can keep your server running is by tweaking this, you will eventually run into trouble. That being said, by tweaking MaxRequestsPerChild you may be able to increase MaxClients as much as 50%.

Apache Further Tweaking
For mixed purpose sites (say image galleries, download sites, etc.), you can often improve performance by running two different apache daemons on the same server. For example, we recently compiled apache to just serve up images (gifs,jpegs,png etc). This way for a site that has thousands of stock photos. We put both the main apache and the image apache on the same server and noticed a drop in load and ram usage. Consider a page had about 20-50 image calls — the were all off-loaded to the stripped down apache, which could run 3x more servers with the same ram usage than the regular apache on the server.

Finally, think outside the box: replace or supplement Apache

Use a 2nd server
You can use a tiny, lightning fast server to handle static documents & images, and pass any more complicated requests on to Apache on the same machine. This way Apache won’t tie up its multi-megabyte processes serving simple streams of bytes. You can have Apache only get used, for example, when a php script needs to be executed. Good options for this are:

TUX / “Red Hat Content Accelerator” – http://www.redhat.com/docs/manuals/tux/
kHTTPd – http://www.fenrus.demon.nl/
thttpd – http://www.acme.com/software/thttpd/

Try lingerd
Lingerd takes over the job of feeding bytes to the client after Apache has fetched the document, but requires kernel modification. Sounds pretty good, haven’t tried it. lingerd – http://www.iagora.com/about/software/lingerd/

Use a proxy cache
A proxy cache can keep a duplicate copy of everything it gets from Apache, and serve the copy instead of bothering Apache with it. This has the benefit of also being able to cache dynamically generated pages, but it does add a bit of bloat.

Replace Apache completely
If you don’t need all the features of Apache, simply replace it with something more scalable. Currently, the best options appear to be servers that use a non-blocking I/O technology and connect to all clients with the same process. That’s right – only ONE process. The best include:

thttpd – http://www.acme.com/software/thttpd/
Caudium – http://caudium.net/index.html
Roxen – http://www.roxen.com/products/webserver/
Zeus ($$) – http://www.zeus.co.uk

Solutions:

PHP’s CPU & RAM Usage
Compiling PHP scripts is usually more expensive than running them. So why not use a simple tool that keeps them precompiled? I highly recommend Turck MMCache. Alternatives include PHP Accelerator, APC, & Zend Accelerator. You will see a speed increase of 2x-10x, simple as that. I have no stats on the RAM improvement at this time.

Solutions:

Optimize Database Queries
This is covered in detail everywhere, so just keep in mind a few important notes: One bad query statement running often can bring your site to its knees. Two or three bad query statements don’t perform much different than one. In other words, if you optimize one query you may not see any server-wide speed improvement. If you find & optimize ALL your bad queries you may suddenly see a 5x server speed improvement. The log-slow-queries feature of MySQL can be very helpful.

How to log slow queries:

# vi /etc/rc.d/init.d/mysqld

Find this line:
SAFE_MYSQLD_OPTIONS=”–defaults-file=/etc/my.cnf”

change it to:
SAFE_MYSQLD_OPTIONS=”–defaults-file=/etc/my.cnf –log-slow-queries=/var/log/slow-queries.log”

As you can see, we added the option of logging all slow queries to /var/log/slow-queries.log
Close and save mysqld. Shift + Z + Z

touch /var/log/slow-queries.log
chmod 644 /var/log/slow-queries.log

restart mysql
service myslqd restart
mysqld will log all slow queries to this file.

References
These sites contain additional, more well known methods for optimization.

Tuning Apache and PHP for Speed on Unix – http://php.weblogs.com/tuning_apache_unix
Getting maximum performance from MySQL – http://www.f3n.de/doku/mysql/manual_10.html
System Tuning Info for Linux Servers – http://people.redhat.com/alikins/system_tuning.html
mod_perl Performance Tuning (applies outside perl) – http://perl.apache.org/docs/1.0/guide/performance.html

Once again, if this has any errors or important omissions, please let me know and I will correct them.
If you experience a capacity increase on your server after trying the optimizations, let me know!