Two cards. A PHP-FPM pool of five workers where one is held by a slow request and the other four keep serving. One Node.js event loop blocked by fs.readFileSync, every other client waiting in line, dropping from 200 to 20 requests per second in the worked example.

PHP vs JavaScript web development: choose by how each one serves a request

Most comparisons stop at server-side versus client-side. The split that matters is how each language holds a request, because that sets how many users one server can take and what one slow call costs everyone else.

What is the difference between PHP and JavaScript?#

PHP runs on the server and gives every request its own worker, while JavaScript runs in the browser and, through Node.js, on servers inside one shared event loop. The php.net manual calls PHP a general-purpose "scripting language that is especially suited for web" development. Also, the Node.js project's about page calls Node.js an "asynchronous event-driven JavaScript runtime", a program that runs JavaScript outside the browser.

Put as PHP vs JS, JS vs PHP or JavaScript vs PHP, the question sounds like one of syntax. But the difference between JavaScript and PHP that shapes a real system is how each one holds a request while it waits. A PHP worker waits with the request. But a Node.js event loop moves on to the next one.

So this guide works in that frame. First come the two mechanisms, then the arithmetic on one request, then a choice per layer.

One worker per request, or one loop for allOne worker per request under PHP-FPM; one event loop for all requests under Node.js. Source: php.net manual and Node.js about page, read 8 October 2026.

Why does the execution model matter more than the syntax?#

In the worked example below, the same 50 ms request gives a 5-worker PHP-FPM pool a ceiling of 100 requests per second and one Node.js event loop a ceiling of 200. Both numbers come from one request in the Node.js guide to blocking. It takes 50 ms, and 45 ms of that is database I/O (input and output) that can run without holding the thread.

Because a PHP-FPM worker stays with its request for all 50 ms, one worker serves 20 requests a second. Five workers, the default in the PHP source, give the 100. Meanwhile the event loop is busy for only the 5 ms of JavaScript in each request. So in the worked example, one loop serves 200.

At the default settings, one Node.js event loop has twice the headroom of the default PHP-FPM pool for an I/O-heavy request.

Ceiling for the same 50 ms requesttwice the headroom

100

PHP-FPM default pool (5 workers)

200

One Node.js event loop, non-blocking

At the default settings, one Node.js event loop has twice the headroom of the default PHP-FPM pool for an I/O-heavy request.

Ceiling for the same 50 ms request (requests per second, worked example)
Optionrequests per second, worked example
PHP-FPM default pool (5 workers)100
One Node.js event loop, non-blocking200

Source: arithmetic from php-src (pm.max_children = 5) and the Node.js docs' 50 ms request, read 8 October 2026

That gap is not the whole story, and it can flip. If you add workers, the PHP ceiling climbs in a straight line. But if you block the loop once, the Node.js ceiling falls by ten times. So the model decides two things a tech lead reports upward. One is how many requests a server holds; the other is what a single slow call costs everyone else. Syntax decides neither.

What does PHP stand for, and where does each language run?#

PHP stands for PHP: Hypertext Preprocessor, a recursive acronym, and JavaScript is an implementation of the ECMAScript specification that runs in every browser and, through Node.js, on servers. The PHP full form changed once. The php.net history of PHP says Rasmus Lerdorf created the first version in 1994 and named it "Personal Home Page Tools". Then, with PHP 3.0, the name became the recursive acronym it is today.

JavaScript has no full form to learn, but it has a standard. MDN says the standards for JavaScript are the ECMAScript Language Specification (ECMA-262) and its internationalization API. Ecma's TC39 committee, the group that evolves the language, publishes the living draft. What the latest yearly edition added is in our ECMAScript 2026 guide.

Where each runs is simpler than it looks. MDN's introduction to JavaScript says server-side code runs on the server and sends its results to the browser. It lists PHP among the common server-side languages, "and even JavaScript", for example through Node.js. So PHP runs on the server only. JavaScript runs in the browser and, when you choose Node.js, on the server too.

Also, for PHP, the version you meet matters as much as the name. W3Techs counts version 8 on 64.6% of websites that use PHP and version 7 on 27.6%, as of 8 October 2026.

