Mod Perl Icon Mod Perl Icon Server Configuration


[ Prev | Main Page | Next ]

Table of Contents:


The Writing Apache Modules with Perl and C book can be purchased online from O'Reilly and Amazon.com.
Your corrections of either technical or grammatical errors are very welcome. You are encouraged to help me to improve this guide. If you have something to contribute please send it directly to me.

[TOC]


mod_perl Specific Configuration

The next step after building and installing your new mod_perl enabled apache server, is to configure the server. To learn how to modify apache's configuration files, please refer to the documentation included with the apache distribution, or just view the files in conf directory and follow the instructions in these files - the embedded comments within the file do a good job of explaining the options.

Before you start with mod_perl specific configuration, first configure apache, and see that it works. When done, return here to continue...

[ Note that prior to version 1.3.4, the default apache install used three configuration files -- httpd.conf, srm.conf, and access.conf. The 1.3.4 version began distributing the configuration directives in a single file -- httpd.conf. The remainder of this chapter refers to the location of the configuration directives using their historical location. ]

[TOC]


Alias Configurations

First, you need to specify the locations on a file-system for the scripts to be found.

Add the following configuration directives:

    # for plain cgi-bin:
  ScriptAlias /cgi-bin/ /usr/local/myproject/cgi/
    
    # for Apache::Registry mode
  Alias /perl/ /usr/local/myproject/cgi/
    
    # Apache::PerlRun mode
  Alias /cgi-perl/ /usr/local/myproject/cgi/

Alias provides a mapping of URL to file system object under mod_perl. ScriptAlias is being used for mod_cgi.

Alias defines the start of the URL path to the script you are referencing. For example, using the above configuration, fetching http://www.nowhere.com/perl/test.pl, will cause the server to look for the file test.pl at /usr/local/myproject/cgi, and execute it as an Apache::Registry script if we define Apache::Registry to be the handler of /perl location (see below). The URL http://www.nowhere.com/perl/test.pl will be mapped to /usr/local/myproject/cgi/test.pl. This means that you can have all your CGIs located at the same place in the file-system, and call the script in any of three modes simply by changing the directory name component of the URL (cgi-bin|perl|cgi-perl) - is not this neat? (That is the configuration you see above - all three Aliases point to the same directory within your file system, but of course they can be different). If your script does not seem to be working while running under mod_perl, you can easily call the script in straight mod_cgi mode without making any script changes (in most cases), but rather by changing the URL you invoke it by.

FYI: for modperl ScriptAlias is the same thing as:

  Alias /foo/ /path/to/foo/
  SetHandler cgi-handler

where SetHandler cgi-handler invokes mod_cgi. The latter will be overwritten if you enable Apache::Registry. In other words, ScriptAlias does not work for mod_perl, it only appears to work when the additional configuration is in there. If the Apache::Registry configuration came before the ScriptAlias, scripts would be run under mod_cgi. While handy, ScriptAlias is a known kludge, always better to use Alias and SetHandler.

Of course you can choose any other Alias (you will use it later in httpd.conf), you can choose to use all three modes or only one of these. It is undesirable to run scripts in plain mod_cgi from a mod_perl-enabled server - the price is too high, it is better to run these on plain apache server. (See Standalone mod_perl Enabled Apache Server)

[TOC]


Location Configuration

Now we will work with the httpd.conf file. I add all the mod_perl stuff at the end of the file, after the native apache configurations.

First we add:

  <Location /perl>
    #AllowOverride None
    SetHandler perl-script
    PerlHandler Apache::Registry
    Options ExecCGI
    allow from all
    PerlSendHeader On
  </Location>

