PHP compatibility and project lifecycle
Per the PHP manual, as of PHP 8.4.0 the apache2handler SAPI requires Apache 2.4 or newer, and non-ZTS mod_php needs the prefork MPM. The supplied sources do not confirm PHP 8.5 behavior or a php:8.4-apache tag listing. Verify the tag and the lifecycle yourself.
Official sources checked 2026-10-22. Browser lessons on this site run PHP 8.5.
What this is
Apache mod_php is not a standalone PHP runtime. It is PHP built as a server API (SAPI) module that is loaded into the Apache httpd process. The language engine is still PHP core. Apache supplies the HTTP server, and the module hands matching requests to PHP. The PHP manual calls the SAPI apache2handler.
It also sits below the application layer. Drupal is a CMS product that runs on PHP, and Laravel and Symfony are frameworks and components that run on PHP. None of them is part of mod_php, and mod_php does not provide their setup. Async libraries, framework adapters and alternative interpreters are different categories again. HHVM targets Hack and should not be treated as a modern PHP-compatible alternative.
Lifecycle and status
The PHP manual states plainly: do not use mod_php for new installations. It keeps the instructions for historical reference and for rare cases where embedding PHP in Apache httpd is specifically required. For modern deployments it recommends PHP-FPM with Apache's mod_proxy_fcgi. The supplied sources do not give a formal support or deprecation timeline, so this guide records the lifecycle as unknown. Treat mod_php as a legacy or learning topic, not a default for new production work.
Architecture
With mod_php, PHP is embedded directly into every Apache httpd worker process. The manual says that unless PHP is compiled with thread safety (--enable-zts), mod_php requires the prefork MPM, which significantly limits concurrency. Most distribution packages are not built with ZTS, so prefork is typically required.
PHP-FPM is the alternative. PHP runs as a separate FastCGI process manager and Apache talks to it through mod_proxy_fcgi. The manual credits this with better resource management, process isolation, independent PHP restarts and compatibility with Apache's event MPM.
The manual also says that as of PHP 8.4.0 the apache2handler SAPI requires at least Apache 2.4. Support for Apache 2.0 and 2.2 was removed.
The official Docker image has a variant named php:<version>-apache. The Docker Hub page describes it as Debian's Apache httpd with PHP as mod_php, using mpm_prefork by default.
Execution and content model
Apache maps requests to PHP through handler configuration. The manual uses a FilesMatch block with SetHandler application/x-httpd-php. It prefers that over a bare AddType so files such as exploit.php.jpg are not executed as PHP. For front-controller routing it describes FallbackResource combined with an If check, so the handler applies only when the file exists.
This is the classic request lifetime. A request arrives, a worker runs the PHP script, a response is sent, and request-scoped PHP state is normally discarded. The supplied sources do not describe shutdown in detail, so confirm the specifics for your build. Extension-level and process-level resources can behave differently from userland variables.
Classic requests versus persistent workers
Persistent-worker runtimes keep a PHP process alive and reuse it across requests. Swoole and Open Swoole are separate projects, so check each one's own documentation. Neither is covered by the sources for this page. In that model, anything held in static properties, singletons, global variables, caches or service containers can leak from one request to the next. Examples are the current user, a request object, a half-built response or an open transaction.
Typical cleanup is to keep request data out of long-lived objects, reset or rebuild request-scoped services for every request, close connections and transactions in a finally path, and recycle workers on a schedule. Apache with mod_php does not make this a primary concern because your script starts afresh for each request. The example below shows that: a static counter always reads 1.
Practical use cases
- Learning how a SAPI connects PHP to a web server.
- Reproducing a legacy Apache plus mod_php environment on a local machine.
- Working on an application that specifically needs PHP embedded in Apache, which the manual calls rare.
For new services, follow the manual and look at PHP-FPM.
Installation approach
The easiest route is the official Docker image, shown in the example. The Docker Hub page documents the php:<version>-apache variant and a Dockerfile pattern that copies your code into /var/www/html/. Its examples use older tags such as php:7.2-apache. The supplied packet does not enumerate tags, so confirm that php:8.4-apache exists before relying on it.
The manual also documents a native source build:
- Unpack and build Apache httpd with
--enable-so --with-mpm=prefork. - Unpack PHP and configure it with
--with-apxs2pointing at Apache'sapxs. The tool may be namedapxs2on some distributions. - Run
makeandmake install. This also installs PEAR, tools such asphpize, and the PHP CLI. - Copy
php.ini-developmentorphp.ini-productioninto place. - Make sure
httpd.confhas aLoadModule php_module modules/libphp.soline. - Add the handler configuration described above and restart Apache.
The manual warns against enabling the .phps source filter on production systems because it can expose sensitive source code.
Strengths
- The setup is simple. PHP runs inside Apache with no separate FastCGI service.
- The official image offers a ready-made Apache plus PHP variant.
- It is useful for understanding SAPIs and for legacy compatibility.
The supplied sources make no speed claims, and none are made here.
Limitations
- The manual explicitly discourages new mod_php installations.
- Without ZTS, prefork is required, which limits concurrency.
- PHP cannot be restarted independently of Apache, and PHP-FPM is the documented answer to that.
- Version support beyond PHP 8.4's Apache 2.4 requirement is unknown from these sources, including PHP 8.5 behavior.
- No framework or CMS setup is documented in these sources. Follow the official documentation of the application you deploy.
Deployment notes
Bind development ports to 127.0.0.1, as the Compose file does. The Docker Hub page recommends the production php.ini for production images and suggests OPcache for many production environments. To run the Apache variants as an arbitrary user, use either an unprivileged Apache port or the sysctl option the page documents. If you ever use the FPM image variant instead, never publish its FastCGI port outside a private container network.
Example walkthrough
The example builds from php:8.4-apache, copies one script into the document root and publishes port 8080 on loopback only. The script prints the SAPI name, the PHP version, the server software string, the process ID and a static counter. Running it needs Docker with Compose and network access to pull the image. It is not a browser exercise.
Links
Use the PHP manual page for the Apache 2.x build and its warnings, and the Docker Hub page for image variants, configuration and Apache usage.
Practical examples
These examples have not been run in a live environment yet. Commands and versions follow the official documentation linked on this page; check them before relying on them, and report a problem if one does not work.
php:8.4-apache with Docker Compose on loopback
Builds a small image from the official Apache variant, copies a single PHP script into /var/www/html/, and publishes it on 127.0.0.1:8080. The script shows the SAPI name and that a static counter resets for every request, which is the classic request lifetime. Requires Docker with Compose and network access. Not a browser exercise.
Dockerfile
FROM php:8.4-apache
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY src/ /var/www/html/
compose.yaml
services:
web:
build: .
ports:
- "127.0.0.1:8080:80"
src/index.php
declare(strict_types=1);
final class RequestCounter
{
public static int $count = 0;
}
RequestCounter::$count++;
header('Content-Type: text/plain; charset=utf-8');
echo 'SAPI: ' . PHP_SAPI . "\n";
echo 'PHP version: ' . PHP_VERSION . "\n";
echo 'Server software: ' . ($_SERVER['SERVER_SOFTWARE'] ?? 'unknown') . "\n";
echo 'Process ID: ' . getmypid() . "\n";
echo 'Static counter in this request: ' . RequestCounter::$count . "\n";
echo 'Request time (UTC): ' . gmdate('c') . "\n";
Install and run
docker compose up --build -d
curl http://127.0.0.1:8080/
curl http://127.0.0.1:8080/
docker compose exec web php -v
docker compose exec web php -m
docker compose downExpected behavior
Each request to http://127.0.0.1:8080/ returns plain text. The SAPI line is expected to read apache2handler and the PHP version should be an 8.4 release if the tag exists. The static counter reads 1 on every request because classic PHP request state is not kept between requests. The process ID may repeat or differ depending on which Apache prefork child serves the request.
Needs Docker, Compose and network access to pull the image. The php:8.4-apache tag is taken from the assignment and is not listed in the supplied Docker Hub packet, so confirm it exists. The code is copied at build time, so rebuild after edits. This is a local demonstration, not a production setup. The PHP manual recommends PHP-FPM for new installations.