Apache httpd LAMP Stack Setup

Complete Installation & Configuration Guide for RHEL/OEL8/CentOS

MariaDB โ€ข MySQL โ€ข PHP โ€ข Python โ€ข Perl Integration

โšก Quick Start - Complete LAMP Stack

# Install everything for a complete LAMP stack sudo dnf install -y httpd mod_ssl php php-mysqlnd php-fpm php-gd php-mbstring php-xml php-json php-opcache python3-mod_wsgi mod_perl mod_perl-devel && sudo systemctl enable --now httpd php-fpm && sudo setsebool -P httpd_can_network_connect_db on && sudo firewall-cmd --permanent --add-service={http,https} && sudo firewall-cmd --reload

๐Ÿ“‹ System Information

Target Systems
  • Oracle Enterprise Linux 8 (OEL8)
  • Red Hat Enterprise Linux 8 (RHEL8)
  • CentOS 8 / Rocky Linux 8 / AlmaLinux 8
  • Apache httpd 2.4.x (default in RHEL8)
  • PHP 7.x / 8.x (from AppStream)
๐Ÿ“˜ Database Compatibility Note
Good News: The PHP configuration works identically for both MariaDB and MySQL!
  • Socket path is the same: /var/lib/mysql/mysql.sock
  • php-mysqlnd driver works with both databases
  • PDO and MySQLi connections are identical
  • No special configuration needed to switch between them

๐Ÿ“ฆ Required Packages

Apache Core

httpd Apache HTTP Server 2.4
httpd-tools Tools: htpasswd, ab (benchmarking), etc.
mod_ssl SSL/TLS module for HTTPS
httpd-manual Apache documentation (optional)

PHP Integration (FastCGI/PHP-FPM)

php PHP scripting language
php-fpm FastCGI Process Manager (preferred over mod_php)
php-cli Command-line PHP interpreter
php-common Common files for PHP
php-mysqlnd MySQL Native Driver (works with MySQL & MariaDB!)
php-pdo PHP Data Objects abstraction layer
php-gd GD graphics library
php-mbstring Multi-byte string support
php-xml XML support
php-json JSON support
php-opcache OPcache accelerator for performance
php-intl Internationalization extension
php-bcmath BC Math (arbitrary precision)
php-zip ZIP archive support
php-curl cURL support for HTTP requests

Python Integration (mod_wsgi)

python3-mod_wsgi Apache module for Python 3 WSGI apps
python3 Python 3 interpreter
python3-pip Python package installer

Perl Integration (mod_perl)

mod_perl Embedded Perl interpreter for Apache
mod_perl-devel Development files for mod_perl
perl Perl interpreter (pre-installed)

๐Ÿ”ง Installation Steps

Complete Installation

One Command Install - Full LAMP Stack

sudo dnf install -y \ httpd \ httpd-tools \ mod_ssl \ php \ php-fpm \ php-cli \ php-common \ php-mysqlnd \ php-pdo \ php-gd \ php-mbstring \ php-xml \ php-json \ php-opcache \ php-intl \ php-bcmath \ php-zip \ php-curl \ python3-mod_wsgi \ python3-pip \ mod_perl \ mod_perl-devel

Enable and Start Services

Service Management
# Enable Apache and PHP-FPM to start on boot sudo systemctl enable httpd sudo systemctl enable php-fpm # Start both services sudo systemctl start httpd sudo systemctl start php-fpm # Check status sudo systemctl status httpd sudo systemctl status php-fpm # Verify Apache is listening sudo ss -tlnp | grep httpd sudo ss -tlnp | grep php-fpm

Configure Firewall

Firewall Configuration
# Allow HTTP and HTTPS sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload # Verify sudo firewall-cmd --list-all

Configure SELinux (CRITICAL!)

SELinux Configuration
# Allow httpd to connect to databases (MySQL/MariaDB) sudo setsebool -P httpd_can_network_connect on sudo setsebool -P httpd_can_network_connect_db on # Allow httpd to send email (if needed) sudo setsebool -P httpd_can_sendmail on # For custom document root # sudo semanage fcontext -a -t httpd_sys_content_t "/custom/path(/.*)?" # sudo restorecon -Rv /custom/path # For writable directories (uploads, cache) # sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/uploads(/.*)?" # sudo restorecon -Rv /var/www/html/uploads # Check status getenforce getsebool -a | grep httpd
โš ๏ธ #1 LAMP Stack Problem: SELinux
If PHP can't connect to your database, it's almost always because httpd_can_network_connect_db is not enabled. This is THE most common issue at 2 AM. Enable it!

โš™๏ธ Apache httpd Configuration