This configuration causes all scripts that are called with a /perl path prefix to be executed under the Apache::Registry module and as a CGI (so the ExecCGI, if you omit this option the script will be printed to the caller's browser as a plain text or possibly will trigger a 'Save-As' window).

PerlSendHeader On tells the server to send an HTTP header to the browser on every script invocation. You will want to turn this off for nph (non-parsed-headers) scripts. PerlSendHeader On means to call ap_send_http_header() after parsing your script headers. It is only meant for CGI emulation, its always better to use CGI-&gt;header from CGI.pm module or $r-&gt;send_http_header directly.

Remember the Alias from the section above? We must use the same Alias here, if you use Location that does not have the same Alias defined in srm.conf, the server will fail to locate the script in the file system. (We are talking about script execution here -- there are cases where Location is something that is being executed by the server itself, without having the corresponding file, like /perl-status location.)

Note that sometimes you will have to add :

  PerlModule Apache::Registry

before you specify the location that uses Apache::Registry as a PerlHandler. Basically you can start running the scripts in the Apache::Registry mode...

You have nothing to do about /cgi-bin location (mod_cgi), since it has nothing to do with mod_perl.

Here is a similar location configuration for Apache::PerlRun (More about Apache::PerlRun):

  <Location /cgi-perl>
    #AllowOverride None
    SetHandler perl-script
    PerlHandler Apache::PerlRun
    Options ExecCGI
    allow from all
    PerlSendHeader On
  </Location>

[TOC]


PerlModule and PerlRequire directives

You may load modules from the config file at server startup via:

    PerlModule Apache::DBI CGI DBD::Mysql

There is a limit of 10 PerlModule's, if you need more to be loaded when the server starts, use one PerlModule to pull in many or write them all in a regular perl syntax and put them into a startup file which can be loaded with use of the PerlRequire directive.

    PerlRequire  /home/httpd/perl/lib/startup.pl

Both PerlModule and PerlRequire are implemented by require(), but there is a subtle change. PerlModule works like use(), expecting a module name without .pm extension and slashes. Apache::DBI is OK, while Apache/DBI.pm is not. PerlRequire is the opposite to PerlModule -- it expects a relative or full path to the module or a filename, like in the example above.

As with any file that's being required() -- it must return a true value, to ensure that this happens don't forget to add 1; at the end of such files.

We must stress that all the code that is run at the server initialization time is run with root priveleges if you are executing it as a root user (you have to unless you choose an unpriveledged port, above 1024. somethings that you might have to if you don't have a root access. Just remember that you better pick a well known port like 8000 or 8080 since other non-standard ports might be blocked by firewalls that protect many organizations and individuals). This means that anyone who has write access to a script or module that is loaded by PerlModule or PerlRequire, effectively has root access to the system. You might want to take a look at the new and experimental PerlOpmask directive and PERL_OPMASK_DEFAULT compile time option to try to disable some dangerous operators.

[TOC]


Perl*Handlers

As you know Apache specifies about 11 phases of the request loop, namely in that order: Post-Read-Request, URI Translation, Header Parsing, Access Control, Authentication, Authorization, MIME type checking, FixUp, Response (Content phase). Logging and finally Cleanup. These are the stages of a request where the Apache API allows a module to step in and do something. There is a dedicated PerlHandler for each of these stages. Namely:

    PerlChildInitHandler
    PerlPostReadRequestHandler
    PerlInitHandler
    PerlTransHandler
    PerlHeaderParserHandler
    PerlAccessHandler
    PerlAuthenHandler
    PerlAuthzHandler
    PerlTypeHandler
    PerlFixupHandler
    PerlHandler
    PerlLogHandler
    PerlCleanupHandler
    PerlChildExitHandler

The first 4 handlers cannot be used in the <Location>, <Directory>, <Files> and .htaccess file, the main reason is all the above require a known path to the file in order to bind a requested path with one or more of the identifiers above. Starting from PerlHeaderParserHandler (5th) URI is allready being mapped to a physical pathname, thus can be used to match the <Location>, <Directory> or <Files> configuration section, or to look at .htaccess file if exists at the specified directory in the translated path.

The Apache documentation (or even better -- the ``Writing Apache Modules with Perl and C'' book by Doug MacEachern and Lincoln Stein) will tell you all about those stages and what your modules can do. By default, these hooks are disabled at compile time, see the INSTALL document for information on enabling these hooks.

Note that by default Perl API expects a subrotine called handler to handle the request in the registered PerlHandler module. Thus if your module implements this subrotine, you can register the handler as simple as writing:

  Perl*Handler Apache::SomeModule

replace Perl*Handler with a wanted name of the handler. mod_perl will preload the specified module for you. But if you decide to give the handler code a different name, like my_handler, you must preload the module and to write explicitly the chosen name.

  PerlModule Apache::SomeModule
  Perl*Handler Apache::SomeModule::my_handler

Please note that the former approach will not preload the module at the startup, so either explicitly preload it with PerlModule directive, add it to the startup file or use a nice shortcut the Perl*Handler syntax suggests:

  Perl*Handler +Apache::SomeModule

Notice the leading + character. It's equal to:

  PerlModule Apache::SomeModule
  Perl*Handler Apache::SomeModule

If a module wishes to know what handler is currently being run, it can find out with the current_callback method. This method is most useful to PerlDispatchHandlers who wish to only take action for certain phases.

 if($r->current_callback eq "PerlLogHandler") {
     $r->warn("Logging request");
 }

[TOC]


STACKED HANDLERS

With the mod_perl stacked handlers mechanism, it is possible for more than one Perl*Handler to be defined and run during each stage of a request.

Perl*Handler directives can define any number of subroutines, e.g. (in config files)

 PerlTransHandler OneTrans TwoTrans RedTrans BlueTrans

With the method, Apache-&gt;push_handlers(), callbacks can be added to the stack by scripts at runtime by mod_perl scripts.

Apache-&gt;push_handlers() takes the callback hook name as its first argument and a subroutine name or reference as its second. e.g.:

 Apache->push_handlers("PerlLogHandler", \&first_one);
 
 $r->push_handlers("PerlLogHandler", sub {
     print STDERR "__ANON__ called\n";
     return 0;
 });

After each request, this stack is cleared out.

All handlers will be called unless a handler returns a status other than OK or DECLINED.

example uses:

CGI.pm maintains a global object for its plain function interface. Since the object is global, it does not go out of scope, DESTROY is never called. CGI-&gt;new can call:

 Apache->push_handlers("PerlCleanupHandler", \&CGI::_reset_globals);

This function will be called during the final stage of a request, refreshing CGI.pm's globals before the next request comes in.

Apache::DCELogin establishes a DCE login context which must exist for the lifetime of a request, so the DCE::Login object is stored in a global variable. Without stacked handlers, users must set

 PerlCleanupHandler Apache::DCELogin::purge

in the configuration files to destroy the context. This is not ``user-friendly''. Now, Apache::DCELogin::handler can call:

 Apache->push_handlers("PerlCleanupHandler", \&purge);

Persistent database connection modules such as Apache::DBI could push a PerlCleanupHandler handler that iterates over %Connected, refreshing connections or just checking that ones have not gone stale. Remember, by the time we get to PerlCleanupHandler, the client has what it wants and has gone away, we can spend as much time as we want here without slowing down response time to the client (but the process is unavailable for serving new request befor the operation is completed).

PerlTransHandlers may decide, based on URI or other condition, whether or not to handle a request, e.g. Apache::MsqlProxy. Without stacked handlers, users must configure:

 PerlTransHandler Apache::MsqlProxy::translate
 PerlHandler      Apache::MsqlProxy

PerlHandler is never actually invoked unless translate() sees the request is a proxy request ($r-&gt;proxyreq), if it is a proxy request, translate() sets $r-&gt;handler("perl-script"), only then will PerlHandler handle the request. Now, users do not have to specify PerlHandler Apache::MsqlProxy, the translate() function can set it with push_handlers().

Includes, footers, headers, etc., piecing together a document, imagine (no need for SSI parsing!):

 PerlHandler My::Header Some::Body A::Footer

A little test:

 #My.pm
 package My;

 sub header {
     my $r = shift;
     $r->content_type("text/plain");
     $r->send_http_header;
     $r->print("header text\n");
 }
 sub body   { shift->print("body text\n")   }
 sub footer { shift->print("footer text\n") }
 1;
 __END__

 #in config
 <Location /foo>
 SetHandler "perl-script"
 PerlHandler My::header My::body My::footer   
 </Location>

Parsing the output of another PerlHandler? this is a little more tricky, but consider:

 <Location /foo>
   SetHandler "perl-script"
   PerlHandler OutputParser SomeApp
 </Location>
 
 <Location /bar>
   SetHandler "perl-script"
   PerlHandler OutputParser AnotherApp
 </Location>

Now, OutputParser goes first, but it untie()'s *STDOUT and re-tie()'s to its own package like so:

 package OutputParser;

 sub handler {
     my $r = shift;
     untie *STDOUT;
     tie *STDOUT => 'OutputParser', $r;
 }
  
 sub TIEHANDLE {
     my($class, $r) = @_;
     bless { r => $r}, $class;
 }
 
 sub PRINT {
     my $self = shift;   
     for (@_) {
         #do whatever you want to $_
         $self->{r}->print($_ . "[insert stuff]");
     }
 }

 1;
 __END__

To build in this feature, configure with:

 % perl Makefile.PL PERL_STACKED_HANDLERS=1 [PERL_FOO_HOOK=1,etc]

Another method Apache-&gt;can_stack_handlers will return TRUE if mod_perl was configured with PERL_STACKED_HANDLERS=1, FALSE otherwise.

[TOC]


PERL METHOD HANDLERS

If a Perl*Handler is prototyped with $$, this handler will be invoked as method. e.g.

 package My;
 @ISA = qw(BaseClass);
  
 sub handler ($$) {
     my($class, $r) = @_;
     ...;
 }
  
 package BaseClass;
  
 sub method ($$) {
     my($class, $r) = @_;
     ...;
 }
 __END__

Configuration:

 PerlHandler My

or

 PerlHandler My->handler

Since the handler is invoked as a method, it may inherit from other classes:

 PerlHandler My->method

In this case, the My class inherits this method from BaseClass.

To build in this feature, configure with:

 % perl Makefile.PL PERL_METHOD_HANDLERS=1 [PERL_FOO_HOOK=1,etc]

[TOC]


PerlFreshRestart

To reload PerlRequire, PerlModule, other use()'d modules and flush the Apache::Registry cache on server restart, add:

  PerlFreshRestart On
Make sure you read L<Evil things might happen when using
PerlFreshRestart|warnings/Evil_things_might_happen_when_us>.

[TOC]


/perl-status location

A very useful feature. You can watch what happens to the perl guts of the server. Below you will find the instructions of configuration and usage of this feature

[TOC]


Configuration

Add this to httpd.conf:

  <Location /perl-status>
    SetHandler perl-script
    PerlHandler Apache::Status
    order deny,allow
    #deny from all
    #allow from 
  </Location>

If you are going to use Apache::Status, it's important to put it as a first module in the start-up file, or in the httpd.conf (after Apache::Registry):

  # startup.pl
  use Apache::Registry ();
  use Apache::Status ();
  use Apache::DBI ();

If you don't put Apache::Status before Apache::DBI then you don't get Apache::DBI's menu entry in status.

[TOC]


Usage

Assuming that your mod_perl server listens to port 81, fetch http://www.nowhere.com:81/perl-status

  Embedded Perl version 5.00502 for Apache/1.3.2 (Unix) mod_perl/1.16 
  process 187138, running since Thu Nov 19 09:50:33 1998

This is the linked menu that you should see:

  Signal Handlers
  Enabled mod_perl Hooks
  PerlRequire'd Files
  Environment
  Perl Section Configuration
  Loaded Modules
  Perl Configuration
  ISA Tree
  Inheritance Tree
  Compiled Registry Scripts
  Symbol Table Dump

Let's follow for example : PerlRequire'd Files -- we see:

  PerlRequire                          Location
  /usr/myproject/lib/apache-startup.pl /usr/myproject/lib/apache-startup.pl

From some menus you can continue deeper to peek at the perl internals of the server, to watch the values of the global variables in the packages, to the list of cached scripts and modules and much more. Just click around...

[TOC]


Compiled Registry Scripts section seems to be empty.

Sometimes when you fetch /perl-status you and follow the Compiled Registry Scripts link from the status menu -- you see no listing of scripts at all. This is absolutely correct -- Apache::Status shows the registry scripts compiled in the httpd child which is serving your request for /perl-status. If a child has not compiled yet the script you are asking for, /perl-status will just show you the main menu. This usually happens when the child was just spawned.

[TOC]


PerlSetVar, PerlSetEnv and PerlPassEnv

  PerlSetEnv key val
  PerlPassEnv key

PerlPassEnv passes, PerlSetEnv sets and passes the ENVironment variables to your scripts. you can access them in your scripts through %ENV (e.g. $ENV{"key"}).

Regarding the setting of PerlPassEnv PERL5LIB in httpd.conf If you turn on taint checks (PerlTaintMode On), $ENV{PERL5LIB} will be ignored (unset).

PerlSetVar is very similar to PerlSetEnv, but you extract it with another method. In <Perl> sections:

  push @{ $Location{"/"}->{PerlSetVar} }, [ 'FOO' => BAR ];

and in the code you read it with:

  my $r = Apache->request;
  print $r->dir_config('FOO');

[TOC]


perl-startup file

Since many times you have to add many perl directives to the configuration file, it can be a good idea to put all of these into a one file, so the configuration file will be cleaner. Add the following line to httpd.conf:

    # startup.perl loads all functions that we want to use within
    # mod_perl
  Perlrequire /path/to/startup.pl

before the rest of the mod_perl configuration directives.

Also you can call perl -c perl-startup to test the file's syntax. What does this take?

[TOC]


Sample perl-startup file

An example of perl-startup file:

  use strict;
  
  # extend @INC if needed
  use lib qw(/dir/foo /dir/bar);
  
  # make sure we are in a sane environment.
  $ENV{GATEWAY_INTERFACE} =~ /^CGI-Perl/
     or die "GATEWAY_INTERFACE not Perl!";
   
  # for things in the "/perl" URL
  use Apache::Registry;          
   
  #load perl modules of your choice here
  #this code is interpreted *once* when the server starts
  use LWP::UserAgent ();
  use DBI ();
  
  # tell me more about warnings
  use Carp ();
  $SIG{__WARN__} = \&Carp::cluck;
  
  # Load CGI.pm and call its compile() method to precompile 
  # (but not to import) its autoloaded methods. 
  use CGI ();
  CGI->compile(':all');

Note that starting with $CGI::VERSION 2.46, the recommended method to precompile the code in CGI.pm is:

  use CGI qw(-compile :all);

But the old method is still available for backward compatibility.

See also Apache::Status

[TOC]


What modules should you add to the startup file and why.

Modules that are being loaded at the server startup will be shared among server children, so only one copy of each module will be loaded, thus saving a lot of RAM for you. Usually I put most of the code I develop into modules and preload them from here. You can even preload your CGI script with Apache::RegistryLoader and preopen the DB connections with Apache::DBI. (See Preload Perl modules at server startup).

[TOC]


The confusion with use() clause at the server startup?

Many people wonder, why there is a need for duplication of use() clause both in startup file and in the script itself. The question rises from misunderstanding of the use() operand. use() consists of two other operands, namely require() and import(). So when you write:

  use Foo qw(bar);

perl actually does:

  require Foo.pm;
  import qw(bar);

When you write:

  use Foo qw();

perl actually does:

  require Foo.pm;
  import qw();

which means that the caller does not want any symbols to be imported. Why is this important? Since some modules has @EXPORT set to a list of tags to be exported by default and when you write:

  use Foo;

and think nothing is being imported, the import() call is being executed and probably some symbols do being imported. See the docs/source of the module in question to make sure you use() it correctly. When you write your own modules, always remember that it's better to use @EXPORT_OK instead of @EXPORT, since the former doesn't export tags unless it was asked to.

Since the symbols that you might import into a startup's script namespace will be visible by none of the children, scripts that need a Foo's module exported tags have to pull it in like if you did not preload Foo at the startup file. For example, just because you have use()d Apache::Constants in the startup script, does not mean you can have the following handler:

  package MyModule;
  
  sub {
    my $r = shift;
  
    ## Cool stuff goes here
  
    return OK;
  }

  1;

You would either need to add:

  use Apache::Constants qw( OK );

Or instead of return OK; say:

  return Apache::Constants::OK;

See the manpage/perldoc on Exporter and perlmod for more on import().

[TOC]


The confusion with defining globals in startup

PerlRequire allows you to execute code that preloads modules and does more things. Imported or defined variables are visible in the scope of the startup file. It is a wrong assumption that global variables that were defined in the startup file, will be accessible by child processes.

You do have to define/import variables in your scripts and they will be visible inside a child process who run this script. They will be not shared between siblings. Remember that every script is running in a specially (uniquely) named package - so it cannot access variables from other packages unless it inherits from them or use()'s them.

[TOC]


Running 'apachectl configtest' or 'httpd -t'

apachectl configtest tests the configuration file without starting the server. You can safely modify the configuration file on your production server, if you run this test before you restart the server. Of course it is not 100% error prone, but it will reveal any syntax errors you might do while editing the file.

'apachectl configtest' is the same as 'httpd -t' and it actually executes the code in startup.pl, not just parses it. <Perl> configuration has always started Perl during the configuration read, Perl{Require,Module} do so as well.

If you want your startup code to get a control over the -t (configtest) server launch, start the server configuration test with:

  httpd -t -Dsyntax_check

and in your startup file, add (at the top):

  return if Apache->define('syntax_check');

if you want to prevent the code in the file from being executed.

[TOC]


Perl behavior controls

For PerlWarn and PerlTaintCheck see Switches -w, -T

[TOC]


Tuning MinSpareServers MaxSpareServers StartServers MaxClients MaxRequestsPerChild

See Tuning the Apache's configuration variables for the best performance

[TOC]


Publishing port numbers different from 80

It is advised not to publish the 8080 (or alike) port number in URLs, but rather using a proxying rewrite rule in the thin (httpd_docs) server:

  RewriteRule .*/perl/(.*) http://my.url:8080/perl/$1 [P]

One problem with publishing 8080 port numbers is that I was told that IE 4.x has a bug when re-posting data to a non-port-80 url. It drops the port designator, and uses port 80 anyway.

[TOC]


Perl Sections

With &lt;Perl&gt;&lt;/Perl&gt; sections, it is possible to configure your server entirely in Perl.

<Perl> sections can contain *any* and as much Perl code as you wish. These sections are compiled into a special package whose symbol table mod_perl can then walk and grind the names and values of Perl variables/structures through the apache core configuration gears. Most of the configurations directives can be represented as scalars ($scalar) or lists (@list). An @List inside these sections is simply converted into a space delimited string for you inside. Here is an example:

  #httpd.conf
  <Perl>
  @PerlModule = qw(Mail::Send Devel::Peek);
 
  #run the server as whoever starts it
  $User  = getpwuid($>) || $>;
  $Group = getgrgid($)) || $); 
 
  $ServerAdmin = $User;
 
  </Perl>

