1. Introduction

The Apache HTTP Server (Apache) is ubiquitous among Web servers for many reasons:

  • stable
  • open-source
  • cross-platform
  • extensible

On the last point, Apache comes with multiple modules, supports even more, and provides a way to build and install new ones. Perhaps the best way to do so is the APache eXtenSion tool (apxs).

In this tutorial, we explore apxs and how to install it on different Linux distributions. First, we briefly refresh our knowledge about Apache modules. After that, we go over the standard tool to build and install such modules: apxs.

While Debian-based distributions use apache2, a special structure, and scripts, the server binary in RPM-based versions is called httpd and only employs regular configuration files.

We tested the code in this tutorial on Debian 12 (Bookworm) and Rocky 9 (Blue Onyx) with Apache 2.4. Unless otherwise specified, it should work in most POSIX-compliant environments.

2. Apache Modules

Apache modules are at the core of the Apache HTTP Server. In fact, the core module provides the main features.

Indeed, this encapsulation of even the most basic functionality shows how Apache was implemented as an extensible product from the ground up.

2.1. Example Modules and Naming

On top of the primitive functions, there are many modules for more complex features. Let’s see some examples:

  • ssl_module: SSL and TLS support
  • auth*_module: different authentication modules
  • dir_module: trailing slash redirections and directory indices
  • so_module: load executable code and modules dynamically

Usually, the names of module files start with mod_, while the module name ends with _module.

2.2. Hardcoded Functionality

To begin with, we can see how Apache was compiled via the -l flag.

Let’s check that in Debian:

$ apache2 -l
Compiled in modules:
  core.c
  mod_so.c
  mod_watchdog.c
  http_core.c
  mod_log_config.c
  mod_logio.c
  mod_version.c
  mod_unixd.c

RPM-based distributions often have versions with fewer hardcoded modules:

$ httpd -l
Compiled in modules:
  core.c
  mod_so.c
  http_core.c

Still, the core, http_core, and mod_so parts are common. Notably, without the mod_so component, we wouldn’t be able to extend the installation with the use of modules.

2.3. List Enabled Modules

Let’s employ apache2ctl to check the [-M]odules available in a basic installation on most Debian-based distributions:

$ apache2ctl -M
Loaded Modules:
 core_module (static)
 so_module (static)
 watchdog_module (static)
 http_module (static)
 log_config_module (static)
 logio_module (static)
 version_module (static)
 unixd_module (static)
 access_compat_module (shared)
 alias_module (shared)
 auth_basic_module (shared)
 authn_core_module (shared)
 authn_file_module (shared)
 authz_core_module (shared)
 authz_host_module (shared)
 authz_user_module (shared)
 autoindex_module (shared)
 deflate_module (shared)
 dir_module (shared)
 env_module (shared)
 filter_module (shared)
 mime_module (shared)
 mpm_event_module (shared)
 negotiation_module (shared)
 reqtimeout_module (shared)
 setenvif_module (shared)
 socache_shmcb_module (shared)
 ssl_module (shared)
 status_module (shared)

On the other hand, we can use the same flag with the httpd tool on RPM-based distributions:

$ httpd -M         
Loaded Modules:
core_module (static)
so_module (static)
http_module (static)
access_compat_module (shared)
actions_module (shared)
alias_module (shared)
allowmethods_module (shared)
auth_basic_module (shared)
auth_digest_module (shared)
authn_anon_module (shared)
authn_core_module (shared)
authn_dbd_module (shared)
authn_dbm_module (shared)
authn_file_module (shared)
authn_socache_module (shared)
authz_core_module (shared)
authz_dbd_module (shared)
authz_dbm_module (shared)
authz_groupfile_module (shared)
[...]
cgid_module (shared)
http2_module (shared)
proxy_http2_module (shared)

Some modules are static, meaning they are integrated deep within the binaries, while others are shared, so we can disable and remove them at will. Regardless, each module ends with the _module suffix.

Notably, what we see above are only the enabled modules.

2.4. Check Available Modules

In Debian-based systems, we can usually see the available modules under /etc/apache2/mods-available/:

$ ls /etc/apache2/mods-available/
access_compat.load    cgi.load                  log_debug.load       ratelimit.load
actions.conf          charset_lite.load         log_forensic.load    reflector.load
actions.load          data.load                 lua.load             remoteip.load
alias.conf            dav_fs.conf               macro.load           reqtimeout.conf
alias.load            dav_fs.load               md.load              reqtimeout.load
allowmethods.load     dav.load                  mime.conf            request.load
asis.load             dav_lock.load             mime.load            rewrite.load
auth_basic.load       dbd.load                  mime_magic.conf      sed.load
auth_digest.load      deflate.conf              mime_magic.load      session_cookie.load
auth_form.load        deflate.load              mpm_event.conf       session_crypto.load
authn_anon.load       dialup.load               mpm_event.load       session_dbd.load
authn_core.load       dir.conf                  mpm_prefork.conf     session.load
authn_dbd.load        dir.load                  mpm_prefork.load     setenvif.conf
[...]

However, their .so* files are in */usr/lib/apache2/modules/:

