Skip to content

Apache HTTP Server

Apache httpd is the long-standing, modular web server that powers a huge share of the internet. Its big differentiators are a rich module ecosystem and per-directory .htaccess overrides. This page covers installing httpd, its configuration layout, DocumentRoot, a basic <VirtualHost>, enabling modules, and .htaccess.

Tested on

AlmaLinux 9 / RHEL 9 with the httpd package from AppStream. Debian/Ubuntu uses the apache2 package and tooling — notes are inline.

When to choose Apache vs nginx

  • Choose Apache when you want .htaccess per-directory config (common in shared hosting and many PHP apps), mod_php, or a specific Apache module.
  • Choose nginx for high-concurrency static serving and reverse proxying with the lowest memory footprint. See Nginx.

Apache's concurrency behaviour is set by its MPM (Multi-Processing Module):

MPM Model Notes
prefork One process per connection, no threads Required by the non-thread-safe mod_php; heaviest on memory
worker Threads within processes Lighter than prefork
event Like worker but offloads keep-alive handling Default on RHEL 9; best for high concurrency

RHEL 9 ships the event MPM by default. If you need mod_php you must switch to prefork — but the modern recommendation is PHP-FPM instead (see PHP & PHP-FPM), which works with any MPM.

Install Apache httpd

sudo dnf install -y httpd

# Enable on boot and start now
sudo systemctl enable --now httpd

systemctl status httpd

The service and binary are called httpd on RHEL.

sudo apt update
sudo apt install -y apache2
sudo systemctl enable --now apache2

The service and binary are called apache2 on Debian.

Open the firewall

sudo firewall-cmd --add-service=http --add-service=https --permanent
sudo firewall-cmd --reload
sudo firewall-cmd --list-services

Browse to http://<server-ip>/ — you should see the Apache test page. (Debian/Ubuntu with UFW: sudo ufw allow 'Apache Full'.) See Firewalls.

Configuration layout

/etc/httpd/
├── conf/
│   └── httpd.conf          # main config (global settings, default DocumentRoot)
├── conf.d/                 # drop-in *.conf files (vhosts, ssl.conf, etc.)
├── conf.modules.d/         # *.conf files that LoadModule for installed modules
└── /var/www/html/          # default DocumentRoot