Block sections such as <Location..</Location>> are represented in a %Location hash, e.g.:

  $Location{"/~dougm/"} = {
    AuthUserFile => '/tmp/htpasswd',
    AuthType => 'Basic',
    AuthName => 'test',
    DirectoryIndex => [qw(index.html index.htm)],  
    Limit => {
    METHODS => 'GET POST',
    require => 'user dougm',
    },
  };

If a Directive can take two *or* three arguments you may push strings and the lowest number of arguments will be shifted off the @List or use array reference to handle any number greater than the minimum for that directive:

  push @Redirect, "/foo", "http://www.foo.com/";;
  
  push @Redirect, "/imdb", "http://www.imdb.com/";;
  
  push @Redirect, [qw(temp "/here" "http://www.there.com";)];

Other section counterparts include %VirtualHost, %Directory and %Files.

To pass all environment variables to the children with a single configuration directive, rather than listing each one via PassEnv or PerlPassEnv, a <Perl> section could read in a file and:

  push @PerlPassEnv, [$key => $val];

or

  Apache->httpd_conf("PerlPassEnv $key $val");

These are somewhat simple examples, but they should give you the basic idea. You can mix in any Perl code your heart desires. See eg/httpd.conf.pl and eg/perl_sections.txt in mod_perl distribution for some examples.

