How to Install the Apache Web Server
Install Apache on Linux, enable modules, configure named virtual hosts, use .htaccess, and verify your setup — covering Debian, Ubuntu, Fedora, RHEL, and Arch.
Before you start
- ▸A Linux server with sudo or root access
- ▸A domain name or local hostname pointed at the server's IP address (for virtual host testing)
- ▸Basic familiarity with a terminal text editor such as nano or vim
Apache HTTP Server remains one of the most widely deployed web servers on the internet. Its module system, per-directory configuration via .htaccess, and flexible virtual host model make it a solid choice for hosting anything from a single static site to dozens of applications on one machine. This guide walks through a full installation: getting Apache running, enabling useful modules, configuring a named virtual host, and locking things down enough to serve a real site.
Installation
Debian and Ubuntu
Apache's package is called apache2 on Debian-family systems.
sudo apt update && sudo apt install apache2 -y
Fedora and RHEL / Rocky Linux
On Red Hat-family systems the package is httpd.
sudo dnf install httpd -y
Arch Linux
sudo pacman -S apache
Starting and Enabling the Service
Apache is managed by systemd on all modern distributions. Enable it so it starts at boot, then start it immediately.
Debian / Ubuntu
The package enables the service automatically; confirm it is running:
sudo systemctl enable --now apache2
sudo systemctl status apache2
Fedora / RHEL / Rocky / Arch
sudo systemctl enable --now httpd
sudo systemctl status httpd
You should see Active: active (running) in the output. Visit http://localhost in a browser or run curl -I http://localhost to confirm Apache returns a 200 response.
Opening the Firewall
If a firewall is active — and it should be — allow HTTP and HTTPS traffic before going further.
ufw (Debian / Ubuntu)
sudo ufw allow 'Apache Full'
sudo ufw status
firewalld (Fedora / RHEL / Rocky)
sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reload
nftables (Arch or manual setup)
sudo nft add rule inet filter input tcp dport { 80, 443 } accept
Make this persistent by saving your ruleset to /etc/nftables.conf and ensuring nftables.service is enabled.
Directory Layout
Understanding where files live saves significant debugging time later.
- /etc/apache2/ (Debian/Ubuntu) or /etc/httpd/ (RHEL family) — main configuration tree
- sites-available/ and sites-enabled/ — virtual host definitions (Debian/Ubuntu only; RHEL family uses conf.d/)
- /var/www/html/ — default document root
- /var/log/apache2/ or /var/log/httpd/ — access and error logs
Enabling Modules
Apache's power comes from its module system. On Debian/Ubuntu, a2enmod and a2dismod manage modules by creating or removing symlinks under mods-enabled/. On RHEL-family systems, modules are loaded via LoadModule directives in /etc/httpd/conf.modules.d/.
Commonly needed modules
- rewrite — URL rewriting; required by most CMS software
- ssl — HTTPS support
- headers — set and modify HTTP response headers
- deflate — gzip compression
Enable on Debian / Ubuntu
sudo a2enmod rewrite ssl headers deflate
sudo systemctl restart apache2
Verify a module is loaded (any distro)
apache2ctl -M 2>/dev/null | grep rewrite
# or on RHEL family:
httpd -M 2>/dev/null | grep rewrite
Expected output (will vary): rewrite_module (shared)
On RHEL-family systems, mod_rewrite and mod_ssl are installed but may need their LoadModule line uncommented in /etc/httpd/conf.modules.d/00-base.conf.
Configuring a Virtual Host
A virtual host lets Apache serve different content depending on the requested hostname. This is how you host example.com and blog.example.com from a single IP.
Create the document root and a test page
sudo mkdir -p /var/www/example.com/public_html
echo '<h1>It works</h1>' | sudo tee /var/www/example.com/public_html/index.html
sudo chown -R www-data:www-data /var/www/example.com # Debian/Ubuntu
# sudo chown -R apache:apache /var/www/example.com # RHEL family / Arch
Write the virtual host file (Debian / Ubuntu)
sudo nano /etc/apache2/sites-available/example.com.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com/public_html
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
<Directory /var/www/example.com/public_html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
Enable the site and reload
sudo a2ensite example.com.conf
sudo a2dissite 000-default.conf # optional: disable the default site
sudo systemctl reload apache2
Virtual host on RHEL / Rocky / Arch
Drop the file directly into /etc/httpd/conf.d/example.com.conf using the same <VirtualHost> block above, replacing ${APACHE_LOG_DIR} with /var/log/httpd. Then reload:
sudo systemctl reload httpd
Using .htaccess
AllowOverride All in the <Directory> block above is what permits .htaccess files. Each directory can contain one, and Apache merges the rules downward from the document root. This is convenient but carries a performance cost because Apache checks for a .htaccess file on every request. On a high-traffic server, move the rules into the virtual host block itself and set AllowOverride None.
Example: redirect all HTTP traffic to HTTPS
cat > /var/www/example.com/public_html/.htaccess <<'EOF'
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
EOF
Example: custom 404 page
echo 'ErrorDocument 404 /404.html' | sudo tee -a /var/www/example.com/public_html/.htaccess
Security note: never allow .htaccess files to override AuthConfig or Limit directives on a multi-tenant server unless you fully trust the users writing those files.
Verifying the Configuration
Always test the configuration before reloading Apache. A syntax error will prevent the service from starting.
sudo apache2ctl configtest # Debian/Ubuntu
sudo apachectl configtest # RHEL family / Arch
A clean result shows Syntax OK. Any errors printed above that line point to the file and line number causing the problem.
Test the virtual host from the command line by spoofing the Host header if DNS is not yet pointing at the server:
curl -H 'Host: example.com' http://<server-ip>/
Troubleshooting
Apache fails to start
Run journalctl -xeu apache2 (or httpd) for the full systemd log. Most failures are syntax errors caught by configtest, port conflicts (ss -tlnp | grep :80), or missing module files referenced in configuration.
403 Forbidden on the document root
Check two things: file permissions (ls -la /var/www/example.com/public_html/) and SELinux/AppArmor context. On RHEL-family with SELinux enforcing:
sudo restorecon -Rv /var/www/example.com/
sudo chcon -R -t httpd_sys_content_t /var/www/example.com/
Changes to .htaccess not taking effect
Confirm AllowOverride All is set for the directory in the virtual host config, and that mod_rewrite is loaded. Restart (not just reload) Apache after changing module state.
Virtual host serves the wrong site
Apache matches virtual hosts in the order they appear in configuration. The first match wins. List all configured virtual hosts with:
sudo apache2ctl -S # or apachectl -S
The output shows which VirtualHost block handles each name and highlights any mismatches.
Frequently asked questions
- What is the difference between apache2 and httpd package names?
- They are the same software. Debian and Ubuntu chose the name apache2 when they packaged it; Red Hat-family distributions and Arch have always called it httpd. The binary and configuration behave identically.
- Should I use .htaccess or put rules directly in the virtual host config?
- Put rules in the virtual host config whenever you can. Apache reads .htaccess files on every single request for every directory in the path, which adds measurable overhead. Reserve .htaccess for shared hosting environments where you do not control the main config.
- How do I host multiple sites on one Apache server?
- Create a separate VirtualHost block (and its own config file) for each site with a unique ServerName. Apache selects the correct block by matching the Host header in each incoming request against ServerName and ServerAlias values.
- Why do I get a 403 Forbidden error even though the file exists?
- The most common causes are file permissions that the Apache user cannot read, a missing Require all granted directive in the Directory block, or an SELinux context mismatch on RHEL-family systems. Run apachectl -S and check journalctl for the specific denial.
- Does Apache work on systems using Wayland?
- Apache is a server daemon with no graphical component, so the display server in use on the desktop is completely irrelevant. It runs identically regardless of whether the system uses Wayland, X11, or no display server at all.
Related guides
Configure Prometheus Alertmanager
Configure Prometheus Alertmanager with routing trees, receivers, inhibition rules, grouping, Go templates, and PagerDuty/Slack on-call integrations.
Build an Intranet Server on Linux
Set up a complete small-office intranet on one Linux box: Nginx web server, dnsmasq local DNS, Samba file sharing, and a Wiki.js team wiki.
Build an nftables Firewall Script
Build a complete nftables firewall from scratch: tables, chains, sets, default-deny input policy, service allowlisting, and persistent systemd configuration.
Caddy as a Reverse Proxy
Set up Caddy as a reverse proxy with automatic HTTPS, load balancing, WebSocket passthrough, reusable snippets, and header control — no certbot required.