Chapter 9: Module Anatomy
What is an Apache Module?
An Apache module is a self-contained unit of functionality that plugs into Apache’s core framework. Every feature beyond basic HTTP serving - SSL, compression, URL rewriting, CGI, authentication, session management - is implemented as a module. This chapter brings together everything from the previous chapters (pools, configuration, hooks, filters) to show how they combine into a complete module.
Modules can:
Add new configuration directives (see Chapter 4: Configuration)
Handle specific URL paths or file types (via the handler hook)
Transform content (input/output filters, see Chapter 7: Filters)
Implement authentication/authorization (via security hooks)
Add new protocols (via connection hooks)
Log requests in custom formats (via the log_transaction hook)
The Module Structure
Every module is defined by the module structure (from include/http_config.h). This single struct is the complete interface between a module and Apache’s core - it’s how Apache discovers what a module can do:
// include/http_config.h
struct module {
int version; // API version
int minor_version; // Minor API version
int module_index; // Index in module array
const char *name; // Module name
void *dynamic_load_handle; // dlopen handle (if dynamic)
struct module *next; // Next module in list
unsigned long magic; // Magic number for validation
// Optional initialization function
void (*rewrite_args)(process_rec *process);
// Configuration functions
void *(*create_dir_config)(apr_pool_t *p, char *dir);
void *(*merge_dir_config)(apr_pool_t *p, void *base, void *add);
void *(*create_server_config)(apr_pool_t *p, server_rec *s);
void *(*merge_server_config)(apr_pool_t *p, void *base, void *add);
// Configuration directives
const command_rec *cmds;
// Hook registration function
void (*register_hooks)(apr_pool_t *p);
// Flags for module capabilities
unsigned long flags;
};
The Module Declaration Macro
Modules are declared using the AP_DECLARE_MODULE macro:
AP_DECLARE_MODULE(example) = {
STANDARD20_MODULE_STUFF, // Fills in version, magic, etc.
create_dir_config, // Per-directory config creator
merge_dir_config, // Per-directory config merger
create_server_config, // Per-server config creator
merge_server_config, // Per-server config merger
example_commands, // Configuration directives
register_hooks // Hook registration function
};
The STANDARD20_MODULE_STUFF macro (from include/http_config.h) fills in the boilerplate fields that are the same for every module:
// include/http_config.h
#define STANDARD20_MODULE_STUFF \
MODULE_MAGIC_NUMBER_MAJOR, \
MODULE_MAGIC_NUMBER_MINOR, \
-1, /* module_index - filled by ap_add_module() */ \
__FILE__, /* source file name */ \
NULL, /* dynamic_load_handle */ \
NULL, /* next pointer in linked list */ \
MODULE_MAGIC_COOKIE, \
NULL, /* rewrite_args */ \
0 /* flags */
The module_struct::module_index is set to -1 here because it’s assigned at runtime by ap_add_module() during module initialization (see Chapter 6: Hooks). This index is used to index into the per-module config vectors (request_rec::per_dir_config, request_rec::request_config, etc.) - each module gets a slot at its unique index.
Complete Module Example
For a full, annotated module template with configuration directives, handlers, filters, and lifecycle hooks, see the official Apache Module Development Guide. It covers:
Configuration structures, creators, and mergers
Directive handlers and the
command_rectableHook implementations (handler, post-read, log transaction)
Output/input filters
Child init and post-config hooks
Per-request, per-connection, and per-server config access via ap_get_module_config()
Logging with ap_log_rerror, ap_log_error, and ap_log_cerror
Common Patterns
Getting module config
ap_get_module_config takes different config slots depending on the scope you need - per-directory, per-server, per-connection, or per-request.
static int my_handler(request_rec *r)
{
/* Per-directory config (merged for this request path) */
my_dir_config *dir = ap_get_module_config(
r->per_dir_config, &my_module);
/* Per-server config (for this virtual host) */
my_server_config *srv = ap_get_module_config(
r->server->module_config, &my_module);
/* Per-connection config */
my_conn_config *conn = ap_get_module_config(
r->connection->conn_config, &my_module);
/* Per-request config */
my_req_data *req = ap_get_module_config(
r->request_config, &my_module);
}
Setting per-request data
A common pattern is to store data early in the request lifecycle (e.g. in a post-read hook) and retrieve it later in the handler, using r->request_config as the carrier.
static int my_post_read(request_rec *r)
{
my_req_data *data = apr_pcalloc(r->pool, sizeof(*data));
data->start_time = apr_time_now();
ap_set_module_config(r->request_config, &my_module, data);
return DECLINED;
}
static int my_handler(request_rec *r)
{
my_req_data *data = ap_get_module_config(
r->request_config, &my_module);
if (data) {
apr_time_t elapsed = apr_time_now() - data->start_time;
/* Use elapsed time... */
}
return OK;
}
Logging functions and levels
Function |
Scope |
Example |
|---|---|---|
Request |
|
|
Server |
|
|
Connection |
|
Log levels (least to most verbose): APLOG_EMERG, APLOG_ALERT, APLOG_CRIT, APLOG_ERR, APLOG_WARNING, APLOG_NOTICE, APLOG_INFO, APLOG_DEBUG, APLOG_TRACE1–APLOG_TRACE8
How It All Fits Together
The following diagram shows how the module struct connects to Apache’s core systems:
flowchart TD
M["module struct<br />(AP_DECLARE_MODULE)"] --> CMD["cmds<br />(command_rec[])"]
M --> RH["register_hooks()"]
M --> CDC["create_dir_config()"]
M --> MDC["merge_dir_config()"]
M --> CSC["create_server_config()"]
M --> MSC["merge_server_config()"]
CMD --> |"Config parsing"| DIR["Directive handlers<br />(set_enabled, set_message, ...)"]
RH --> |"ap_hook_*()"| HOOKS["Hook callbacks<br />(handler, fixups, log, ...)"]
RH --> |"ap_register_*_filter()"| FILT["Filter callbacks"]
CDC --> |"apr_pcalloc()"| CONF["Config struct<br />(allocated from pool)"]
MDC --> |"Merge base + add"| CONF
style M fill:#e74c3c,stroke:#c0392b,color:#000
style HOOKS fill:#3498db,stroke:#2980b9,color:#000
style FILT fill:#3498db,stroke:#2980b9,color:#000
style CONF fill:#2ecc71,stroke:#27ae60,color:#000
Summary
A well-structured Apache module includes:
Module declaration - AP_DECLARE_MODULE with STANDARD20_MODULE_STUFF
Configuration structures - per-directory and per-server config structs
Config creators/mergers - set defaults, handle inheritance across
<Directory>nestingDirective handlers - parse, validate, and store config values
Command table - maps directive names to handlers with argument types
Hook implementations - the actual functionality (handlers, filters, access checks)
Hook registration - connect callbacks at the right phases and ordering
Key principles:
Use pools for all allocations (see Chapter 3: Memory Pools)
Return DECLINED unless you’re handling the request
Use ap_get_module_config() for configuration access (indexed by
module_index)Register hooks at appropriate ordering (
APR_HOOK_{FIRST,MIDDLE,LAST})Log with ap_log_rerror (request), ap_log_error (server), ap_log_cerror (connection)
Note
For fuzzing: when you read a module’s source to understand what to fuzz, this anatomy tells you exactly where to look. The module_struct::cmds table tells you what configuration it accepts. The module_struct::register_hooks function tells you which request phases it participates in. The handler tells you what input it processes. All of these are potential attack surfaces that the fuzzer can exercise.