A tip for syntax checking outside of httpd:

  <Perl>
  # !perl
  
  #... code here ...
  
  __END__
  </Perl>

Now you may run:

  perl -cx httpd.conf

To enable <Perl> sections you should build mod_perl with perl Makefile.PL PERL_SECTIONS=1.

You can watch how have you configured the <Perl> sections through the /perl-status location, by choosing the Perl Sections from the menu.

You can dump the configuration by <Perl> sections configuration this way:

  <Perl>
  use Apache::PerlSections();
  ...
  print STDERR Apache->PerlSections->dump();
  </Perl>

Alternatively you can store it in a file:

  Apache::PerlSections->store("httpd_config.pl");

You can then require() that file in some other <Perl> section.

[TOC]


Configuring Apache + mod_perl with mod_macro

mod_macro is an Apache module written by Fabien Coelho that lets you define and use macros in the Apache configuration file.

mod_macro proved really useful when you have many virtual hosts, each virtual host has a number of scripts/modules, most of them with a moderately complex configuration setup.

First download the latest version of mod_macro from http://www.cri.ensmp.fr/~coelho/mod_macro/ , and configure your Apache server to use this module.

Here are some useful macros for mod_perl users:

        # set up a registry script
        <Macro registry>
        SetHandler "perl-script"
        PerlHandler Apache::Registry
        Options +ExecCGI
        </Macro>

        # example
        Alias /stuff /usr/www/scripts/stuff
        <Location /stuff>
        Use registry
        </Location>

