Checking Your Installation
When debugging mod_wsgi or a WSGI application it is important to be able to confirm which Apache and which Python mod_wsgi is running against, how those installations were built, and what configuration the WSGI application is running under. This page lists the checks that gather that information.
The primary purpose of the page is to give a single reference for the diagnostic data that maintainers ask for when responding to a GitHub issue. If you have been directed here to collect information for a bug report, include the full output of the checks rather than a summary — partial output makes it harder to rule out causes.
Apache build information
The first piece of information to collect is which Apache binary is
in use and how it was built. The starting point is httpd -V:
$ httpd -V
Server version: Apache/2.4.66 (Unix)
Server built: Dec 1 2025 12:44:02
Server's Module Magic Number: 20120211:141
Server loaded: APR 1.7.6, APR-UTIL 1.6.3, PCRE 10.47 2025-10-21
Compiled using: APR 1.7.6, APR-UTIL 1.6.3, PCRE 10.47 2025-10-21
Architecture: 64-bit
Server MPM: prefork
threaded: no
forked: yes (variable process count)
Server compiled with....
-D APR_HAS_SENDFILE
-D APR_HAS_MMAP
-D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
-D APR_USE_SYSVSEM_SERIALIZE
-D APR_USE_PTHREAD_SERIALIZE
-D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
-D APR_HAS_OTHER_CHILD
-D AP_HAVE_RELIABLE_PIPED_LOGS
-D DYNAMIC_MODULE_LIMIT=256
-D HTTPD_ROOT="/opt/homebrew/Cellar/httpd/2.4.66"
-D SUEXEC_BIN="/opt/homebrew/opt/httpd/bin/suexec"
-D DEFAULT_PIDLOG="/opt/homebrew/var/run/httpd/httpd.pid"
-D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
-D DEFAULT_ERRORLOG="logs/error_log"
-D AP_TYPES_CONFIG_FILE="/opt/homebrew/etc/httpd/mime.types"
-D SERVER_CONFIG_FILE="/opt/homebrew/etc/httpd/httpd.conf"
The example above is from a Homebrew-installed Apache on macOS. The fields you should report when filing an issue are:
The Apache version, from
Server version.The MPM, from
Server MPM. Common values areevent(the Apache 2.4 default on most Linux distributions),worker, andprefork.The architecture and APR/APR-UTIL versions, from the matching fields.
The location of the Apache executable depends on how Apache was
installed: /usr/sbin/httpd (RHEL family), /usr/sbin/apache2
(Debian/Ubuntu), and /opt/homebrew/bin/httpd (Homebrew on
Apple Silicon macOS) or /usr/local/bin/httpd (Homebrew on
Intel macOS) are all common.
The -D block in the output is a curated subset of compile-time
defines, not the full set of configure arguments. If you need
the actual configure invocation that was used to build Apache,
look for a config.nice file in Apache’s build directory.
config.nice is generated alongside the build and recorded for
reference. apxs -q installbuilddir reports where Apache thinks
the build directory is:
$ apxs -q installbuilddir
/opt/homebrew/opt/httpd/lib/httpd/build
$ cat /opt/homebrew/opt/httpd/lib/httpd/build/config.nice
Apache modules loaded
Apache modules can be statically compiled into the httpd binary
or loaded dynamically from configuration. httpd -l lists the
statically compiled set:
$ httpd -l
Compiled in modules:
core.c
mod_so.c
http_core.c
On almost all modern Apache builds this is a small fixed set
including mod_so.c, which is the module that loads other
modules dynamically.
httpd -M lists every module that will be loaded for the current
configuration — both the statically compiled modules and any
LoadModule directives in the active config files:
$ httpd -M
Loaded Modules:
core_module (static)
so_module (static)
http_module (static)
mpm_prefork_module (shared)
authn_file_module (shared)
authn_core_module (shared)
authz_host_module (shared)
authz_groupfile_module (shared)
authz_user_module (shared)
authz_core_module (shared)
access_compat_module (shared)
auth_basic_module (shared)
reqtimeout_module (shared)
filter_module (shared)
mime_module (shared)
log_config_module (shared)
env_module (shared)
headers_module (shared)
setenvif_module (shared)
version_module (shared)
unixd_module (shared)
status_module (shared)
autoindex_module (shared)
dir_module (shared)
alias_module (shared)
wsgi_module (shared)
The names match what would be used in a LoadModule directive,
not the module file names on disk. The order in which modules
appear matters when two modules can both handle a request and
neither explicitly designates a relative dispatch order.
Confirm that wsgi_module is present in the list. If it isn’t,
either the LoadModule directive is missing or Apache is
rejecting the load — check the Apache error log for a load-time
diagnostic.
Cross-process mutex
Apache uses a global cross-process mutex to serialise which child
process accepts the next incoming connection. mod_wsgi separately
uses a similar mutex per daemon process group to serialise which
daemon-process worker accepts the next request proxied to the
group. The mutex mechanism — flock, fcntl, sysvsem,
posixsem, or pthread — varies by platform and can be
overridden in configuration.
Under normal circumstances the platform default is fine and you should not need to override it. The information becomes relevant when reporting an issue that involves stalls or deadlocks between Apache children and mod_wsgi daemon workers.
Which mechanism Apache uses by default for the accept mutex can be
read from the -D block in the httpd -V output above — the
APR_USE_*_SERIALIZE lines describe the platform default. In the
example above the lines are:
-D APR_USE_SYSVSEM_SERIALIZE
-D APR_USE_PTHREAD_SERIALIZE
The USE lines are interpreted in order, so this example shows
sysvsem as the default. mod_wsgi uses the same default for its
daemon-process-group mutex.
The default can be overridden for Apache itself via the Mutex
directive (Apache 2.4 replaced the older AcceptMutex directive
with a more general Mutex directive that takes a mutex name).
For mod_wsgi’s daemon-process-group mutex, the override directive
is WSGIAcceptMutex instead;
Mutex does not affect mod_wsgi.
The set of mechanisms available on a given platform is whichever
APR_HAS_*_SERIALIZE macros are defined to 1 in apr.h.
The simplest way to discover the available set without locating
apr.h is to put a deliberately invalid value into the
configuration and run httpd -t:
$ httpd -t
AH00526: Syntax error on line N of /path/to/httpd.conf:
Invalid Mutex argument xxx (Mutex mechanisms are: 'none', \
'default', 'flock:/path/to/file', 'fcntl:/path/to/file', \
'file:/path/to/file', 'sysvsem', 'posixsem', 'sem')
The same trick works for WSGIAcceptMutex once mod_wsgi is
loaded:
httpd: Syntax error on line N of /path/to/httpd.conf: \
Accept mutex lock mechanism 'xxx' is invalid. Valid accept \
mutex mechanisms for this platform are: default, flock, \
fcntl, sysvsem, posixsem.
default defers the choice back to APR.
Python installation in use
Even when mod_wsgi.so links to the right libpython, the
Python interpreter still needs to find the installation — the
matching lib/pythonX.Y/ directory containing the standard
library and any installed packages.
The embedded Python locates this by searching PATH for its own
executable name and taking the parent of where it is found as
sys.prefix. Apache typically inherits a minimal PATH, so
on hosts with multiple Python installations the wrong one can be
picked up.
The most reliable way to confirm what Python is actually in use at
runtime is to deploy the diagnostic WSGI script below and look at
the sys.prefix and sys.version lines.
If the discovered installation is wrong, override the choice with
the WSGIPythonHome directive,
pointing at the sys.prefix of the Python you want used:
>>> import sys
>>> print(sys.prefix)
/usr/local
WSGIPythonHome /usr/local
The full discussion of multi-Python hosts and how the embedded Python finds its installation is in Installation Issues under “Multiple Python Versions”.
Diagnostic WSGI script
The remaining checks — what process group, application group, and threading model the WSGI application is running under, plus the Python identity values from the previous section — can all be captured with one diagnostic WSGI script:
import sys
def application(environ, start_response):
lines = [
"mod_wsgi.process_group = %r" % environ["mod_wsgi.process_group"],
"mod_wsgi.application_group = %r" % environ["mod_wsgi.application_group"],
"wsgi.multithread = %r" % environ["wsgi.multithread"],
"wsgi.multiprocess = %r" % environ["wsgi.multiprocess"],
"sys.version = %r" % sys.version,
"sys.prefix = %r" % sys.prefix,
"sys.path = %r" % sys.path,
]
body = ("\n".join(lines) + "\n").encode("utf-8")
start_response("200 OK", [
("Content-Type", "text/plain"),
("Content-Length", str(len(body))),
])
return [body]
Mount the script and visit its URL. The output values are interpreted as follows.
mod_wsgi.process_group — name of the daemon process group the
request was dispatched to. An empty string means embedded mode
(the WSGI application is running inside an Apache child process,
not a mod_wsgi daemon process). A non-empty string is the name of
the daemon process group named by the active WSGIDaemonProcess
/ WSGIProcessGroup configuration. Embedded mode is also what
WSGIProcessGroup %{GLOBAL} selects explicitly.
mod_wsgi.application_group — name of the Python sub-interpreter
the application is running in. An empty string means the main
interpreter (%{GLOBAL}). The default when WSGIApplicationGroup
is not set is %{RESOURCE}, which produces a value composed
from the server name, the connection port (omitted for ports 80
and 443), and the WSGI mount point — for example:
mod_wsgi.application_group = 'tests.example.com|/interpreter.wsgi'
wsgi.multithread — True if the WSGI application is running
in a multithreaded environment, False if not. Daemon mode
defaults to multithreaded. Embedded mode is multithreaded under
the Event and Worker MPMs and single-threaded under the Prefork
MPM. If True, the application code and any framework it uses
must be thread-safe.
wsgi.multiprocess — True if multiple processes may be
serving requests for the application. Daemon mode is multiprocess
when processes=N is set with N>1, otherwise single-process.
Embedded mode is always multiprocess (each Apache child handles
requests independently). Application-state caches that need to be
shared across requests must allow for both multithread and
multiprocess being True simultaneously.
sys.version, sys.prefix, sys.path — the Python installation and module search path actually in use at runtime, as discussed in the previous section.
Where to go next
Installation Issues — known build and runtime failure modes if any of the checks above point at a problem.
Debugging Techniques — broader debugging techniques for WSGI applications running under mod_wsgi.
Configuration Issues — common Apache configuration pitfalls.