Set up PHP 8.4 with Apache web server using PHP-FPM for optimal performance and security. Learn to configure essential PHP modules, implement security hardening measures, and optimize your LAMP stack for production environments.
Prerequisites
- Root or sudo access
- Basic Linux command line knowledge
- Server with at least 1GB RAM
What this solves
PHP 8.4 brings significant performance improvements and new features that make your web applications faster and more secure. This tutorial shows you how to install PHP 8.4 with Apache web server, configure PHP-FPM for better resource management, and implement security hardening to protect your server from common vulnerabilities.
Step-by-step installation
Update system packages
Start by updating your system to ensure you have access to the latest package repositories and security patches.
sudo apt update && sudo apt upgrade -y
Install Apache web server
Install Apache HTTP server which will serve your PHP applications and handle web requests.
sudo apt install -y apache2 apache2-utils
Enable and start Apache service
Enable Apache to start automatically on boot and start the service immediately.
sudo systemctl enable --now apache2
sudo systemctl status apache2
Add PHP 8.4 repository
Add the official PHP repository to access PHP 8.4 packages, as they may not be available in default repositories.
sudo apt install -y software-properties-common
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
Install PHP 8.4 and essential modules
Install PHP 8.4 with commonly needed modules for web development, database connectivity, and security features.
sudo apt install -y php8.4 php8.4-cli php8.4-common php8.4-fpm php8.4-mysql php8.4-pgsql php8.4-sqlite3 php8.4-curl php8.4-gd php8.4-mbstring php8.4-xml php8.4-zip php8.4-bcmath php8.4-intl php8.4-redis php8.4-memcached
Configure PHP-FPM for Apache
Enable and configure PHP-FPM (FastCGI Process Manager) for better performance and resource isolation compared to mod_php.
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.4-fpm
sudo systemctl enable --now php8.4-fpm
Configure PHP security settings
Modify PHP configuration to implement security best practices and disable dangerous functions.
; Security hardening
expose_php = Off
allow_url_fopen = Off
allow_url_include = Off
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
max_execution_time = 30
max_input_time = 30
memory_limit = 128M
post_max_size = 8M
upload_max_filesize = 8M
max_file_uploads = 20
; Error handling (for production)
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
; Session security
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1
Configure PHP-FPM pool settings
Optimize PHP-FPM pool configuration for better performance and security isolation.
[www]
user = www-data
group = www-data
listen = /run/php/php8.4-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
; Process management
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
; Security
security.limit_extensions = .php
php_admin_value[disable_functions] = exec,passthru,shell_exec,system
php_admin_flag[allow_url_fopen] = off
Create Apache virtual host for PHP
Set up a virtual host configuration that properly integrates with PHP-FPM and includes security headers.
ServerName example.com
DocumentRoot /var/www/html
# PHP-FPM configuration
SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost"
# Security headers
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
# Directory security
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
ErrorLog ${APACHE_LOG_DIR}/php-error.log
CustomLog ${APACHE_LOG_DIR}/php-access.log combined
Enable Apache modules and site
Enable required Apache modules for security headers and activate the PHP site configuration.
sudo a2enmod headers rewrite
sudo a2ensite php-site.conf
sudo a2dissite 000-default.conf
Set correct permissions for web directory
Configure proper ownership and permissions for the web root directory. Never use chmod 777 as it compromises security.
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html
sudo find /var/www/html -type f -exec chmod 644 {} \;
Create PHP test file
Create a simple PHP file to test your installation and view PHP configuration details.
Restart services
Restart Apache and PHP-FPM to apply all configuration changes.
sudo systemctl restart apache2
sudo systemctl restart php8.4-fpm
Configure firewall rules
Allow HTTP and HTTPS traffic through the firewall for web access.
sudo ufw allow 'Apache Full'
sudo ufw status
Verify your setup
Test your PHP installation and verify that all services are running correctly.
php -v
sudo systemctl status apache2
sudo systemctl status php8.4-fpm
curl -I http://localhost/info.php
Visit http://your-server-ip/info.php in your browser to see the PHP configuration page. Remove this file after testing for security.
sudo rm /var/www/html/info.php
Performance optimization
Install and configure OPcache
Enable PHP OPcache for significant performance improvements by caching compiled PHP code.
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.save_comments=1
opcache.validate_timestamps=1
Configure Apache for better performance
Optimize Apache settings for handling concurrent connections and reduce memory usage.
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 1000
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| PHP files download instead of executing | PHP handler not configured | Check Apache PHP-FPM configuration and restart services |
| Permission denied errors | Incorrect file ownership | Run sudo chown -R www-data:www-data /var/www/html |
| PHP-FPM socket connection refused | Socket permissions or path mismatch | Verify socket path in both PHP-FPM and Apache configs |
| High memory usage | Incorrect PHP-FPM process settings | Adjust pm.max_children and memory_limit values |
| Slow page loading | OPcache not enabled | Install and configure OPcache extension |
Next steps
- Install and configure MariaDB 11.6 with performance optimization and security hardening
- Configure Apache web server with virtual hosts and SSL certificates
- Configure PHP Composer for dependency management and autoloading
- Implement PHP application monitoring with New Relic and logging
- Configure Linux firewall rules with fail2ban for SSH brute force protection
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
PHP_VERSION="8.4"
DOMAIN="${1:-localhost}"
# Usage function
usage() {
echo "Usage: $0 [domain_name]"
echo "Example: $0 example.com"
echo "If no domain provided, defaults to localhost"
exit 1
}
# Error handling
error_exit() {
echo -e "${RED}[ERROR] $1${NC}" >&2
exit 1
}
info() {
echo -e "${GREEN}$1${NC}"
}
warn() {
echo -e "${YELLOW}$1${NC}"
}
# Cleanup on error
cleanup() {
warn "Script failed. Cleaning up..."
systemctl stop apache2 2>/dev/null || systemctl stop httpd 2>/dev/null || true
systemctl stop php${PHP_VERSION}-fpm 2>/dev/null || systemctl stop php84-php-fpm 2>/dev/null || true
}
trap cleanup ERR
# Check if running as root or with sudo
if [[ $EUID -ne 0 ]]; then
error_exit "This script must be run as root or with sudo"
fi
# Detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update && apt upgrade -y"
APACHE_SERVICE="apache2"
APACHE_USER="www-data"
APACHE_GROUP="www-data"
PHP_FPM_SERVICE="php${PHP_VERSION}-fpm"
PHP_FPM_SOCK="/run/php/php${PHP_VERSION}-fpm.sock"
PHP_INI_PATH="/etc/php/${PHP_VERSION}/fpm/php.ini"
PHP_FPM_POOL="/etc/php/${PHP_VERSION}/fpm/pool.d/www.conf"
APACHE_VHOST_DIR="/etc/apache2/sites-available"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
APACHE_SERVICE="httpd"
APACHE_USER="apache"
APACHE_GROUP="apache"
PHP_FPM_SERVICE="php84-php-fpm"
PHP_FPM_SOCK="/var/run/php84-fpm.sock"
PHP_INI_PATH="/etc/opt/remi/php84/php.ini"
PHP_FPM_POOL="/etc/opt/remi/php84/php-fpm.d/www.conf"
APACHE_VHOST_DIR="/etc/httpd/conf.d"
;;
fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
APACHE_SERVICE="httpd"
APACHE_USER="apache"
APACHE_GROUP="apache"
PHP_FPM_SERVICE="php84-php-fpm"
PHP_FPM_SOCK="/var/run/php84-fpm.sock"
PHP_INI_PATH="/etc/opt/remi/php84/php.ini"
PHP_FPM_POOL="/etc/opt/remi/php84/php-fpm.d/www.conf"
APACHE_VHOST_DIR="/etc/httpd/conf.d"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
APACHE_SERVICE="httpd"
APACHE_USER="apache"
APACHE_GROUP="apache"
PHP_FPM_SERVICE="php84-php-fpm"
PHP_FPM_SOCK="/var/run/php84-fpm.sock"
PHP_INI_PATH="/etc/opt/remi/php84/php.ini"
PHP_FPM_POOL="/etc/opt/remi/php84/php-fpm.d/www.conf"
APACHE_VHOST_DIR="/etc/httpd/conf.d"
;;
*)
error_exit "Unsupported distribution: $ID"
;;
esac
else
error_exit "Cannot detect distribution. /etc/os-release not found."
fi
info "[1/8] Updating system packages..."
$PKG_UPDATE
info "[2/8] Installing Apache web server..."
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL apache2 apache2-utils
else
$PKG_INSTALL httpd httpd-tools
fi
info "[3/8] Enabling and starting Apache service..."
systemctl enable --now $APACHE_SERVICE
systemctl is-active --quiet $APACHE_SERVICE || error_exit "Apache failed to start"
info "[4/8] Adding PHP 8.4 repository..."
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL software-properties-common
add-apt-repository ppa:ondrej/php -y
apt update
else
$PKG_INSTALL epel-release
if [[ "$ID" == "fedora" ]]; then
$PKG_INSTALL https://rpms.remirepo.net/fedora/remi-release-$(rpm -E %fedora).rpm
else
$PKG_INSTALL https://rpms.remirepo.net/enterprise/remi-release-$(rpm -E %rhel).rpm
fi
dnf module enable php:remi-8.4 -y
fi
info "[5/8] Installing PHP 8.4 and essential modules..."
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL php8.4 php8.4-cli php8.4-common php8.4-fpm php8.4-mysql php8.4-pgsql php8.4-sqlite3 \
php8.4-curl php8.4-gd php8.4-mbstring php8.4-xml php8.4-zip php8.4-bcmath php8.4-intl
else
$PKG_INSTALL php84-php php84-php-cli php84-php-common php84-php-fpm php84-php-mysqlnd php84-php-pgsql \
php84-php-pdo php84-php-curl php84-php-gd php84-php-mbstring php84-php-xml php84-php-zip \
php84-php-bcmath php84-php-intl
fi
info "[6/8] Configuring PHP-FPM for Apache..."
if [[ "$PKG_MGR" == "apt" ]]; then
a2enmod proxy_fcgi setenvif
a2enconf php8.4-fpm
else
# Configure SELinux for RHEL-based systems
command -v setsebool >/dev/null && setsebool -P httpd_execmem 1 || true
fi
systemctl enable --now $PHP_FPM_SERVICE
systemctl is-active --quiet $PHP_FPM_SERVICE || error_exit "PHP-FPM failed to start"
info "[7/8] Configuring PHP security settings..."
# Backup original php.ini
cp "$PHP_INI_PATH" "$PHP_INI_PATH.backup"
# Apply security configurations
cat >> "$PHP_INI_PATH" << 'EOF'
; Security hardening
expose_php = Off
allow_url_fopen = Off
allow_url_include = Off
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
max_execution_time = 30
max_input_time = 30
memory_limit = 128M
post_max_size = 8M
upload_max_filesize = 8M
max_file_uploads = 20
; Error handling (for production)
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
; Session security
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1
EOF
# Configure PHP-FPM pool
cp "$PHP_FPM_POOL" "$PHP_FPM_POOL.backup"
cat > "$PHP_FPM_POOL" << EOF
[www]
user = $APACHE_USER
group = $APACHE_GROUP
listen = $PHP_FPM_SOCK
listen.owner = $APACHE_USER
listen.group = $APACHE_GROUP
listen.mode = 0660
; Process management
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
; Security
security.limit_extensions = .php
php_admin_value[disable_functions] = exec,passthru,shell_exec,system
php_admin_flag[allow_url_fopen] = off
EOF
# Create log file with proper permissions
touch /var/log/php_errors.log
chown $APACHE_USER:$APACHE_GROUP /var/log/php_errors.log
chmod 640 /var/log/php_errors.log
info "[8/8] Creating test PHP file and virtual host..."
# Create document root
DOC_ROOT="/var/www/html/$DOMAIN"
mkdir -p "$DOC_ROOT"
chown -R $APACHE_USER:$APACHE_GROUP "$DOC_ROOT"
chmod 755 "$DOC_ROOT"
# Create test PHP file
cat > "$DOC_ROOT/info.php" << 'EOF'
<?php
phpinfo();
?>
EOF
chown $APACHE_USER:$APACHE_GROUP "$DOC_ROOT/info.php"
chmod 644 "$DOC_ROOT/info.php"
# Create virtual host configuration
if [[ "$PKG_MGR" == "apt" ]]; then
VHOST_FILE="$APACHE_VHOST_DIR/$DOMAIN.conf"
cat > "$VHOST_FILE" << EOF
<VirtualHost *:80>
ServerName $DOMAIN
DocumentRoot $DOC_ROOT
<Directory "$DOC_ROOT">
AllowOverride All
Require all granted
</Directory>
<FilesMatch \.php$>
SetHandler "proxy:unix:$PHP_FPM_SOCK|fcgi://localhost"
</FilesMatch>
ErrorLog \${APACHE_LOG_DIR}/$DOMAIN-error.log
CustomLog \${APACHE_LOG_DIR}/$DOMAIN-access.log combined
</VirtualHost>
EOF
a2ensite "$DOMAIN"
else
VHOST_FILE="$APACHE_VHOST_DIR/$DOMAIN.conf"
cat > "$VHOST_FILE" << EOF
<VirtualHost *:80>
ServerName $DOMAIN
DocumentRoot $DOC_ROOT
<Directory "$DOC_ROOT">
AllowOverride All
Require all granted
</Directory>
<FilesMatch \.php$>
SetHandler "proxy:unix:$PHP_FPM_SOCK|fcgi://localhost"
</FilesMatch>
ErrorLog logs/$DOMAIN-error.log
CustomLog logs/$DOMAIN-access.log combined
</VirtualHost>
EOF
fi
chmod 644 "$VHOST_FILE"
# Restart services
systemctl restart $PHP_FPM_SERVICE
systemctl restart $APACHE_SERVICE
info "Verification checks..."
# Verify services are running
systemctl is-active --quiet $APACHE_SERVICE || error_exit "Apache is not running"
systemctl is-active --quiet $PHP_FPM_SERVICE || error_exit "PHP-FPM is not running"
# Verify PHP-FPM socket
test -S "$PHP_FPM_SOCK" || error_exit "PHP-FPM socket not found"
info "Installation completed successfully!"
info "Apache is running and serving: http://$DOMAIN"
info "Test PHP installation: http://$DOMAIN/info.php"
warn "Remove info.php after testing for security"
warn "Configure SSL/TLS certificates for production use"
Review the script before running. Execute with: bash install.sh