If your registry scripts are all located in the same directory, and your aliasing rules consistent, you can use this macro:

        # set up a registry script for a specific location
        <Macro registry $location $script>
        Alias /script /usr/www/scripts/$script
        <Location $location>
        SetHandler "perl-script"
        PerlHandler Apache::Registry
        Options +ExecCGI
        </Location>
        </Macro>

        # example
        Use registry stuff stuff.pl

If you're using content handlers packaged as modules, you can use the following macro:

        # set up a mod_perl content handler module
        <Macro modperl $module>
        SetHandler "perl-script"
        Options +ExecCGI
        PerlHandler $module
        </Macro>

        #examples
        <Location /perl-status>
        PerlSetVar StatusPeek On
        PerlSetVar StatusGraph On
        PerlSetVar StatusDumper On
        Use modperl Apache::Status
        </Location>

The following macro sets up a Location for use with HTML::Embperl. Here we define all ``.html'' files to be processed by Embperl.

        <Macro embperl>
        SetHandler "perl-script"
        Options +ExecCGI
        PerlHandler HTML::Embperl
        PerlSetEnv EMBPERL_FILESMATCH \.html$
        </Macro>

        # examples
        <Location /mrtg>
        Use embperl
        </Location>

Macros are also very useful for things that tend to be verbose, such as setting up Basic Authentication:

        # Sets up Basic Authentication
        <Macro BasicAuth $realm $group>
        Order deny,allow
        Satisfy any
        AuthType Basic
        AuthName $realm
        AuthGroupFile /usr/www/auth/groups
        AuthUserFile /usr/www/auth/users
        Require group $group
        Deny from all
        </Macro>

        # example of use
        <Location /stats>
        Use BasicAuth WebStats Admin
        </Location>

