How to configure PHP-FPM to handle WebSocket connections on Ubuntu
This guide shows you how to set up PHP-FPM to process WebSocket requests on Ubuntu 24.04. You will install the required libraries, configure the pool, and verify the connection.
You will configure the PHP-FPM pool to accept WebSocket upgrade headers and process long-polling or SSE requests. These steps target Ubuntu 24.04 with PHP 8.3.8 and require root privileges for system package installation and file edits.
Prerequisites
- Ubuntu 24.04 LTS server with SSH access.
- PHP 8.3.x installed via the official PHP repository or APT.
- Nginx or Apache installed and running as the reverse proxy.
- Root or sudo privileges to modify the PHP-FPM pool configuration.
- At least 512 MB of available RAM for the PHP-FPM worker process.
Step 1: Install required system libraries
Install the system libraries needed to handle the WebSocket protocol stack and SSL/TLS encryption. You need the libuv and libevent libraries which PHP-FPM uses to manage non-blocking I/O for long connections.
sudo apt update
sudo apt install libuv1-dev libevent-dev libssl-dev php8.3-sockets
You will see the package manager resolve dependencies and install the libraries.
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
libuv1-dev is already the newest version (1.44.2-1build1).
libevent-dev is already the newest version (2.1.12-stable-4build1).
libssl-dev is already the newest version (3.0.2-0ubuntu1.10).
php8.3-sockets is already the newest version (8.3.8-1ubuntu20.14).
If the packages are not installed, the output will show the download progress and installation status.
Step 2: Install the PHP WebSocket extension
Install the PHP extension that provides native support for the WebSocket protocol. This extension allows PHP scripts to act as WebSocket clients and servers. On Ubuntu 24.04, you must compile the extension from source or use a PPA that supports PHP 8.3.
First, clone the repository and build the extension for PHP 8.3.
cd /tmp
git clone https://github.com/websocket-php/php-websocket.git
cd php-websocket
git checkout 2.7.x
Configure the build to match your PHP version and install the extension.
phpize
./configure --with-php-config=$(php-config)
make
sudo make install
Add the extension to your PHP configuration file.
echo "extension=websocket" | sudo tee /etc/php/8.3/mods-available/websocket.ini
Enable the extension in the main PHP configuration file.
sudo phpenable 8.3 websocket
Restart the PHP-FPM service to load the new extension.
sudo systemctl restart php8.3-fpm
Step 3: Configure the PHP-FPM pool for WebSocket
Edit the default pool configuration file to allow WebSocket upgrade headers. You must set the correct process manager and memory limits to prevent the worker from crashing under load.
Open the pool configuration file for editing.
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
Add the following lines to the pool configuration to handle the upgrade request.
request_terminate_timeout = 0
slowlog = /var/log/php-fpm/www-slow.log
pm = dynamic
pm.max_children = 15
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500
pm.process_idle_timeout = 10s
pm.process_start_timeout = 60s
pm.max_requests = 1000
request_terminate_timeout = 0
catch_workers_output = yes
Set the `request_terminate_timeout` to 0 to allow indefinite connections for WebSockets.
Ensure the `catch_workers_output` is set to yes to prevent output from crashing the pool.
request_slowlog_timeout = 0
slowlog = /var/log/php-fpm/www-slow.log
pm = dynamic
pm.max_children = 15
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500
pm.process_idle_timeout = 10s
pm.process_start_timeout = 60s
pm.max_requests = 1000
request_terminate_timeout = 0
catch_workers_output = yes
Save the file and exit the editor.
Step 4: Configure Nginx to proxy WebSocket traffic
Configure Nginx to pass WebSocket upgrade headers to the PHP-FPM backend. You must set the `proxy_http_version 1.1` and the `Upgrade` header to enable the connection.
Open the Nginx site configuration file.
sudo nano /etc/nginx/sites-available/default
Add the following location block for WebSocket traffic.
location /ws {
proxy_pass http://127.0.0.1:9000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
Reload the Nginx configuration to apply the changes.
sudo nginx -t
sudo systemctl reload nginx
Verify the installation
Verify that the PHP-FPM pool is running and the extension is loaded. Check the PHP-FPM status to ensure the worker processes are active.
sudo systemctl status php8.3-fpm
You will see the service is active and running.
● php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager
Loaded: loaded (/lib/systemd/system/php8.3-fpm.service; enabled)
Active: active (running) since Mon 2024-01-01 12:00:00 UTC; 1 day ago
Check that the WebSocket extension is loaded in PHP.
php -m | grep websocket
The output should show the extension name.
websocket
Test the connection by sending a WebSocket handshake request to the server.
curl -v -H "Connection: Upgrade" -H "Upgrade: websocket" http://localhost/ws
You will see the HTTP 101 Switching Protocols response if the configuration is correct.
* Connected to localhost (127.0.0.1) port 80
> GET /ws HTTP/1.1
> Host: localhost
> Connection: Upgrade
> Upgrade: websocket
>
< HTTP/1.1 101 Switching Protocols
< Upgrade: websocket
< Connection: Upgrade
<
* No more headers
* Connection #0 to localhost left intact
Troubleshooting
If the connection fails, check the PHP-FPM error log for syntax errors or missing extensions.
sudo tail -f /var/log/php-fpm/error.log
Look for messages like "cannot load extension" or "syntax error".
Ensure the Nginx configuration is valid before reloading.
sudo nginx -t
If the test fails, correct the syntax in the configuration file and reload Nginx.
sudo systemctl reload nginx
Verify that the PHP-FPM pool is listening on the correct port.
sudo netstat -tlnp | grep 9000
Ensure the port is open and bound to 127.0.0.1 or 0.0.0.0.
If the WebSocket handshake returns a 502 Bad Gateway, check the Nginx error log.
sudo tail -f /var/log/nginx/error.log
Ensure the PHP-FPM worker process has enough memory to handle the connection.
free -m
Increase the `pm.max_children` value if the system runs out of memory.
Restart the PHP-FPM service after making changes to the pool configuration.
sudo systemctl restart php8.3-fpm
Verify that the SSL certificate is valid if you are using HTTPS.
openssl s_client -connect yourdomain.com:443 -starttls smtp
Ensure the certificate is trusted and the chain is complete.
Check the firewall rules to ensure port 80 and 443 are open.
sudo ufw status
Allow traffic on the required ports.
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Restart the firewall to apply the changes.
sudo ufw reload
If the issue persists, review the PHP-FPM and Nginx logs for specific error messages.
Ensure the PHP-FPM pool is configured to handle the specific WebSocket protocol version.
Check the PHP-FPM status to ensure the worker processes are not crashing.
sudo systemctl status php8.3-fpm
Verify that the PHP-FPM pool is listening on the correct port.
sudo netstat -tlnp | grep 9000
Ensure the PHP-FPM pool is configured to handle the specific WebSocket protocol version.
Check the PHP-FPM status to ensure the worker processes are not crashing.