Show data table
Most PHP on the web is a current major version: version 8 at 64.6%, version 7 at 27.6%, version 5 at 7.8%, version 4 at 0.1% of websites that use PHP. Source: W3Techs, 8 October 2026.
Segment Value (%) Share
Version 8 64.6 64.5%
Version 7 27.6 27.6%
Version 5 7.8 7.8%
Version 4 0.1 0.1%

Version 8 runs on 64.6% of websites that use PHP, and version 7 on 27.6%.

PHP versions in use Most PHP on the web is a current major version: version 8 at 64.6%, version 7 at 27.6%, version 5 at 7.8%, version 4 at 0.1% of websites that use PHP. Source: W3Techs, 8 October 2026. W3Techs, 8 October 2026

How does a PHP request run under PHP-FPM?#

Under PHP-FPM, each request holds one worker process from start to finish, and the pm.max_children setting caps how many requests the pool serves at once. In practice, PHP-FPM keeps a pool of PHP worker processes behind your web server. The php.net FPM configuration reference says of pm.max_children: "This option sets the limit on the number of simultaneous requests" that will be served.

The shipped defaults are small on purpose. For example, the default pool file in the PHP source sets pm = dynamic and pm.max_children = 5. Its own comment says those defaults are based on a server without much resources. It also asks you to tweak them to fit your needs.

Each request starts in a clean worker. Once it ends, nothing it created is left behind, and that is the share-nothing model. If one request hits a fatal error or leaks memory, only its own worker is hurt. Then the next request starts fresh.

Then why is starting fresh not slow? OPcache, the bytecode cache built into PHP, answers that. The php.net OPcache manual says it stores precompiled script bytecode in shared memory. As a result, PHP does not load and parse scripts on each request.

The cost of the model is waiting. While a query runs, the worker sits idle but stays taken. So in the worked example, a worker spends 45 of its 50 ms waiting on the database. All that time, it cannot take a new request.

A request's life in one PHP-FPM workerOne PHP-FPM worker holds a request from arrival to response, including the database wait. Source: php.net FPM configuration and OPcache manual, read 8 October 2026.

How does the Node.js event loop serve many connections on one thread?#

Node.js runs all of your JavaScript on one thread, so a request costs the event loop only the time its code runs, not the time it waits on I/O. The event loop is the cycle Node.js repeats to pick up finished I/O and run the callback waiting for it. The Node.js event loop guide splits that cycle into phases, such as timers for scheduled code and poll for new I/O.

Back on the about page, Node.js says almost no function performs I/O directly. It adds that "the process never blocks except when the I/O is performed using synchronous methods". When a request asks the database for a row, Node.js hands the wait to the system and serves the next request. When the row comes back, the callback runs.

  1. Run the JavaScript

    Node.js runs all of your JavaScript on one thread.

  2. Hand off the wait

    When a request asks the database for a row, Node.js hands the wait to the system.

  3. Serve the next request

    The loop moves on to the next one while the row is on its way.

  4. Run the callback

    When the row comes back, the callback runs.

The Node.js guide Don't Block the Event Loop puts the bargain plainly. "Node.js uses a small number of threads to handle many clients." Then it adds that Node.js is fast when the work for each client is small. For tasks with no non-blocking form, such as some file I/O, Node.js keeps a Worker Pool, a small set of helper threads.

So can JavaScript replace PHP on the server? Yes. The about page calls HTTP "a first-class citizen in Node.js", so it serves web requests directly. The question is not whether it can, but whether its one-thread model suits the work you give it.

How many requests can each model hold at once?#

In the worked example, a 50 ms request held by each worker gives a PHP-FPM pool 100 requests per second for every 5 workers, while one event loop reaches 200. The rule for each is short. In the worked example, a PHP-FPM ceiling is workers times 1,000 divided by the request time in ms. And in the same worked example, a Node.js ceiling is 1,000 divided by the ms the request spends running JavaScript.

For example, double pm.max_children to 10 and the PHP-FPM ceiling for the 50 ms request doubles to 200. At 20 workers it reaches 400, and at 40 it reaches 800. Meanwhile the event loop stays at 200 however many workers you add, because it only counts its own 5 ms.

PHP-FPM and Node.js ceilings for the 50 ms request by pm.max_children. Source: worked example from php-src and the Node.js docs; illustrative ceilings, not measurements.