Finally, here is a complete example that uses macros to set up simple virtual hosts. It uses the BasicAuth macro defined previously (yes, macros can be nested!).

        <Macro vhost $ip $domain $docroot $admingroup>
        <VirtualHost $ip>
        ServerAdmin webmaster@$domain
        DocumentRoot /usr/www/htdocs/$docroot
        ServerName www.$domain
        <Location /stats>
        Use BasicAuth Stats-$domain $admingroup
        </Location>
        </VirtualHost>
        </Macro>

        # define some virtual hosts
        Use vhost 10.1.1.1 example.com example example-admin
        Use vhost 10.1.1.2 example.net examplenet examplenet-admin

mod_macro also useful in a non vhost setting. Some sites for example have lots of scripts where people use to view various statistics, email settings and etc. It is much easier to read things like:

  use /forwards email/showforwards
  use /webstats web/showstats

[TOC]


General pitfalls

[TOC]


My cgi/perl code is being returned as a plain text instead of being executed by the webserver?

Check your configuration files and make sure that the ``ExecCGI'' is turned on in your configurations.

  <Location /perl>
    SetHandler perl-script
    PerlHandler Apache::Registry
    Options ExecCGI
    allow from all
    PerlSendHeader On
  </Location>

[TOC]


My script works under cgi-bin, but when called via mod_perl I see A 'Save-As' prompt

