In PHP, what happens to a running web request when the client disconnects, and how do ignore_user_abort() and connection_aborted() change that?
answer
- abort noticed on next output
- ignore_user_abort ini default 0
- function returns previous setting as int
- connection_status bits 1 and 2
- time limit still applies
basics
~20 sBy default PHP aborts a request once it notices the client has gone, which happens when it next tries to send output; shutdown callbacks still run. ignore_user_abort(true) lets the script finish, and connection_aborted() tells code whether the client left.
solid answer
~40 sPHP does not watch the socket continuously: it learns the client disconnected when it next **tries to send output**. At that point it sets the connection status to `CONNECTION_ABORTED`, stops sending output and, unless `ignore_user_abort` is on, **aborts the script**; registered shutdown callbacks still run. The `ignore_user_abort` ini directive defaults to `0`. Calling `ignore_user_abort(true)` flips it for the request and returns the **previous** setting as an `int`. With it on, the script runs to completion, and code can poll `connection_aborted()` (returns `1` or `0`) or `connection_status()`, a bitfield where `1` is aborted and `2` is timeout. `max_execution_time` still applies either way, so a script that ignores the abort can still be stopped by the time limit.
code
php · 14 lines<?php
declare(strict_types=1);
// $pdo, $visitId, $seats and $chargeId come from the booking handler.
$previous = ignore_user_abort(true); // int: 0 or 1
$pdo->beginTransaction();
reserveSeats($pdo, $visitId, $seats);
recordPayment($pdo, $visitId, $chargeId);
$pdo->commit();
if (connection_aborted() === 1) {
error_log("Booking {$visitId} completed after the client left");
}go deeper
Know that a client closing the page can stop a PHP script partway, and that ignore_user_abort(true) makes it run to the end.
Explain that the disconnect is noticed only on output, what ignore_user_abort returns, and how connection_aborted() and connection_status() report the state.
Protect multi-step writes with transactions rather than relying on ignore_user_abort alone, use the timeout and abort bits in shutdown logging, and push long work to background jobs.
Decide which request paths must be abort-proof and which should stop early to save capacity, and where work should leave the request cycle entirely.
## The problem A museum's ticket-booking page takes several seconds to reserve seats, charge a card and write the booking. A visitor gets impatient and closes the tab halfway through. Does the booking complete? In PHP the answer depends on **when** PHP notices, and on one setting. ## How PHP notices a disconnect PHP keeps a **connection status** for each request. It is not updated by a background watcher: the engine finds out the client has gone when it **tries to write output** to it and the write fails. Until then, a script that is busy calling a database or an API keeps running as if nothing happened. When the failed write is detected, PHP: 1. sets the status flag `CONNECTION_ABORTED`; 2. disables further output for the request; 3. if `ignore_user_abort` is **off**, aborts the script at that point; 4. runs the request shutdown, including any callbacks registered with `register_shutdown_function()`. Because detection depends on output, output buffering matters: while output sits in a buffer, nothing is written to the client and nothing can fail. ## The settings and functions | Name | Kind | What it does | |---|---|---| | `ignore_user_abort` | ini directive, `PHP_INI_ALL`, default `0` | when `1`, a disconnect does not abort the script | | `ignore_user_abort(?bool $enable = null): int` | function | sets the directive for this request when given a bool; always returns the **previous** value as `0` or `1` | | `connection_aborted(): int` | function | returns `1` if the client disconnect has been detected, else `0` | | `connection_status(): int` | function | returns a bitfield of `CONNECTION_NORMAL` (0), `CONNECTION_ABORTED` (1) and `CONNECTION_TIMEOUT` (2) | Note that `connection_aborted()` is declared to return `int`, not `bool`, so compare it with `=== 1` or cast it rather than `=== true`. ## Aborted and timed out at the same time With `ignore_user_abort` on, PHP records the disconnect but keeps running. If the script then exceeds `max_execution_time`, it is stopped by the time limit and `connection_status()` returns `3`: both the aborted and the timeout bits are set. A shutdown callback can read that value to tell a timeout from a normal finish. ## When to ignore aborts Use `ignore_user_abort(true)` for work that must not stop halfway because a browser closed: - completing a payment and writing the booking row; - a webhook receiver that must record an event once it has started; - cleanup that leaves data inconsistent if interrupted. Even then, it is not a substitute for real atomicity. Wrap multi-step writes in a database transaction so an abort, a timeout or a crash rolls back cleanly, and move genuinely long work to a background job queue rather than stretching a web request. ## When to check and stop early The opposite case is expensive work nobody will see: generating a large report or streaming a big export. There you can leave aborts ignored, check `connection_aborted()` between chunks after flushing output, and stop early to save resources. ## A timeline of one aborted booking 1. The visitor submits the booking form; the script starts reserving seats. 2. The visitor closes the tab. PHP does not notice: the script is waiting on the database. 3. The script echoes a progress line and flushes it. The write to the closed connection fails, and PHP sets `CONNECTION_ABORTED`. 4. With `ignore_user_abort` off, the script is aborted here, between the seat reservation and the payment record. With it on, the script carries on and commits. 5. Either way, request shutdown runs, and any shutdown callback can check `connection_aborted()` to log what happened. The timeline shows why the setting alone is not the safety net: a transaction is what keeps step 4 from leaving half a booking behind. ## Common misunderstandings - **"PHP stops the instant the tab closes."** It stops, at the earliest, when it next writes output; a script stuck in a slow query keeps going. - **"ignore_user_abort(true) returns true on success."** It returns the previous setting as an `int`. - **"Ignoring aborts means the script can run forever."** `max_execution_time` still applies. - **"Shutdown callbacks are skipped on abort."** They run on an abort just as on a normal finish, so use `connection_aborted()` inside one to tell the cases apart.
- A script is inside a 20-second query when the visitor closes the tab. When does PHP react?Not during the query. PHP detects the disconnect only when it next tries to write output to the client. The query finishes, and the abort takes effect at the next output that reaches the connection, unless ignore_user_abort is on.
- What does connection_status() return after an ignored abort followed by a timeout?3. With ignore_user_abort on, PHP records CONNECTION_ABORTED (1) and keeps running; when max_execution_time then stops the script, CONNECTION_TIMEOUT (2) is added, and a shutdown callback sees both bits.
saying these in an interview costs you the question
- PHP stops a script the instant the browser tab is closed.
- ignore_user_abort(true) also lifts max_execution_time for the request.
- Shutdown functions are skipped when the client disconnects.
- connection_aborted() returns a bool, so === true is the right check.
- ignore_user_abort is on by default in php.ini-production.