pm.max_childrenPHP-FPM ceiling (requests per second)one Node.js loop, non-blocking
5100200
10200200
20400200
40800200

Source: worked example from the pm.max_children default in php-src and the Node.js docs' 50 ms request with 45 ms of I/O; illustrative ceilings, not measurements.

These are ceilings, not promises. Because a PHP worker uses memory, the server's RAM caps pm.max_children long before the arithmetic does. And one Node.js process uses one core for JavaScript. However, the about page notes the cluster module, which lets several processes share sockets and so use more cores.

In practice, measure your own request time and I/O share, then run the same two lines. If the request spends most of its time waiting, a loop wins on headroom per process. But if it spends most of its time computing, one loop falls behind a pool whose workers spread across several cores.

Your pool against one event loop

Start from the worked example: 5 workers, a 50 ms request, 45 ms of it I/O. Change any input to see both ceilings.

Your request and your pool

measured on this project

Ceilings only. Memory caps pm.max_children and one Node.js process uses one core for JavaScript, so measure your own request first.

PHP-FPM ceiling, requests per second

100

One Node.js loop ceiling, requests per second
200

Arithmetic from the php-src default pool and the Node.js docs' example request. Modelled, not measured.

What happens when one call blocks the Node.js event loop?#

In the worked example, making that 50 ms request run synchronously drops one Node.js process from 200 to 20 requests per second, while each PHP-FPM worker still serves its own request. The Node.js guide to blocking says why: "the event loop is unable to continue running JavaScript while a blocking operation is occurring."

Show data table
the same request, written two ways. Source: arithmetic from the Node.js docs' 50 ms example (OpenJS Foundation), read 8 October 2026
Item Value
non-blocking: 5 ms of JavaScript per request 200
blocking: all 50 ms held on the loop 20

Holding all 50 ms on the loop cuts one Node.js process from 200 to 20 requests per second.

One loop, the read written two ways the same request, written two ways. Source: arithmetic from the Node.js docs' 50 ms example (OpenJS Foundation), read 8 October 2026 OpenJS Foundation (Node.js)

So a synchronous call such as fs.readFileSync holds the only thread until it returns. Every other client waits in line, even one whose own work would take a millisecond. The don't-block guide adds a security angle. If some input can make a callback slow, a malicious client can send that input on purpose. As a result, one request can deny service to everyone.

PHP-FPM cannot fail this way. Instead, a slow request holds one worker, and the other four in the default pool keep serving. So the pool loses a fifth of its capacity, not all of it. However, PHP pays for that safety every time a worker waits, which is the trade from the start.

Which official pages should you build from?#

Build from the php.net FPM configuration reference and the default pool file in php-src first, then the Node.js guides on blocking and on the event loop. Read them in this order, taking one thing from each:

What is the smallest working setup for each?#

The smallest working setup is a PHP-FPM pool with an explicit pm.max_children, and a Node.js handler that calls the non-blocking fs.readFile instead of fs.readFileSync. For PHP, set these lines in the pool file your distribution already ships (often www.conf), then restart PHP-FPM.

ini
; Step 1: the pool and its process manager mode
[www]
pm = dynamic
; Step 2: set the worker cap on purpose, sized from your request time
; (20 workers on a 50 ms request is a 400 requests per second ceiling)
pm.max_children = 20
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

For Node.js, the change is one call. The commented line is the blocking form the Node.js docs warn about.

javascript
// Step 3: read the file without holding the event loop
const fs = require('node:fs');

// Blocking: every other client waits until this returns
// const data = fs.readFileSync('/file.md');

fs.readFile('/file.md', (err, data) => {
  if (err) {
    throw err;
  }
  console.log(data.length);
});

Since both snippets follow the official docs, you can check each line there. The pool lines match the php-src default file, and the read matches the Node.js blocking guide.

PHP vs JavaScript web development: which should you choose for each layer?#

W3Techs counts PHP on 69.7% of websites whose server-side language it knows and JavaScript on 7.6%, as of 8 October 2026, so the choice is per layer, not per company. As a result, PHP hosting and PHP developers are easy to find. But server-side JavaScript developers are a smaller pool, separate from browser JavaScript.