Main Apache Configuration (Key Settings) /etc/httpd/conf/httpd.conf
# Apache httpd.conf - Essential Settings # Full file is longer - these are the key edits ServerRoot "/etc/httpd" Listen 80 ### Performance Tuning ### Timeout 60 KeepAlive On MaxKeepAliveRequests 100 KeepAliveTimeout 5 ### MPM Event (default - best performance) ### <IfModule mpm_event_module> ServerLimit 16 StartServers 3 MinSpareThreads 75 MaxSpareThreads 250 ThreadsPerChild 25 MaxRequestWorkers 400 MaxConnectionsPerChild 1000 </IfModule> ### Security ### ServerTokens Prod ServerSignature Off TraceEnable Off ### User/Group ### User apache Group apache ### Server Info ### ServerAdmin root@localhost ServerName localhost:80 # Change to your domain: ServerName www.example.com:80 ### Document Root ### DocumentRoot "/var/www/html" ### Directory Permissions ### <Directory /> AllowOverride none Require all denied </Directory> <Directory "/var/www/html"> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> ### Directory Index ### <IfModule dir_module> DirectoryIndex index.html index.php index.py index.pl </IfModule> ### Logging ### ErrorLog "logs/error_log" LogLevel warn CustomLog "logs/access_log" combined ### Include Additional Config Files ### IncludeOptional conf.d/*.conf

๐Ÿ˜ PHP-FPM Configuration

PHP-FPM Pool Configuration /etc/php-fpm.d/www.conf
; PHP-FPM www Pool - Key Settings [www] user = apache group = apache ; Unix socket (best performance) listen = /run/php-fpm/www.sock listen.owner = apache listen.group = apache 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 ; Monitoring pm.status_path = /php-fpm-status ping.path = /php-fpm-ping ; Slow log slowlog = /var/log/php-fpm/www-slow.log request_slowlog_timeout = 3s
Apache PHP-FPM Proxy Config /etc/httpd/conf.d/php-fpm.conf
# Proxy PHP requests to PHP-FPM <FilesMatch \.php$> SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost" </FilesMatch> # Status pages (monitoring) <LocationMatch "^/(php-fpm-(status|ping))$"> SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost" Require local </LocationMatch> # Security: Deny PHP in uploads <Directory "/var/www/html/uploads"> <FilesMatch "\.php$"> Require all denied </FilesMatch> </Directory>
PHP Configuration (Essential Settings) /etc/php.ini
; PHP Configuration - Key Settings Only ; Error Handling error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT display_errors = Off log_errors = On error_log = /var/log/php-fpm/error.log ; Resource Limits max_execution_time = 30 max_input_time = 60 memory_limit = 128M ; File Uploads file_uploads = On upload_max_filesize = 64M post_max_size = 64M max_file_uploads = 20 ; Database - Works for MySQL AND MariaDB! mysqli.default_socket = /var/lib/mysql/mysql.sock pdo_mysql.default_socket = /var/lib/mysql/mysql.sock ; Timezone (CHANGE THIS!) date.timezone = America/New_York ; OPcache (Performance) opcache.enable = 1 opcache.memory_consumption = 128 opcache.max_accelerated_files = 4000 ; Security expose_php = Off allow_url_include = Off default_charset = "UTF-8"

๐Ÿ Python WSGI Configuration

mod_wsgi Configuration /etc/httpd/conf.d/wsgi.conf
# Python WSGI Configuration LoadModule wsgi_module modules/mod_wsgi_python3.so # Simple test application WSGIScriptAlias /python /var/www/wsgi-scripts/test.wsgi <Directory /var/www/wsgi-scripts> Require all granted </Directory>
Example Python WSGI Script /var/www/wsgi-scripts/test.wsgi
#!/usr/bin/python3 # Simple WSGI test with database def application(environ, start_response): status = '200 OK' output = b'Python WSGI Test\n' import sys output += f'Python: {sys.version}\n'.encode() # Test database (works with MySQL or MariaDB!) try: import pymysql conn = pymysql.connect( host='localhost', user='webapp_user', password='YourPassword', database='webapp_db' ) cursor = conn.cursor() cursor.execute("SELECT VERSION()") ver = cursor.fetchone() output += f'Database: {ver[0]}\n'.encode() conn.close() except Exception as e: output += f'DB Error: {e}\n'.encode() headers = [('Content-Type', 'text/plain')] start_response(status, headers) return [output]

๐Ÿช Perl mod_perl Configuration

mod_perl Configuration /etc/httpd/conf.d/perl.conf
# Perl mod_perl Configuration LoadModule perl_module modules/mod_perl.so Alias /perl /var/www/perl <Directory /var/www/perl> SetHandler perl-script PerlResponseHandler ModPerl::Registry PerlOptions +ParseHeaders Options +ExecCGI Require all granted </Directory> <FilesMatch "\.pl$"> SetHandler perl-script PerlResponseHandler ModPerl::Registry PerlOptions +ParseHeaders Options +ExecCGI </FilesMatch>
Example Perl CGI Script /var/www/perl/test.pl
#!/usr/bin/perl use strict; use warnings; use CGI; use DBI; my $q = CGI->new; print $q->header('text/plain'); print "Perl mod_perl Test\n"; print "Perl: $]\n\n"; # Test database (works with MySQL or MariaDB!) eval { my $dbh = DBI->connect( "DBI:mysql:database=webapp_db;host=localhost", "webapp_user", "YourPassword", {RaiseError=>1} ); my $ver = $dbh->selectrow_array("SELECT VERSION()"); print "Database: $ver\n"; $dbh->disconnect(); }; print "DB Error: $@\n" if $@;