Did you put PerlSendHeader On in the configuration part of the <Location foo></Location>?

[TOC]


Is there a way to provide a different startup.pl file for each individual virtual host

No. Any virtual host will be able to see the routines from a startup.pl loaded for any other virtual host.

[TOC]


Is there a way to modify @INC on a per-virtual-host or per-location basis.

You can use 'PerlSetEnv PERL5LIB ...' or a PerlFixupHandler w/ the lib pragma.

Even a better way is to use Apache::PerlVINC

[TOC]


A Script from one virtual host calls a script with the same path from the other virtual host

This has been a bug before, last fixed in 1.15_01, i.e. if you are running 1.15, that could be the problem. You should set this variable in a startup file (PerlRequire):

  $Apache::Registry::NameWithVirtualHost = 1;

But, as we know sometimes bug turns into a feature. If there is the same script running for more than one Virtual host on the same machine, this can be a waste, right? Set it to 0 in a startup script if you want to turn it off and have this bug as a feature. (Only makes sense if you are sure that there will be no otherscripts named by the same path/name). It also saves you some memory on the way.

  $Apache::Registry::NameWithVirtualHost = 0;

[TOC]


the server no longer retrieves the DirectoryIndex files for a directory

The problem was reported by users who declared mod_perl configuration inside a <Directory> section for all files matching to *.pl. The problem has gone away after placing the usage of mod_perl in a <File>- section.

