apache
Installation
SKILL.md
Identity
- Unit:
apache2.service(Debian/Ubuntu),httpd.service(RHEL/Fedora/CentOS) - Config (Debian):
/etc/apache2/apache2.conf,/etc/apache2/sites-available/,/etc/apache2/sites-enabled/,/etc/apache2/mods-available/,/etc/apache2/mods-enabled/,/etc/apache2/conf-available/ - Config (RHEL):
/etc/httpd/httpd.conf,/etc/httpd/conf.d/,/etc/httpd/conf.modules.d/ - Logs:
journalctl -u apache2/journalctl -u httpd,/var/log/apache2/(Debian),/var/log/httpd/(RHEL) - User:
www-data(Debian/Ubuntu),apache(RHEL/Fedora) - Distro install:
apt install apache2/dnf install httpd
Key Operations
| Operation | Debian/Ubuntu | RHEL/Fedora |
|---|---|---|
| Status | systemctl status apache2 |
systemctl status httpd |
| Start | sudo systemctl start apache2 |
sudo systemctl start httpd |
| Stop | sudo systemctl stop apache2 |
sudo systemctl stop httpd |
| Reload (graceful) | sudo systemctl reload apache2 |
sudo systemctl reload httpd |
| Restart | sudo systemctl restart apache2 |
sudo systemctl restart httpd |
| Test config | sudo apachectl configtest |
sudo apachectl configtest |
| Full config dump | sudo apache2ctl -S |
sudo httpd -S |
| Enable site | sudo a2ensite example.conf |
(symlink to /etc/httpd/conf.d/) |
| Disable site | sudo a2dissite example.conf |
(remove symlink) |
| Enable module | sudo a2enmod rewrite |
edit /etc/httpd/conf.modules.d/ |
| Disable module | sudo a2dismod rewrite |
comment out LoadModule line |
| List enabled modules | apache2ctl -M |
httpd -M |
| Check open ports | ss -tlnp | grep apache2 |
ss -tlnp | grep httpd |
| Check vhost config | apache2ctl -S |
httpd -S |
| Tail error log | sudo tail -f /var/log/apache2/error.log |
sudo tail -f /var/log/httpd/error_log |
| Tail access log | sudo tail -f /var/log/apache2/access.log |
sudo tail -f /var/log/httpd/access_log |
| Create htpasswd file | htpasswd -c /etc/apache2/.htpasswd user |
htpasswd -c /etc/httpd/.htpasswd user |
| Add htpasswd user | htpasswd /etc/apache2/.htpasswd user |
htpasswd /etc/httpd/.htpasswd user |
| Graceful stop | sudo apachectl graceful-stop |
sudo apachectl graceful-stop |
Expected State
- Ports: 80/tcp (HTTP), 443/tcp (HTTPS)
- Verify:
ss -tlnp | grep ':80\|:443' - Firewall (Debian):
sudo ufw allow 'Apache Full'orsudo ufw allow 80,443/tcp - Firewall (RHEL):
sudo firewall-cmd --add-service=http --add-service=https --permanent && sudo firewall-cmd --reload - Loaded modules: Verify with
apache2ctl -M— expectssl_module,rewrite_modulefor typical setups
Health Checks
systemctl is-active apache2(orhttpd) →activesudo apachectl configtest 2>&1→ containsSyntax OKcurl -sI http://localhost→ HTTP response (not connection refused)ss -tlnp | grep ':80\|:443'→ apache2 or httpd listed
Common Failures
| Symptom | Likely cause | Check/Fix |
|---|---|---|
403 Forbidden |
Missing Require all granted or wrong file permissions |
Check Directory block for Require directive; verify DocumentRoot permissions with ls -la |
404 Not Found |
DocumentRoot wrong path, or file doesn't exist | apache2ctl -S to confirm active vhost; verify path with ls |
Address already in use on port 80/443 |
Another process bound to the port | ss -tlnp | grep :80 — find and stop conflicting process (often another httpd or nginx) |
.htaccess not working |
AllowOverride None in the Directory block |
Change to AllowOverride All or the specific directives needed; reload apache |
SSL handshake failure / ERR_SSL_PROTOCOL_ERROR |
mod_ssl not loaded, wrong cert path, or TLS version mismatch | apache2ctl -M | grep ssl; verify cert paths; check SSLProtocol directive |
Permission denied on files |
Apache user (www-data/apache) can't read files |
chown -R www-data:www-data /var/www/mysite or fix SELinux context (restorecon -Rv /var/www/) |
mod_rewrite not working |
Module not enabled, or AllowOverride not set | a2enmod rewrite && systemctl reload apache2; ensure AllowOverride All is set |
| Wrong VirtualHost matches requests | ServerName conflict or missing default vhost | apache2ctl -S to see vhost matching order; add _default_ or explicit ServerName |
AH00558: Could not reliably determine server's FQDN |
ServerName not set globally | Add ServerName localhost to apache2.conf or the main httpd.conf |
413 Request Entity Too Large |
LimitRequestBody too small for uploads |
Increase LimitRequestBody in the VirtualHost or Directory block |
Pain Points
- Debian vs RHEL config layout: Debian uses
a2ensite/a2enmodwithsites-available/+sites-enabled/symlinks and separatemods-available/. RHEL puts everything inconf.d/with no enable/disable tooling — you manage files directly. .htaccessperformance cost: Every request traverses all parent directories looking for.htaccessfiles whenAllowOverrideis anything butNone. On high-traffic sites this is measurable overhead. PreferAllowOverride Noneand put directives in the VirtualHost block directly.- ServerName ordering and default vhost selection: Apache picks the first VirtualHost that matches the requested IP:port if no
ServerNamematches. Unlike nginx, there is no explicitdefault_serverflag — the first defined vhost on a given port becomes the default. File load order (alphabetical in conf.d/ and sites-enabled/) determines which is first. - MPM selection (prefork vs worker vs event):
preforkis required formod_phpbut is single-threaded per process and memory-heavy.workeris multi-threaded but incompatible with non-thread-safe PHP.event(the default in modern Apache) is the best choice with PHP-FPM. Check withapache2ctl -V | grep MPM. - mod_php vs PHP-FPM:
mod_phpembeds PHP into Apache (requires prefork MPM, loads PHP for every request including static files). PHP-FPM viamod_proxy_fcgiuses a separate process pool, works with event MPM, and lets you run multiple PHP versions. Prefer PHP-FPM for new deployments. - Graceful vs immediate restart:
systemctl reload apache2sends SIGUSR1 (graceful restart) — existing requests finish before workers restart.systemctl restart apache2sends SIGTERM, immediately killing all connections. Use reload for production config changes. - SELinux on RHEL: If files have correct Unix permissions but Apache still gets
Permission denied, SELinux is likely the cause. Check withausearch -c httpd --raw | audit2allowand fix withchcon -t httpd_sys_content_tor a custom policy.
References
See references/ for:
httpd.conf.annotated— complete server config and VirtualHost blocks with every directive explainedcommon-patterns.md— VirtualHost, SSL, reverse proxy, PHP-FPM, htpasswd, rewrites, and static file examplesdocs.md— official documentation links
Related skills