๐Ÿงช Testing the Stack

Create Test Files

Setup Test Environment
# Create directories sudo mkdir -p /var/www/{wsgi-scripts,perl} sudo chmod 755 /var/www/{wsgi-scripts,perl} # PHP test (with database check) cat << 'EOF' | sudo tee /var/www/html/test.php <?php echo "PHP Version: " . phpversion() . "\n"; // Test database connection (MySQL or MariaDB!) $conn = new mysqli('localhost', 'webapp_user', 'YourPass', 'webapp_db'); if ($conn->connect_error) die("Failed: " . $conn->connect_error); echo "Database: Connected!\n"; $result = $conn->query("SELECT VERSION()"); $row = $result->fetch_assoc(); echo "DB Version: " . $row['VERSION()'] . "\n"; $conn->close(); ?> EOF # Python WSGI test (see example above) sudo vi /var/www/wsgi-scripts/test.wsgi sudo chmod 755 /var/www/wsgi-scripts/test.wsgi # Perl test (see example above) sudo vi /var/www/perl/test.pl sudo chmod 755 /var/www/perl/test.pl # Set SELinux contexts sudo restorecon -Rv /var/www # Test in browser: # http://your-server/test.php # http://your-server/python # http://your-server/perl/test.pl

๐Ÿ” Troubleshooting

Common Issues

Problem Solution
PHP files download instead of execute PHP-FPM not configured
sudo systemctl restart httpd php-fpm
PHP can't connect to database SELinux!
sudo setsebool -P httpd_can_network_connect_db on
Permission denied errors sudo chown -R apache:apache /var/www/html
sudo restorecon -Rv /var/www
403 Forbidden Check Directory permissions in httpd.conf
Check SELinux contexts
Apache won't start sudo apachectl configtest
sudo tail /var/log/httpd/error_log

Useful Commands

# Test Apache configuration sudo apachectl configtest # View loaded modules httpd -M # Check syntax of individual conf files sudo apachectl -t -D DUMP_VHOSTS # Restart services sudo systemctl restart httpd php-fpm # View logs in real-time sudo tail -f /var/log/httpd/error_log sudo tail -f /var/log/php-fpm/error.log # Check SELinux denials sudo ausearch -m avc -ts recent | grep httpd # List SELinux booleans for httpd getsebool -a | grep httpd # Check file contexts ls -Z /var/www/html

โšก Performance & Security

Enable Compression /etc/httpd/conf.d/compression.conf
<IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/html text/plain text/xml AddOutputFilterByType DEFLATE text/css text/javascript AddOutputFilterByType DEFLATE application/javascript application/json </IfModule>
Security Headers /etc/httpd/conf.d/security.conf
# Security headers Header always set X-Frame-Options "SAMEORIGIN" Header always set X-Content-Type-Options "nosniff" Header always set X-XSS-Protection "1; mode=block" Header always set Referrer-Policy "strict-origin-when-cross-origin" # HSTS (only after SSL is working!) # Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

โœ… Best Practices Summary

๐Ÿ’ก LAMP Stack Best Practices
  • SELinux: Configure it, don't disable it!
  • PHP-FPM: Use Unix sockets, not TCP (better performance)
  • Database: php-mysqlnd works with both MySQL and MariaDB
  • OPcache: Always enable it for PHP performance
  • SSL/TLS: Use Let's Encrypt (free, automated)
  • Firewall: Only open ports 80/443
  • Logs: Monitor them regularly
  • Updates: Keep everything patched
  • Backups: Automate them!
  • Security: Disable directory listing, hide PHP version
โš ๏ธ Don't Forget!
  1. Enable httpd_can_network_connect_db SELinux boolean
  2. Start both httpd AND php-fpm services
  3. Open firewall ports (80, 443)
  4. Set correct file ownership (apache:apache)
  5. Test configuration before reloading: apachectl configtest

๐Ÿ“„ Important Files & Logs

Configuration Files

/etc/httpd/conf/httpd.conf # Main Apache config /etc/httpd/conf.d/*.conf # Additional configs /etc/httpd/conf.modules.d/*.conf # Module configs /etc/php.ini # PHP config /etc/php-fpm.d/www.conf # PHP-FPM pool config

Log Files

/var/log/httpd/access_log # Apache access log /var/log/httpd/error_log # Apache error log /var/log/httpd/ssl_access_log # SSL access log /var/log/httpd/ssl_error_log # SSL error log /var/log/php-fpm/error.log # PHP-FPM errors /var/log/php-fpm/www-slow.log # Slow PHP scripts