Skip to main content
Advertisement

Pro Tips — Apache vs Nginx Selection and Coexistence Patterns

This chapter revisits the real-world selection criteria for Apache vs Nginx, covers the port-separation pattern for running both web servers simultaneously on one server, and addresses common Apache issues in production.


Apache vs Nginx — Final Selection Criteria

CriteriaChoose ApacheChoose Nginx
Traffic scaleSmall to mediumLarge scale (thousands of req/sec+)
PHP executionmod_php (Prefork) or php-fpmphp-fpm (always)
Config flexibility.htaccess, complex mod_rewriteSimple config syntax
Module varietyVery diverse mod_* ecosystemLimited (compiled in)
Memory usageHigher (process/thread model)Lower (event-driven)
Static filesFast (sufficient)Very fast
Legacy compatibilityVery high (.htaccess, etc.)Low
Primary use casePHP apps, CMS (WordPress·Drupal), legacyReverse proxy, high-traffic, containers

Practical Conclusion

  • New Java/API backend: Nginx (excellent reverse proxy)
  • WordPress·Drupal·Joomla: Apache (mod_php, .htaccess dependency)
  • Legacy PHP maintenance: Apache (keep existing .htaccess intact)
  • Microservices API Gateway: Nginx
  • Cloud / Kubernetes: Nginx Ingress

Apache + Nginx Coexistence (Port Separation Pattern)

When both Apache and Nginx need to run on the same server, use port separation to avoid conflicts.

Setup: Nginx Front-End + Apache Back-End

[Internet] → [Nginx :80/:443] → [Apache :8080] → [PHP/CMS]

Nginx handles SSL termination and static files; Apache focuses on PHP processing.

# /etc/apache2/ports.conf
Listen 8080

# /etc/apache2/sites-available/wordpress.conf
<VirtualHost *:8080>
ServerName example.com
DocumentRoot /var/www/wordpress
<Directory /var/www/wordpress>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

# Static files served directly by Nginx
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
root /var/www/wordpress;
expires 30d;
add_header Cache-Control "public";
}

# PHP processing proxied to Apache
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}

Common Apache Issues and Solutions

Issue 1: mod_rewrite Not Working

Symptom: RewriteEngine On in .htaccess is ignored

Cause 1: AllowOverride None

<Directory "/var/www/html">
AllowOverride All # Fix: change to All
</Directory>

Cause 2: mod_rewrite not enabled

apache2ctl -M | grep rewrite
sudo a2enmod rewrite
sudo systemctl reload apache2

Issue 2: 403 Forbidden Error

Cause 1: File/directory permission issue

sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;
sudo chown -R www-data:www-data /var/www/html/

Cause 2: Require all denied in Directory block

<Directory "/var/www/html">
Require all granted # Change denied → granted
</Directory>

Cause 3: SELinux (CentOS/RHEL)

sudo restorecon -Rv /var/www/html/

Issue 3: Slow Response — HostnameLookups On

# Wrong — causes DNS lookup delays per request
HostnameLookups On

# Correct — always Off (default)
HostnameLookups Off

Issue 4: MaxRequestWorkers Exceeded

# Event MPM tuning
<IfModule mpm_event_module>
StartServers 4
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 400 # Increase this
MaxConnectionsPerChild 0
</IfModule>

Apache Security Hardening Checklist

ServerTokens Prod           # Hide version info
ServerSignature Off # Hide server signature

<Directory /var/www/html>
Options -Indexes # Disable directory listing
</Directory>

FileETag MTime Size # Remove inode from ETag

<LimitExcept GET POST HEAD>
Require all denied # Block unnecessary HTTP methods
</LimitExcept>

<Files ".ht*">
Require all denied # Block .htaccess direct access
</Files>

<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
</IfModule>

Deployment Automation Script

#!/bin/bash
set -e

echo "=== Apache Config Validation ==="
if apache2ctl configtest; then
echo "✅ Syntax check passed"
else
echo "❌ Syntax errors — aborting"
exit 1
fi

echo "=== Reloading Apache ==="
systemctl reload apache2
echo "✅ Deployment complete"

Summary

SituationRecommendation
New Java/API projectChoose Nginx
WordPress / PHP CMSChoose Apache
CoexistenceNginx (80/443) → Apache (8080) proxy
403 errorCheck permissions → AllowOverride → SELinux
Security hardeningServerTokens Prod + Options -Indexes + security headers
PerformanceEvent MPM + HostnameLookups Off + AllowOverride None
Advertisement