$ ls /usr/lib/apache2/modules/
httpd.exp               mod_cache_socache.so   mod_lbmethod_bybusyness.so  mod_proxy_wstunnel.so
mod_access_compat.so    mod_case_filter_in.so  mod_lbmethod_byrequests.so  mod_ratelimit.so
mod_actions.so          mod_case_filter.so     mod_lbmethod_bytraffic.so   mod_reflector.so
mod_alias.so            mod_cern_meta.so       mod_lbmethod_heartbeat.so   mod_remoteip.so
mod_allowmethods.so     mod_cgid.so            mod_ldap.so                 mod_reqtimeout.so
mod_asis.so             mod_cgi.so             mod_log_debug.so            mod_request.so
mod_auth_basic.so       mod_charset_lite.so    mod_log_forensic.so         mod_rewrite.so
mod_auth_digest.so      mod_data.so            mod_lua.so                  mod_sed.so
mod_auth_form.so        mod_dav_fs.so          mod_macro.so                mod_session_cookie.so
[...]

Meanwhile, RPM-based systems usually store the .so files under /usr/lib64/httpd/modules/ with symbolic links at /etc/httpd/modules/:

$ ls /usr/lib64/httpd/modules/
mod_access_compat.so    mod_cgi.so                  mod_log_config.so      mod_reflector.so
mod_actions.so          mod_cgid.so                 mod_log_debug.so       mod_remoteip.so
mod_alias.so            mod_charset_lite.so         mod_log_forensic.so    mod_reqtimeout.so
mod_allowmethods.so     mod_data.so                 mod_logio.so           mod_request.so
mod_asis.so             mod_dav.so                  mod_lua.so             mod_rewrite.so
mod_auth_basic.so       mod_dav_fs.so               mod_macro.so           mod_sed.so
mod_auth_digest.so      mod_dav_lock.so             mod_mime.so            mod_setenvif.so
mod_authn_anon.so       mod_dbd.so                  mod_mime_magic.so      mod_slotmem_plain.so
mod_authn_core.so       mod_deflate.so              mod_mpm_event.so       mod_slotmem_shm.so
[...]

Notably, the .so files are usually considerably more than the enabled modules.

2.5. Toggle Modules

Debian-based systems use a special hierarchy for configuration:

$ ls /etc/apache2/
apache2.conf            conf-available  envvars  mods-available  ports.conf       sites-enabled
apache2.conf.dpkg-dist  conf-enabled    magic    mods-enabled    sites-available

The a2* commands control symbolic links under the conf-*, mods-*, and sites-* directories above:

$ a2[Tab]
a2disconf  a2dismod   a2dissite  a2enconf   a2enmod    a2ensite   a2query    a2x

In particular, a2enmod* and a2dismod create and remove symbolic links in mods-enabled that point to *mods-available.

On the other hand, RPM-based distributions work directly with the httpd.conf configuration file to load and unload modules:

$ cat /etc/httpd/conf/httpd.conf
[...]
#
# Dynamic Shared Object (DSO) Support
#
# To be able to use the functionality of a module which was built as a DSO you
# have to place corresponding `LoadModule' lines at this location so the
# directives contained in it are actually available _before_ they are used.
# Statically compiled modules (those listed by `httpd -l') do not need
# to be loaded here.
#
# Example:
# LoadModule foo_module modules/mod_foo.so
#
Include conf.modules.d/*.conf
[...]

Further, we can place .conf files that contain LoadModule statements in /etc/httpd/conf.modules.d/.

After any configuration changes, we restart the service via a manager like systemctl or manually.

3. APache eXtenSion (apxs)

The apxs Apache eXtenSion tool is the standard way to build and install modules for Apache.

3.1. Install

To install the apxs utility, we use the httpd-devel package in RPM-based distributions and apache2-dev for Debian-based ones.

First, let’s perform the installation on Debian:

$ apt install apache2-dev

On the Linux RPM side, we employ dnf or yum:

$ dnf install httpd-devel

Sometimes, RPM-based distributions may require special repository configurations such as Extra Packages for Enterprise Linux (EPEL), also called Extras.

To verify whether we have it configured, we can use the repolist subcommand:

$ dnf repolist
repo id                                    repo name
appstream                                  Rocky Linux 9 - AppStream
baseos                                     Rocky Linux 9 - BaseOS
extras                                     Rocky Linux 9 - Extras

If not available, to install EPEL, we use online sources:

$ dnf config-manager --set-enabled crb
$ dnf install \
  https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm \
  https://dl.fedoraproject.org/pub/epel/epel-next-release-latest-9.noarch.rpm

Thus, we should be able to install httpd-devel.

Notably, some RPM distributions like Rocky don’t require configuration on top of the default to do that.

3.2. Basic Usage

To use apxs, we run it with a set of options and a module base or source file:

$ apxs -i -a -c mod_<MODNAME>.c
gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_<MODNAME>.c
ld -Bshareable -o mod_<MODNAME>.so mod_<MODNAME>.o
cp mod_<MODNAME>.so /path/to/apache/modules/mod_<MODNAME>.so
chmod 755 /path/to/apache/modules/mod_<MODNAME>.so
[activating module `<MODNAME>' in /path/to/apache/etc/httpd.conf]

Let’s break this common option set down:

  • -c: compile supplied .c source file
  • -i: install module after a successful build
  • -a: activate module after installation

Alternatively, we can use -o to directly specify any module object file to work with. Usually, apxs recognizes the .c, .o, and .a extensions, so it can act on them accordingly.

Further, we can already see that build tools are required for the -c option. A further consideration is that gcc* always uses *-fpic, so we should ensure any compiled objects use position independent code (PIC)

Once we have the resulting file, we can use it like any other module.

4. Summary

In this article, we talked about the Apache HTTP Server modules and the standard tool to compile and install them.

In conclusion, Apache is a versatile Web server built to be extensible from the ground up.