Skip to content

Battling with Apache and PassEnv

Dear Reader,

I’ve been managing Apache servers since 1999 and while I consider myself competent, I never processed to be an expert. So it really didn’t surprise me yesterday when I learned something new.

A buddy of mine was discussing a problem he was having with his corporate web site. It’s hosted on multiple web servers behind a load balancer. The problem is, they are having problems with one of the web servers but they can’t figure out which. I remember that back at Jupiter, we had a similar problem and that one of my guys there injected a header that allowed us to track which server a specific request was coming from. Unfortunately for my friend, that was about as far as I remembered. (I was management then, I didn’t pay attention to details) However, he’s a bright guy and had already thought of this…the difference was, he knew basically how to do it.

I started playing with ideas on my development server and got about 75% of the way there when he IMed me that he had it working. (So much for speed) I fired up FireFox and TamperData and sure enough, he had a custom header in there. (BTW, TamperData is invaluable for debugging things like this.) However, when I followed his direction, it just was not happening for me. Googling around found me a LOT of copies of the Apache manual, but no concrete examples. So, since I couldn’t find the answer on the web, I decided to post how I did this in case some other Apache noob comes looking for it.

First, an important detail, my development environment is a customized version of CentOS 5. I use yum for just about everything, except Apache, PHP, MySQL and their support programs. I use a script that comes with DirectAdmin, my production control panel of choice, to maintain those pieces. The important thing to note here is that CentOS does NOT use apachectrl to start and stop apache.

The answer to inserting a header, of course, revolves around PassEnv. If you have mod_env installed in your Apache 1.3.7 or Apache 2.x, this will work.

I’m working with Virtual Hosts and each development site has it’s on conf file that is included into the main httpd.conf. The great thing about PassEvn though is you can put it in an .htaccess file as well. This means for testing, you don’t have to constantly be bouncing the service.

To identify my server, I decided I wanted to add a header that displayed the host name that the page was being served from. This means in my conf file or .htaccess, I need the following:

Header set X-MyHeader "%{HOSTNAME}e"

That gets us about 75% there. The PassEnv makes the HOSTNAME environment variable available to APache and the Header command actually sets the new header. “%{VARAIABLENAME}e” is the syntax for displaying the variable in the header.

All that is fine and good and if you drop that in your .htaccess file and then hit a page on the site, you will get:

X-MyHeader (null)

Now, my friend said he added:

export HOSTNAME=`hostname`

to his apachectrl script and all was good. I tried this and (if you read the note above, you see this coming) it did not fix the problem.

Digging deeper, I found that apachectrl sources a file /usr/sbin/envvars. Looking it, it’s obvious that this is where they expect you to put these commands so you don’t have to have a customized version of apachectrl. So I took my export out of apachectrl and put it in envvars. Still no dice, yeah, I know, you expected this.

The problem, as I explained above, is that if you are using “service httpd start” on CentOS then apachectrl is being ignored. Lickily for us, /etc/init.d/httpd is another bash script. So I grabbed the lines out of apachectrl that sourced /usr/sbin/envvars, inserted them in /etc/init.d/httpd and BINGO, we have a header.

if test -f /usr/sbin/envvars; then
  . /usr/sbin/envvars

I put them high up in the file, near where it sources the functions file.

So, it is possible, and even easy to add custom headers into Apache that include environment variables. You need to make sure that mod_env and mod_headers are both installed and the rest is just figuring out where to put things.

UPDATE: Since I was asked, yes you can access this new information in PHP via the $_ENV super global.

A var_dump of the $_ENV on my development server now looks like this:

array(8) {
  string(5) "david"
  string(5) "xterm"
  string(15) "/etc/httpd/lib:"
  string(29) "/sbin:/usr/sbin:/bin:/usr/bin"
  string(1) "/"
  string(11) "en_US.UTF-8"
  string(1) "2"
  string(15) "/usr/sbin/httpd"

So you can see, you can see that while the Header X-MyHeader is not exposed, the variable it contains, HOSTNAME, is.

For the record, hostname was probably not the best example since it’s usually in the $_SERVER array. However, the point of the experiment was not to expose this information to PHP, it was to put it in the response form the server.

Until next time,