How PHP Works (Request Lifecycle)
PHP is a server-side language: none of it runs in the visitor's browser. Every .php file you write is executed on a machine you control (or rent), and the only thing that ever leaves that machine is plain text — usually HTML, sometimes JSON, occasionally a raw image or file download. Understanding the path a request takes from "user clicks a link" to "browser renders a page" explains a lot of PHP's behavior that otherwise looks arbitrary: why global state does not persist between requests, why a slow script blocks one visitor and not the whole server, and why performance tuning revolves around a cache called OPcache.
The full round trip
One HTTP request through a PHP stack
Browser | 1. GET /profile.php?id=42 v Web server (Apache / Nginx) | 2. "This is a .php file — hand it to PHP" v PHP interpreter (mod_php thread OR PHP-FPM worker process) | 3. Parse profile.php top to bottom, execute statements | - may query a database | - may read/write files | - may call external APIs v PHP generates output | 4. echo / print statements build a string (HTML, JSON, ...) v Web server | 5. Wraps the output in an HTTP response (status, headers, body) v Browser 6. Renders the HTML / parses the JSON / saves the file
Nothing in that diagram is optional or reorderable. Step 3 always finishes — or the script errors out — before step 4 begins, which is why calling header('Location: ...') after you have already echoed something fails with "headers already sent": the response headers are supposed to be decided before any body content exists.
mod_php vs PHP-FPM
Step 2 and 3 — how the web server actually hands work to PHP — can happen two very different ways, and the choice matters for performance and deployment.
mod_php:PHP is compiled as a module that runs inside the Apache process itself. Each Apache worker that handles a request has a full PHP interpreter embedded in it. Simple to set up, but every Apache worker carries the memory overhead of a PHP interpreter even when serving a static image, and mod_php only works with Apache.PHP-FPM (FastCGI Process Manager):PHP runs as a separate pool of worker processes, entirely outside the web server. The web server (commonly Nginx, but Apache can use it too) receives the request and forwards it to a PHP-FPM worker over a socket, then relays the worker's output back to the browser. Because PHP is decoupled from the web server, you can scale, restart, or upgrade PHP without touching the web server, and idle web server workers stay lightweight.
Where the interpreter actually lives
mod_php (Apache only) PHP-FPM (Nginx or Apache)
Apache worker process Nginx worker process
+----------------------+ +----------------------+
| HTTP handling | | HTTP handling only |
| + embedded PHP | +-----------+-----------+
+----------------------+ | FastCGI
v
+----------------------+
| PHP-FPM worker pool |
| (separate processes) |
+----------------------+PHP-FPM is the standard choice for new deployments today, including virtually every managed hosting platform and container-based setup: it lets Nginx serve static assets extremely efficiently while routing only .php requests to a pool of PHP workers that can be sized, monitored, and restarted independently.
PHP is not a long-running process
This is the detail that most surprises developers coming from Node.js or a long-running Java/Go server. In the classic PHP model, there is no persistent application instance sitting in memory between requests. For every incoming request, the interpreter (a mod_php thread or a PHP-FPM worker) does the following, from scratch:
Read the requested
.phpfile (and everything itrequires/includes) from disk.Parse that source code into an internal representation (opcodes).
Execute the opcodes top to bottom.
Discard every variable, object, and open connection the script created.
That means a variable set near the top of a script is gone the instant the response is sent — there is no shared in-memory state across requests unless you deliberately put it somewhere external, like a database, a file, or a cache such as Redis or Memcached. Two visitors hitting the same page at the same moment get two completely independent executions of the script; they cannot see each other's local variables even for an instant.
OPcache: the exception that makes PHP fast
OPcache is a bundled PHP extension, enabled by default on most modern installs, that caches the compiled opcodes for each script in shared memory. The first request after a file changes still pays the parse-and-compile cost; every request after that skips straight to execution using the cached opcodes, as long as the file's modification time has not changed.
Confirming OPcache is active
php -i | grep -i opcache.enable # opcache.enable => On => On # opcache.enable_cli => Off => Off (usually off for one-shot CLI scripts)
OPcache does not make PHP a persistent, stateful process the way Node.js is — variables and objects from one request still vanish after that request finishes. It only removes the compilation cost, not the per-request execution model. This distinction is why tools like Swoole, RoadRunner, or FrankenPHP exist: they go further and keep a PHP application (and its bootstrapped state) alive across requests, trading the simplicity of "fresh start every time" for Node-like persistence and speed.