[TOC]


Configuration Security Concerns

It is better not to advertise the port mod_perl server running at to the outside world for it creates a potential security risk by revealing which module(s) and/or OS you are running your web server on.

The more modules you have in your web server, the more complex the code in your webserver.

The more complex the code in your web server, the more chances for bugs.

The more chance for bugs, the more chance that some of those bugs may involve security.

Never was completely sure why the default of the ServerToken directive in Apache is Full rather than Minimal. Seems like you would only make it full if you are debugging.

For more information see Publishing port numbers different from 80

Another approach is to modify httpd sources to reveal no unwanted information, so if you know the port the HEAD request will return an empty or phony Server: field.

[TOC]


Logical grouping of Location, Directory and FilesMatch directives

Let's say that you want all the file in a specific directory and below to be handled the same way, but a few of them to be handled somewhat different. For example:

  <Directory /home/foo>
    <FilesMatch "\.(html|txt)$">
      SetHandler perl-script
      PerlHandler Apache::AddrMunge
    </FilesMatch>
  </Directory>

Alternatively you can use <Files> inside an .htaccess file.

Note that you cannot have Files derective inside Location, but you can have Files inside Directory.

[TOC]


Apache restarts twice on start

When the server is restarted. the configuration and module initialization phases are called again (twice in total). To ensure that the future restart will workout correctly, Apache actually runs these two phases twice during server startup, to check that all modules can survive a restart.

(META: And add an example that writes to the log file - I was restarted 1, 2 times)

[TOC]


The Writing Apache Modules with Perl and C book can be purchased online from O'Reilly and Amazon.com.
Your corrections of either technical or grammatical errors are very welcome. You are encouraged to help me to improve this guide. If you have something to contribute please send it directly to me.
[ Prev | Main Page | Next ]

Written by Stas Bekman.
Last Modified at 09/26/1999
Mod Perl Icon Use of the Camel for Perl is
a trademark of O'Reilly & Associates,
and is used by permission.