Full tutorial with GUI integration, fixed OpenSSL support, external modules, and a working custom PHP builder. This guide explains how to properly add PHP 8.4 and PHP 8.5 to Control Web Panel (CWP) on EL9-based systems. Unlike older CWP releases, CWP does not provide official PHP 8.4/8.5 packages yet — but with the steps below you can fully integrate them into and you can also fix the PHP 8.3 OpenSSL problem based on this: ✔ the CWP PHP Selector GUI ✔ Apache / Nginx vhosts ✔ external PECL modules ✔ system services ✔ CSF/Monit integration This article provides everything: file paths, scripts, corrections, and the build steps. ⚙️ Step 1 — Extend the CWP GUI to Support PHP 8.4 and 8.5CWP reads available PHP versions from: /usr/local/cwpsrv/htdocs/resources/conf/el9/php-fpm_selector/versions.iniAdd the new PHP versions like: [8.3]
version[]=8.3.28
version[]=8.3.27
version[]=8.3.25
version[]=8.3.21
version[]=8.3.20
version[]=8.3.19
version[]=8.3.17
version[]=8.3.16
version[]=8.3.15
version[]=8.3.14
version[]=8.3.13
version[]=8.3.12
version[]=8.3.11
version[]=8.3.10
version[]=8.3.9
version[]=8.3.8
version[]=8.3.7
version[]=8.3.6
version[]=8.3.4
version[]=8.3.3
version[]=8.3.2
version[]=8.3.1
version[]=8.3.0
[8.4]
version[]=8.4.15
version[]=8.4.14
version[]=8.4.13
version[]=8.4.12
version[]=8.4.11
version[]=8.4.10
version[]=8.4.9
version[]=8.4.8
version[]=8.4.7
version[]=8.4.6
version[]=8.4.4
version[]=8.4.3
version[]=8.4.2
version[]=8.4.1
version[]=8.4.0
[8.5]
version[]=8.5.0
This makes the versions selectable in the GUI. If you don't want to make previous PHP 8.4 versions optional under 8.4.14, you can leave them out of the list. ⚙️ Step 2 — Create 8.4.ini and 8.5.ini GUI TemplatesLocation: /usr/local/cwpsrv/htdocs/resources/conf/el9/php-fpm_selector/Create these files based on the sample and content of 8.3.ini: 8.4.ini
8.5.ini
Both must contain a corrected OpenSSL section: [openssl]
default=1
option="--with-openssl=/usr"
info-file=openssl.txt
(Older CWP templates use deprecated OpenSSL paths that break EL9 builds. The corrected version above is mandatory. And similarly, the OpenSSL section in the 8.3.ini file must be corrected.) These files provide the GUI-driven configure flags that our builder script will later use. ⚙️ Step 3 — Create External and Pre Run Modules for PHP 8.4 and 8.5Create these directories and update the content of 8.3 folder with the linked ZIP file: /usr/local/cwpsrv/htdocs/resources/conf/el9/php-fpm_selector/external_modules/8.3
/usr/local/cwpsrv/htdocs/resources/conf/el9/php-fpm_selector/external_modules/8.4
/usr/local/cwpsrv/htdocs/resources/conf/el9/php-fpm_selector/external_modules/8.5Populate each directory with the updated scripts: apcu.sh imagick.sh memcache.sh memcached.sh mailparse.sh mongodb.sh redis.sh ssh2.sh uploadprogress.sh xdebug.sh yaz.sh sodium.sh sourceguardian.sh sqlsrv.sh ioncube.sh (With proper loader URL handling: stable for 8.4, beta for 8.5.) 👉 Here is the ZIP file with all corrected module scripts to this post for download: External_Modules Create these directories and update the content of 8.3 folder with the linked ZIP file: /usr/local/cwpsrv/htdocs/resources/conf/el9/php-fpm_selector/pre_run/8.3
/usr/local/cwpsrv/htdocs/resources/conf/el9/php-fpm_selector/pre_run/8.4
/usr/local/cwpsrv/htdocs/resources/conf/el9/php-fpm_selector/pre_run/8.5Pre Run Modules ⚙️ Step 4 — Install the Custom Builder ScriptPlace the builder script in: /root/build-php-fpm84-el9.sh
This script: ✔ reads the GUI-generated configure template ✔ enforces OpenSSL 3.x for EL9 ✔ builds PHP cleanly under /opt/alt/php-fpm84/usr/ ✔ installs FPM service, ini files, sockets ✔ compiles all external modules ✔ integrates CSF and Monit ✔ logs every step You can rewrite this script to install PHP 8.3.28 or PHP 8.5.0 by changing just a few variables. Here is the corrected PHP compiler script located in /root/build-php-fpm84-el9.sh file: #!/bin/bash
set -euo pipefail
set -x
# --- Basic variables ---
PHPMAJOR="84" # php-fpm84
PHPVER="8.4.15" # PHP version
FPMDIR="/opt/alt/php-fpm${PHPMAJOR}"
CONFBASE="/usr/local/cwp/.conf/php-fpm_conf"
arch=$(uname -m)
if [[ "$arch" == "x86_64" ]]; then
platform="x86-64"
libdir="/usr/lib64"
else
platform="x86"
libdir="/usr/lib"
fi
# --- Packages for Build (EL9) ---
dnf -y install \
krb5-devel glibc-common gnutls-devel \
libargon2 libargon2-devel libbsd-devel \
perl libzip libzip-devel pcre2 pcre2-devel \
libavif libavif-devel \
uw-imap-devel \
openssl-devel
# If there is any old CWP OpenSSL hack left, don't use it.:
if [ -d /usr/local/opensslso ]; then
echo "WARN: /usr/local/opensslso exists, but we DO NOT USE for compiling PHP (OpenSSL 1.1 hack)."
fi
# --- Force OpenSSL 3.x ---
export PKG_CONFIG_PATH=/usr/lib64/pkgconfig
export OPENSSL_CFLAGS="-I/usr/include"
export OPENSSL_LIBS="-L/usr/lib64"
export LDFLAGS="-lssl -lcrypto"
# --- CWP pre-conf, if exists (e.g.: pcre2, & other libs) ---
if [ -e "${CONFBASE}/php${PHPMAJOR}_pre.conf" ]; then
bash "${CONFBASE}/php${PHPMAJOR}_pre.conf"
fi
# --- PHP SOURCE DOWNLOAD CHECK: CWP CDN → OFFICIAL php.net → GitHub fallback ---
CWP_URL="http://static.cdn-cwp.com/files/php/php-${PHPVER}.tar.gz"
PHPNET_URL="https://www.php.net/distributions/php-${PHPVER}.tar.gz"
GITHUB_URL="https://codeload.github.com/php/php-src/tar.gz/refs/tags/php-${PHPVER}"
# Function: check HTTP 200 + verify tar.gz content
check_and_verify() {
local url="$1"
local testfile="/tmp/php-test-${PHPVER}.tar.gz"
echo "Checking: $url"
# First check HTTP status code
if ! curl -I -L -s "$url" | grep -q "200"; then
echo " → HTTP check failed"
return 1
fi
# Download temporary test file
if ! wget -q "$url" -O "$testfile"; then
echo " → Download failed"
return 1
fi
# Validate MIME type of tar.gz
if file "$testfile" | grep -qiE "gzip compressed data|tar archive"; then
rm -f "$testfile"
echo " → Valid TAR.GZ"
return 0
fi
echo " → Invalid TAR.GZ (HTML or wrong file)"
rm -f "$testfile"
return 1
}
# Check sources in order (CWP → php.net → GitHub)
if check_and_verify "$CWP_URL"; then
PHPSOURCE="$CWP_URL"
elif check_and_verify "$PHPNET_URL"; then
PHPSOURCE="$PHPNET_URL"
elif check_and_verify "$GITHUB_URL"; then
PHPSOURCE="$GITHUB_URL"
else
echo "ERROR: Could not download a valid PHP source for version ${PHPVER}"
exit 1
fi
echo "Using source: $PHPSOURCE"
# --- Build directory ---
rm -rf /usr/local/src/php-build
mkdir -p /usr/local/src/php-build
cd /usr/local/src/php-build
wget -q "${PHPSOURCE}" -O "php-${PHPVER}.tar.gz"
tar -xvf "php-${PHPVER}.tar.gz"
cd "php-${PHPVER}"
# --- Configure: CWP's own php84.conf, but already wired to OpenSSL 3.x from env ---
if [ ! -x "${CONFBASE}/php${PHPMAJOR}.conf" ]; then
chmod +x "${CONFBASE}/php${PHPMAJOR}.conf" 2>/dev/null || true
fi
# IMPORTANT: LDFLAGS + PKG_CONFIG_PATH already exported
bash "${CONFBASE}/php${PHPMAJOR}.conf"
# --- Compiling ---
if command -v nproc >/dev/null 2>&1; then
make -j"$(nproc)"
else
make
fi
make install
# --- PHP.ini + FPM scaffolding ---
mkdir -p "${FPMDIR}/usr/php/php.d"
mkdir -p "${FPMDIR}/usr/var/sockets"
mkdir -p "${FPMDIR}/usr/etc/php-fpm.d"
mkdir -p "${FPMDIR}/usr/etc/php-fpm.d/users"
rsync php.ini-production "${FPMDIR}/usr/php/php.ini"
sed -i 's/^short_open_tag.*/short_open_tag = On/' "${FPMDIR}/usr/php/php.ini"
sed -i 's/^;cgi.fix_pathinfo=.*/cgi.fix_pathinfo=1/' "${FPMDIR}/usr/php/php.ini"
sed -i 's/.*mail.add_x_header.*/mail.add_x_header = On/' "${FPMDIR}/usr/php/php.ini"
sed -i 's@.*mail.log.*@mail.log = /usr/local/apache/logs/phpmail.log@' "${FPMDIR}/usr/php/php.ini"
echo "include=${FPMDIR}/usr/etc/php-fpm.d/users/*.conf" > "${FPMDIR}/usr/etc/php-fpm.d/users.conf"
echo "include=${FPMDIR}/usr/etc/php-fpm.d/*.conf" > "${FPMDIR}/usr/etc/php-fpm.conf"
cat > "${FPMDIR}/usr/etc/php-fpm.d/cwpsvc.conf" <<EOF
[cwpsvc]
listen = ${FPMDIR}/usr/var/sockets/cwpsvc.sock
listen.owner = cwpsvc
listen.group = cwpsvc
listen.mode = 0640
user = cwpsvc
group = cwpsvc
pm = ondemand
pm.max_children = 25
pm.process_idle_timeout = 15s
request_terminate_timeout = 0
EOF
# --- Systemd service ---
cp sapi/fpm/php-fpm.service "/usr/lib/systemd/system/php-fpm${PHPMAJOR}.service"
sed -i "s|\${exec_prefix}|${FPMDIR}/usr|g" "/usr/lib/systemd/system/php-fpm${PHPMAJOR}.service"
sed -i "s|\${prefix}|${FPMDIR}/usr|g" "/usr/lib/systemd/system/php-fpm${PHPMAJOR}.service"
systemctl daemon-reload
systemctl enable "php-fpm${PHPMAJOR}"
# --- Loading Apache FPM module if not already present ---
if [ ! -e "/usr/local/apache/conf.d/php-fpm.conf" ]; then
cat > /usr/local/apache/conf.d/php-fpm.conf <<EOF
<IfModule !proxy_fcgi_module>
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
</IfModule>
EOF
fi
# --- External modules (imagick, redis, imap, etc.) ---
if [ -e "${CONFBASE}/php${PHPMAJOR}_external.conf" ]; then
bash "${CONFBASE}/php${PHPMAJOR}_external.conf" || true
fi
# --- Monitor integration ---
if [ -d "/etc/monit.d" ]; then
if [ ! -e "/etc/monit.d/php-fpm${PHPMAJOR}" ]; then
if [ -e "/usr/local/cwpsrv/htdocs/resources/conf/monit.d/php-fpm${PHPMAJOR}" ]; then
cp "/usr/local/cwpsrv/htdocs/resources/conf/monit.d/php-fpm${PHPMAJOR}" /etc/monit.d/ 2>/dev/null || true
monit reload || true
fi
fi
fi
systemctl restart "php-fpm${PHPMAJOR}"
# --- CSF pignore ---
if [ -e "/etc/csf/csf.pignore" ]; then
# PHP-FPM + PHP binary
if ! grep -q "${FPMDIR}/usr/sbin/php-fpm" /etc/csf/csf.pignore; then
echo "exe:${FPMDIR}/usr/sbin/php-fpm" >> /etc/csf/csf.pignore
fi
if ! grep -q "${FPMDIR}/usr/bin/php" /etc/csf/csf.pignore; then
echo "exe:${FPMDIR}/usr/bin/php" >> /etc/csf/csf.pignore
fi
# memcached daemon
if command -v memcached >/dev/null 2>&1; then
if ! grep -q "exe:/usr/bin/memcached" /etc/csf/csf.pignore; then
echo "exe:/usr/bin/memcached" >> /etc/csf/csf.pignore
fi
fi
# redis-server daemon
if command -v redis-server >/dev/null 2>&1; then
if ! grep -q "exe:/usr/bin/redis-server" /etc/csf/csf.pignore; then
echo "exe:/usr/bin/redis-server" >> /etc/csf/csf.pignore
fi
fi
# Restart CSF/LFD to apply changes
csf -r
fi
rm -rf /usr/local/src/php-build
rm -rf /usr/local/src/build-dir
echo "PHP ${PHPVER} (php-fpm${PHPMAJOR}) build finished successfully."
It is for sample, you can modify it by your needs. ⚙️ Step 5 — Trigger the GUI Build OnceFrom the CWP Admin Panel: PHP-FPM Selector → PHP 8.4 → Choose desired version of PHP → Build
CWP will: generate the build configuration files stop early because no packages exist (expected!) but now the config files are ready The GUI selector (Image on the shared link) This step must be done once so the GUI produces: /usr/local/cwp/.conf/php-fpm_conf/php84.conf
/usr/local/cwp/.conf/php-fpm_conf/php84_pre.conf
/usr/local/cwp/.conf/php-fpm_conf/php84_external.conf
Our builder script depends on them. ⚙️ Step 6 — Build PHP 8.4 With LoggingRun the custom builder script manually: bash /root/build-php-fpm84-el9.sh 2>&1 | tee /root/php84-build.log
This: logs all output compiles PHP 8.4.14 installs extensions - e.g. IonCube loader if it was selected reloads FPM integrates CSF pignore entries exe:/usr/bin/redis-server exe:/usr/bin/memcached After the build: Test PHP:/opt/alt/php-fpm84/usr/bin/php -v
/opt/alt/php-fpm84/usr/sbin/php-fpm -t
Verify IonCube:grep -Ri ioncube /opt/alt/php-fpm84/usr/php/php.d/
Verify Imagick:/opt/alt/php-fpm84/usr/bin/php -r "print_r(Imagick::getVersion());"⚙️ Repeat the Process for PHP 8.5The steps are identical, except: PHP version is different ionCube loader uses the beta build ioncube_loaders_lin_x86-64_beta.tar.gz Here is the IonCube documentation. The PHP 8.4 is fully supported, PHP 8.5 is Beta in the moment. 🎉 ResultAfter completing these steps you will have: ✔ Fully working PHP 8.4 or 8.5 ✔ 100% GUI-compatible (PHP Selector, vhost handler, extension manager) ✔ Proper OpenSSL 3.x integration ✔ All external PECL modules working ✔ Systemd FPM services ✔ Clean, isolated alt-PHP folders under /opt/alt ✔ No conflict with AlmaLinux REMI PHP packages And most importantly: You can now run PHP 8.4 / 8.5 safely in production on CWP EL9. I also fixed the PHP - OpenSSL compilation issue in AlmaLinux 9.x with PHP 8.3.28 version of the script. PHP now uses OpenSSL 3.x. Checking the compiled PHP versions (e.g. 8.4.14 and 8.3.28): [root@vps ~]# /opt/alt/php-fpm84/usr/bin/php -v
PHP 8.4.14 (cli) (built: Dec 1 2025 22:53:34) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.4.14, Copyright (c) Zend Technologies
with the ionCube PHP Loader v15.0.0, Copyright (c) 2002-2025, by ionCube Ltd.
with Zend OPcache v8.4.14, Copyright (c), by Zend Technologies
[root@vps ~]# /opt/alt/php-fpm83/usr/bin/php -v
PHP 8.3.28 (cli) (built: Dec 1 2025 16:39:53) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.28, Copyright (c) Zend Technologies
with Zend OPcache v8.3.28, Copyright (c), by Zend Technologies
[root@vps ~]#
Checking OpenSSL: [root@vps ~]# /opt/alt/php-fpm84/usr/bin/php -i | grep -i "openssl"
Configure Command => './configure' '--prefix=/opt/alt/php-fpm84/usr' '--with-config-file-path=/opt/alt/php-fpm84/usr/php' '--with-config-file-scan-dir=/opt/alt/php-fpm84/usr/php/php.d' '--with-zlib=/usr' '--enable-mbstring' '--with-zip' '--enable-bcmath' '--enable-pcntl' '--enable-ftp' '--enable-exif' '--enable-calendar' '--enable-sysvmsg' '--enable-sysvsem' '--enable-sysvshm' '--with-tidy' '--with-curl' '--with-iconv' '--with-gmp' '--with-pspell' '--enable-gd' '--with-jpeg' '--with-freetype' '--enable-gd-jis-conv' '--with-webp' '--with-avif' '--with-zlib-dir=/usr' '--with-xpm' '--with-openssl=/usr' '--with-pdo-mysql=mysqlnd' '--with-gettext=/usr' '--with-bz2=/usr' '--with-mysqli' '--enable-soap' '--enable-phar' '--with-xsl' '--with-kerberos' '--enable-posix' '--enable-sockets' '--with-external-pcre' '--with-libdir=lib64' '--with-mysql-sock=/var/lib/mysql/mysql.sock' '--enable-intl' '--with-imap' '--with-imap-ssl' '--enable-fpm' '--enable-opcache' '--with-password-argon2' 'PKG_CONFIG_PATH=/usr/lib64/pkgconfig' 'OPENSSL_CFLAGS=-I/usr/include' 'OPENSSL_LIBS=-L/usr/lib64'
SSL Version => OpenSSL/3.5.1
libSSH Version => libssh/0.10.4/openssl/zlib
openssl
OpenSSL support => enabled
OpenSSL Library Version => OpenSSL 3.5.1 1 Jul 2025
OpenSSL Header Version => OpenSSL 3.5.1 1 Jul 2025
Openssl default config => /etc/pki/tls/openssl.cnf
openssl.cafile => no value => no value
openssl.capath => no value => no value
OpenSSL support => enabled
[root@vps ~]#
If you have any suggestions, please feel free to write to me.