Show data table
the server side only; JavaScript in the browser is not counted here. Source: W3Techs, 8 October 2026
Item Value
PHP 69.7%
JavaScript 7.6%
Ruby 7.1%
Java 5.5%
Scala 5%
ASP.NET 4.2%
Python 1.1%

PHP runs on 69.7% of websites whose server-side language W3Techs knows, and JavaScript on 7.6%.

Server-side language share the server side only; JavaScript in the browser is not counted here. Source: W3Techs, 8 October 2026 W3Techs, 8 October 2026

Here is the rule, layer by layer:

  • Content and commerce sites. Pick PHP. Each request is short and self-contained, which is what share-nothing does best. The next decision inside PHP is the framework, covered in WordPress vs Laravel in 2026.
  • APIs that mostly wait on other services. Either works. A Node.js loop gives more headroom per process when most of the time is I/O. A PHP-FPM pool gets there with more workers and more memory.
  • Real-time features such as chat or live dashboards. Pick JavaScript on Node.js. A held connection is nearly free on the loop, while on PHP-FPM it takes a whole worker.
  • The browser. JavaScript is the only choice, and the real question is how much of it to send. htmx vs React weighs server-rendered HTML against a JavaScript client, and React rendering on Cloudflare covers where that rendering runs.

So the answer to PHP vs JavaScript for web development is a rule per layer, not a winner. Is it an either-or choice, then? No. In practice, many sites run a PHP back end with JavaScript in the browser. Some add a small Node.js service just for the real-time part.

When is each one the wrong tool?#

PHP-FPM is the wrong tool for long-lived connections such as chat or live dashboards, and a Node.js event loop is the wrong tool for heavy CPU work on its one thread. Each model has a case it handles badly, and knowing it saves a rewrite.

First, long-lived connections on PHP-FPM. Since every open connection holds a worker, a pool of 20 workers fills up with 20 idle chat users. Instead, run that part on Node.js, or on a separate real-time service, and keep PHP for the pages.

Second, long jobs inside a PHP request. For example, a report that takes a minute holds its worker for that minute. Worse, a timeout may cut it off. Instead, move it to a queue worker; Laravel queues at scale covers how to run those reliably.

Third, CPU-heavy work on the Node.js loop. For instance, image resizing, big JSON parsing or encryption in a callback stalls every client, as the worked example shows. Instead, follow the don't-block guide. Split the work into small steps, or move it to a separate pool built with Child Process or Cluster.

Each model's wrong-tool cases and what to use instead, from the three cases above. Source: the Node.js guide Don't Block the Event Loop and the php.net FPM configuration reference.

CaseWhy it breaksInstead
Long-lived connections on PHP-FPMevery open connection holds a workerrun that part on Node.js or a separate real-time service
Long jobs inside a PHP requestthe job holds its worker, and a timeout may cut it offmove it to a queue worker
CPU-heavy work on the Node.js loopthe work stalls every clientsplit it into small steps, or move it to Child Process or Cluster

Where should you go next?#

Choose the server language per layer, size the pool or guard the loop with the arithmetic above, and take the next decision from the posts linked here. If Node.js is on your list, building a Node.js HTTP/1.1 server shows what one process does with a raw connection. If PHP is, modern PHP features shows what the language gives a team today, and WordPress vs Laravel is the choice after that.

When you want help on either side, there are pages on how we work with PHP developers and Node.js developers. But the four official pages above are enough on their own to size a pool or fix a blocked loop.

Questions this post answers

What is the full form of PHP?
PHP stands for PHP: Hypertext Preprocessor, a recursive acronym. The php.net history page says the first version, from 1994, was called Personal Home Page Tools. The current name arrived with PHP 3.0.
PHP or JavaScript: which is better for web development?
Neither wins everywhere. PHP fits short request and response layers such as content and commerce sites. But JavaScript is the only choice in the browser, and through Node.js it fits real-time and I/O-heavy server layers.
Can JavaScript replace PHP on the server?
Yes. Node.js runs JavaScript on the server and serves web requests, so it can replace PHP for any layer. But whether it should depends on the work, because one event loop handles waiting well and heavy CPU work badly.
Is PHP vs JavaScript web development an either-or choice?
No. Most sites already use both: PHP or another language on the server, and JavaScript in the browser. Choose per layer, and add a Node.js service only where its event loop fits better.

Keep reading