httpd.conf ends with IncludeOptional conf.d/*.conf, so any .conf file you drop in conf.d/ is loaded. Modules are loaded by files in conf.modules.d/.

/etc/apache2/
├── apache2.conf            # main config
├── ports.conf              # Listen directives
├── sites-available/        # vhost files (write here)
├── sites-enabled/          # symlinks created by a2ensite
├── mods-available/         # all available modules
└── mods-enabled/           # symlinks created by a2enmod
└── /var/www/html/          # default DocumentRoot

Debian splits everything into available/enabled pairs managed by helper commands:

sudo a2ensite mysite      # enable a site (symlink into sites-enabled)
sudo a2dissite mysite     # disable it
sudo a2enmod rewrite      # enable a module
sudo a2dismod rewrite     # disable it
sudo systemctl reload apache2   # apply

DocumentRoot and a basic VirtualHost

DocumentRoot is the directory Apache serves files from. The global default is /var/www/html. To host a site under its own root, define a <VirtualHost>.

1. Create the site files

sudo mkdir -p /var/www/example.com
echo '<h1>Hello from Apache on AlmaLinux 9</h1>' | sudo tee /var/www/example.com/index.html

2. SELinux: label the custom DocumentRoot (RHEL/AlmaLinux)

Apache (like nginx) is confined by SELinux. A DocumentRoot outside /var/www/html must carry the httpd_sys_content_t type or you will get 403 Forbidden:

sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/example.com(/.*)?"
sudo restorecon -Rv /var/www/example.com

Outbound connections

If Apache must reach a network service (proxying, remote DB, sending mail from an app), allow it persistently: sudo setsebool -P httpd_can_network_connect 1. See SELinux.

3. Create the vhost file

On RHEL/AlmaLinux, create /etc/httpd/conf.d/example.com.conf:

<VirtualHost *:80>
    ServerName   example.com
    ServerAlias  www.example.com
    DocumentRoot /var/www/example.com

    <Directory /var/www/example.com>
        # Allow Apache to serve this directory
        Require all granted

        # Control whether .htaccess can override settings here (see below)
        AllowOverride All
    </Directory>

    # Per-site logs
    ErrorLog  /var/log/httpd/example.com-error.log
    CustomLog /var/log/httpd/example.com-access.log combined
</VirtualHost>

On Debian, write this to /etc/apache2/sites-available/example.com.conf (logs go to /var/log/apache2/), then run sudo a2ensite example.com.

Enabling modules

Apache's functionality comes from modules. On RHEL many are installed and loaded by default; install extra ones with dnf.

# Example: SSL and rewrite support
sudo dnf install -y mod_ssl

# List currently loaded modules
httpd -M

mod_rewrite is included in the base httpd package and loaded via conf.modules.d/. To confirm: httpd -M | grep rewrite.

sudo a2enmod ssl rewrite
sudo systemctl reload apache2
apache2ctl -M        # list loaded modules

.htaccess and AllowOverride

.htaccess is a per-directory config file. Apache reads it on every request for that directory tree — handy on shared hosting where you cannot edit the main config, but slower than central config.

.htaccess only takes effect if AllowOverride permits it for that directory:

AllowOverride value Effect
None .htaccess is ignored entirely (default on RHEL 9 for /var/www/html)
All All directives allowed in .htaccess
FileInfo, AuthConfig, etc. Allow only specific classes of directive

Example .htaccess in /var/www/example.com/ that needs mod_rewrite and AllowOverride All:

# /var/www/example.com/.htaccess
RewriteEngine On
# Redirect everything that is not a real file to index.php (common front-controller pattern)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]

Performance

On servers you fully control, prefer putting these rules directly in the <Directory> block and leaving AllowOverride None. This avoids Apache walking the directory tree looking for .htaccess files on every request.

Test the config, then reload

Never reload blindly — validate first:

# RHEL/AlmaLinux
sudo apachectl configtest
# (equivalent: sudo httpd -t)

Expected:

Syntax OK

Then apply with a graceful reload:

sudo systemctl reload httpd      # RHEL/AlmaLinux
# sudo systemctl reload apache2  # Debian

On Debian the test command is sudo apache2ctl configtest.

Logs

/var/log/httpd/access_log     # all requests
/var/log/httpd/error_log      # errors and startup messages
/var/log/httpd/example.com-*  # the per-vhost logs from our config
/var/log/apache2/access.log
/var/log/apache2/error.log
# Tail the error log while testing (RHEL path shown)
sudo tail -f /var/log/httpd/error_log

Verify your work

# 1. Service enabled and active
systemctl is-enabled httpd && systemctl is-active httpd

# 2. Config syntax is OK
sudo apachectl configtest

# 3. Firewall allows HTTP/HTTPS
sudo firewall-cmd --list-services | grep http

# 4. The vhost responds
curl -H 'Host: example.com' http://localhost/
# -> <h1>Hello from Apache on AlmaLinux 9</h1>

# 5. Custom DocumentRoot has the right SELinux label
ls -Zd /var/www/example.com   # -> httpd_sys_content_t

Summary

  • Install with dnf install httpd (service: httpd) on RHEL/AlmaLinux or apt install apache2 (service: apache2) on Debian.
  • RHEL config lives in /etc/httpd/conf/httpd.conf plus drop-ins in conf.d/; Debian uses /etc/apache2 with a2ensite/a2enmod helpers.
  • DocumentRoot sets the served directory; <VirtualHost> hosts a site under its own root and ServerName.
  • .htaccess only works where AllowOverride allows it; prefer central config when you control the server.
  • Custom DocumentRoots on RHEL/AlmaLinux need the httpd_sys_content_t SELinux label.
  • Always apachectl configtest before systemctl reload httpd; logs are in /var/log/httpd/.

Next: add PHP with PHP & PHP-FPM, host multiple sites in Virtual Hosts, or enable HTTPS in HTTPS with Let's Encrypt.

Test yourself