How to configure PHP-FPM to handle WebSocket connections
Configure PHP-FPM to accept WebSocket traffic using the OPUS extension. Set up Nginx upstream blocks and adjust PHP-FPM pool settings for long-lived connections.
Configure PHP-FPM to handle WebSocket connections by enabling the OPUS extension and adjusting Nginx upstream settings. These steps target PHP 8.3.x, Nginx 1.24.x, and Ubuntu 24.04. You will modify the PHP-FPM pool configuration to support long-lived connections and update Nginx to proxy WebSocket traffic correctly.
Prerequisites
- Operating System: Ubuntu 24.04 LTS
- Web Server: Nginx 1.24.x
- PHP Version: PHP 8.3.x
- Required Packages: php-opus, php-sockets, php-zip
- Privileges: sudo access to modify configuration files
Step 1: Install the OPUS extension
Install the OPUS library and the PHP extension required to handle WebSocket traffic. The OPUS library provides the underlying socket handling capabilities for PHP. Run the following command to install the necessary packages via the system package manager.
sudo apt update
sudo apt install php-opus php-sockets php-zip
You will see output indicating the packages are being downloaded and configured. The installation process compiles the extension modules into the PHP binary.
Step 2: Enable the OPUS extension
Enable the OPUS extension within the PHP configuration so the web server can use WebSocket functionality. Locate the PHP configuration directory and create a new file or edit the existing configuration to load the extension. Create a file specifically for the OPUS extension to keep configurations organized.
sudo nano /etc/php/8.3/mods-available/opus.ini
Add the line below to load the extension. This command tells PHP to use the OPUS library for socket operations.
extension=opus.so
Save the file and exit the text editor. Then, enable the extension for the specific PHP version by creating a symbolic link in the mods-available directory.
sudo phpenmod opus
Verify that the extension is loaded by checking the PHP configuration file. The command below lists all enabled extensions for PHP 8.3. You should see opus in the list of loaded modules.
php -m | grep opus
The output will display "opus" in the list of enabled modules, confirming the extension is active.
Step 3: Configure PHP-FPM pool settings
Adjust the PHP-FPM pool configuration to handle long-lived WebSocket connections. The default timeout settings in PHP-FPM are often too short for WebSocket traffic, causing connections to drop prematurely. Locate the main PHP-FPM pool configuration file and modify the timeout values.
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
Find the line starting with "request_terminate_timeout" and change the value from the default (often 30s or 60s) to a higher value like 3600s. This allows connections to remain open for up to one hour, which is typical for WebSocket sessions.
request_terminate_timeout = 3600s
Also, ensure the "pm.max_children" setting is appropriate for your server resources. Increase this value if you expect many concurrent WebSocket connections. For example, set it to 50 or higher depending on your server capacity.
pm.max_children = 50
Save the file and exit the text editor. These changes ensure PHP-FPM does not terminate the process while waiting for a WebSocket frame.
Step 4: Configure Nginx upstream blocks
Update the Nginx configuration to proxy WebSocket traffic correctly to the PHP backend. Nginx requires specific headers to pass WebSocket upgrade requests to the backend. Open the Nginx configuration file for your site or the default site configuration.
sudo nano /etc/nginx/sites-available/default
Add the following lines inside the server block to handle WebSocket upgrades. These headers tell the backend to accept the WebSocket protocol upgrade request.
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;
}
Save the file and exit the text editor. The "Upgrade" header is critical for maintaining the WebSocket connection. The "Connection" header must be set to "upgrade" to signal the connection is persistent.
Reload the Nginx configuration to apply the changes without dropping active connections. This command tests the syntax first and then gracefully reloads the configuration.
sudo nginx -t
sudo systemctl reload nginx
If the syntax test passes, Nginx will restart and apply the new configuration. You should see no errors in the terminal output.
Verify the installation
Test the WebSocket connection by creating a simple PHP script that echoes a WebSocket handshake response. Create a file in your web root directory that handles the WebSocket upgrade logic. Use a tool like wsclient or a browser extension to connect to the endpoint.
sudo nano /var/www/html/test_ws.php
Add the following code to the file. This script checks if the OPUS extension is loaded and returns a success message.
Save the file and access the URL in your browser. You should see the text "OPUS extension is loaded successfully." If you see this, the PHP side is configured correctly. Next, test the actual WebSocket handshake using a client application. Connect to the endpoint defined in your Nginx configuration. Ensure the connection remains open without timing out.
Troubleshooting
Check the Nginx error logs if the WebSocket connection fails immediately. The error log will indicate if Nginx is rejecting the upgrade request or if the upstream connection is broken. Open the error log file and search for "upstream" or "websocket" errors.
sudo tail -f /var/log/nginx/error.log
Check the PHP-FPM error logs if PHP-FPM terminates the connection prematurely. The log will show if the request timeout was reached or if the worker process crashed. Open the PHP-FPM error log and look for "terminate" or "timeout" messages.
sudo tail -f /var/log/php-fpm/error.log
Verify that the OPUS extension is actually loaded by running the PHP CLI command again. If the extension is missing, the module file might not be installed correctly. Reinstall the opus package if the extension check fails.
php -m | grep opus
Ensure the Nginx upstream address matches the PHP-FPM socket or port. If you are using a Unix socket, the proxy_pass directive must point to the correct socket path. Update the proxy_pass line if the default socket path is different.
proxy_pass http://unix:/run/php/php8.3-fpm.sock;
Restart the PHP-FPM service after changing the pool configuration. A simple reload might not apply changes to the pool file if the process manager detects a syntax error or if the file is locked. Restarting ensures the new timeout settings take effect.
sudo systemctl restart php8.3-fpm
Monitor the system resources if the server becomes unresponsive under load. High memory usage or CPU spikes can cause PHP-FPM workers to crash. Check the memory usage with the free command and adjust the pm.max_children setting if necessary.
free -h
Adjust the PHP-FPM process manager settings to use dynamic or on-demand mode if static mode causes resource exhaustion. The dynamic mode allows PHP-FPM to spawn new workers as needed. Modify the pm.start_servers, pm.min_spare_servers, and pm.max_spare_servers values in the pool configuration file.
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
Save the file and restart PHP-FPM to apply the new process manager settings. This configuration helps maintain stability during fluctuating traffic patterns. Verify the WebSocket connection again after making these adjustments.