How to install and configure Apache on Linux

How to install and configure Apache on Linux

We may earn an affiliate commission through purchases made from our guides and tutorials.

Apache HTTP Server remains the most widely deployed general-purpose web server on Linux. It is stable, well-documented, and flexible enough to serve static sites, dynamic apps, and reverse-proxied backends. You will install a secure baseline with system service management, a firewall, virtual hosts, TLS, and a few tuning passes so you can go from “new droplet” to “production-ready.” You will need a Linux VM with shell access; if you use a cloud VPS, make sure your DigitalOcean account is set up before you begin, as we will assume you can create a droplet and SSH into it. We start with packages, then layer on configuration and security so each step builds on the last.

Prerequisites

Use a fresh Linux server with a non-root sudo user and an A record pointed at your server’s public IP. Update your package index and base system first to avoid dependency surprises, then confirm that ports 80 and 443 are open in your provider’s control panel. With that in place, you can install the web server and verify it runs before touching configuration.

Update the system

Ubuntu or Debian:

sudo apt update
sudo apt -y upgrade

RHEL, AlmaLinux, Rocky, or Fedora:

sudo dnf -y upgrade

Install Apache

Package names differ slightly by family. After installation, enable the service so it survives reboots and start it now to verify the default page loads.

Ubuntu or Debian:

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

RHEL, AlmaLinux, or Rocky:

sudo dnf -y install httpd
sudo systemctl enable --now httpd

Fedora:

sudo dnf -y install httpd
sudo systemctl enable --now httpd

Open a browser to http://<server-ip> to confirm the default Apache test page. If it fails to load, check the firewall next.

Configure the firewall

A running service is only useful if the network allows it. Allow HTTP now so you can validate configuration, then add HTTPS before enabling TLS. This prevents unnecessary downtime while you work through certificates.

Ubuntu or Debian with UFW:

sudo ufw allow "Apache"
sudo ufw status

RHEL family with firewalld:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

Understand the filesystem layout

Knowing where Apache looks for configuration and content reduces guesswork when something does not work. Debian-based systems use site-level includes; RHEL-based systems centralize under a single config tree. The implication is simple: follow the platform conventions to avoid conflicts with package updates.

  • Ubuntu/Debian:
    Config root /etc/apache2/, main file /etc/apache2/apache2.conf, modules in /etc/apache2/mods-*, sites in /etc/apache2/sites-available/ with symlinks in sites-enabled/, web root /var/www/html.
  • RHEL/AlmaLinux/Rocky/Fedora:
    Config root /etc/httpd/, main file /etc/httpd/conf/httpd.conf, additional files under /etc/httpd/conf.d/, web root /var/www/html.

We will create a dedicated virtual host and document root rather than editing the default site, which keeps your changes isolated and easier to roll back.

Create a site and document root

Create a directory for your domain, assign ownership to your deploy user, and place a minimal index file. This validates that your virtual host points to the expected path before you add TLS or rewrites.

sudo mkdir -p /var/www/example.com/public
sudo chown -R $USER:$USER /var/www/example.com
printf '%s\n' '<!doctype html><title>It works</title><h1>Apache is serving example.com</h1>' > /var/www/example.com/public/index.html

Define a virtual host

A virtual host (vhost) maps a requested hostname to a configuration block. Start with a simple HTTP vhost and log files in a dedicated directory. This structure keeps logs per site, which simplifies troubleshooting and analytics later.

Ubuntu or Debian:

sudo tee /etc/apache2/sites-available/example.com.conf >/dev/null <<'CONF'
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/public

    <Directory /var/www/example.com/public>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog  ${APACHE_LOG_DIR}/example.com_error.log
    CustomLog ${APACHE_LOG_DIR}/example.com_access.log combined
</VirtualHost>
CONF

sudo a2ensite example.com.conf
sudo a2dissite 000-default.conf
sudo systemctl reload apache2

RHEL, AlmaLinux, Rocky, or Fedora:

sudo tee /etc/httpd/conf.d/example.com.conf >/dev/null <<'CONF'
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/public

    <Directory /var/www/example.com/public>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog  /var/log/httpd/example.com_error.log
    CustomLog /var/log/httpd/example.com_access.log combined
</VirtualHost>
CONF

sudo systemctl reload httpd

Browse to http://example.com after updating your DNS A record. If the default page still appears, the vhost is mis-selected; ensure ServerName matches the Host header and that only one site answers on port 80.

Enable essential modules

Apache loads features as modules. Enabling only what you need reduces memory footprint and attack surface. You will use mod_ssl for TLS, mod_rewrite for clean URLs, and mod_headers for security headers. We will add mod_http2 for modern browsers on TLS.

Ubuntu or Debian:

sudo a2enmod ssl rewrite headers http2
sudo systemctl reload apache2

RHEL family:

# ssl and rewrite ship by default; ensure they are loaded
sudo sed -i 's/^#LoadModule ssl_module/LoadModule ssl_module/' /etc/httpd/conf.modules.d/00-ssl.conf
sudo sed -i 's/^#LoadModule rewrite_module/LoadModule rewrite_module/' /etc/httpd/conf.modules.d/00-base.conf
echo "LoadModule http2_module modules/mod_http2.so" | sudo tee /etc/httpd/conf.modules.d/10-http2.conf
sudo systemctl restart httpd

Obtain and install TLS certificates

Use Let’s Encrypt via Certbot to automate certificate issuance and renewal. Installing the web server plugin lets Certbot edit your vhost and add a secure redirect. Enabling HTTPS early ensures that downstream configuration, like security headers and HTTP/2, applies immediately.

Ubuntu or Debian:

sudo apt -y install certbot python3-certbot-apache
sudo certbot --apache -d example.com -d www.example.com --redirect --hsts

RHEL, AlmaLinux, or Rocky:

sudo dnf -y install certbot python3-certbot-apache
sudo certbot --apache -d example.com -d www.example.com --redirect --hsts

Open the firewall for HTTPS if you have not already.

UFW:

sudo ufw allow "Apache Full"

firewalld:

sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Confirm automatic renewals:

sudo systemctl status certbot.timer
sudo certbot renew --dry-run

Add baseline security headers

Security headers reduce common risks such as clickjacking and MIME sniffing. Apply them globally so all vhosts inherit safe defaults, then override per site if an app needs relaxed policies. This approach centralizes your security posture while allowing exceptions.

Ubuntu or Debian:

sudo tee /etc/apache2/conf-available/security-headers.conf >/dev/null <<'CONF'
<IfModule mod_headers.c>
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set X-Content-Type-Options "nosniff"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
  Header always set X-XSS-Protection "0"
  Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
</IfModule>
CONF

sudo a2enconf security-headers
sudo systemctl reload apache2

RHEL family:

sudo tee /etc/httpd/conf.d/security-headers.conf >/dev/null <<'CONF'
<IfModule mod_headers.c>
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set X-Content-Type-Options "nosniff"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
  Header always set X-XSS-Protection "0"
  Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
</IfModule>
CONF

sudo systemctl reload httpd

Optimize Apache for your instance

Apache uses Multi-Processing Modules (MPMs) to handle connections. event is best for TLS and HTTP/2 because it scales keep-alives efficiently. Right-size worker counts to your vCPU and memory so you avoid swapping under load.

Ubuntu or Debian:

sudo a2dismod mpm_prefork
sudo a2enmod mpm_event
sudo systemctl restart apache2

RHEL family:

# Ensure event MPM is active
sudo sed -i 's/^LoadModule mpm_prefork_module/#LoadModule mpm_prefork_module/' /etc/httpd/conf.modules.d/00-mpm.conf
sudo sed -i 's/^#LoadModule mpm_event_module/LoadModule mpm_event_module/' /etc/httpd/conf.modules.d/00-mpm.conf
sudo systemctl restart httpd

Tune worker settings. Start conservatively, then load test and iterate.

Ubuntu or Debian:

sudo tee /etc/apache2/conf-available/mpm_event_tuning.conf >/dev/null <<'CONF'
<IfModule mpm_event_module>
  ServerLimit           16
  StartServers           2
  ThreadLimit           64
  ThreadsPerChild       32
  MaxRequestWorkers    512
  MaxConnectionsPerChild  0
</IfModule>
CONF

sudo a2enconf mpm_event_tuning
sudo systemctl reload apache2

RHEL family:

sudo tee /etc/httpd/conf.d/mpm_event_tuning.conf >/dev/null <<'CONF'
<IfModule mpm_event_module>
  ServerLimit             16
  StartServers             2
  ThreadLimit             64
  ThreadsPerChild         32
  MaxRequestWorkers      512
  MaxConnectionsPerChild   0
</IfModule>
CONF

sudo systemctl reload httpd

These values suit a 2–4 vCPU VM with moderate traffic. If MaxRequestWorkers is too high for memory, processes will swap; if too low, Apache queues requests. Plan to run a quick load test and revisit these numbers.

Serve PHP or an application backend

Many apps need PHP or a reverse proxy to another service. You have two common paths. Use php-fpm with proxy_fcgi for PHP stacks, or mod_proxy to send requests to an upstream such as Node, Python, or Go. Keeping application runtimes out of Apache worker processes improves stability.

PHP via php-fpm (Ubuntu or Debian):

sudo apt -y install php-fpm libapache2-mod-fcgid
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php*-fpm
sudo systemctl reload apache2

PHP via php-fpm (RHEL family):

sudo dnf -y install php-fpm
sudo systemctl enable --now php-fpm
echo "LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so" | sudo tee /etc/httpd/conf.modules.d/10-proxy_fcgi.conf
sudo systemctl reload httpd

Reverse proxy to an upstream app:

# Enable proxy modules if needed
sudo bash -lc 'apachectl -M | grep proxy || true'
# Ubuntu/Debian:
sudo a2enmod proxy proxy_http
sudo systemctl reload apache2
# RHEL family:
echo "LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so" | sudo tee /etc/httpd/conf.modules.d/10-proxy_http.conf
sudo systemctl reload httpd

Add a location block in your vhost to forward traffic:

ProxyPass        /app http://127.0.0.1:3000/
ProxyPassReverse /app http://127.0.0.1:3000/

Handle SELinux and permissions (RHEL family)

If SELinux is enforcing, Apache may not read your content or connect to backends until you set the correct contexts and booleans. Correct labeling avoids brittle workarounds and keeps the system policy intact.

# Allow Apache to connect to network backends
sudo setsebool -P httpd_can_network_connect 1
# Label your custom web root
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/example.com(/.*)?"
sudo restorecon -Rv /var/www/example.com

Log management and rotation

Logs support both debugging and monitoring. Keep them per site, rotate them predictably, and ship them if you have a central log stack. Use the combined format for analytics and keep error logs clean to spot regressions quickly.

Confirm rotation configuration:

Ubuntu or Debian:

sudo cat /etc/logrotate.d/apache2

RHEL family:

sudo cat /etc/logrotate.d/httpd

Tail logs while testing:

sudo tail -f /var/log/apache2/*log    # Ubuntu/Debian
# or
sudo tail -f /var/log/httpd/*log      # RHEL family

Hardening checklist

You reduce information leakage and shrink your attack surface by removing modules and tokens you do not need. Hide version banners, disable directory indexes, and prefer least privilege for file ownership. These changes are low risk and provide immediate benefits.

Ubuntu or Debian:

# Hide version tokens
sudo sed -i 's/^#*ServerTokens.*/ServerTokens Prod/' /etc/apache2/conf-available/security.conf
sudo sed -i 's/^#*ServerSignature.*/ServerSignature Off/' /etc/apache2/conf-available/security.conf
sudo a2enconf security
# Disable autoindex if not needed
sudo a2dismod autoindex
sudo systemctl reload apache2

RHEL family:

sudo tee -a /etc/httpd/conf.d/security.conf >/dev/null <<'CONF'
ServerTokens Prod
ServerSignature Off
CONF
# Disable autoindex if loaded
sudo sed -i 's/^LoadModule autoindex_module/#LoadModule autoindex_module/' /etc/httpd/conf.modules.d/00-base.conf
sudo systemctl reload httpd

Set sensible file permissions:

sudo chown -R $USER:$USER /var/www/example.com
find /var/www/example.com -type d -exec chmod 755 {} \;
find /var/www/example.com -type f -exec chmod 644 {} \;

Graceful reloads and zero-downtime deploys

Reloading keeps connections alive while applying changes. Use configtest before a reload to catch syntax errors, then rely on systemd to manage the process. This avoids accidental service interruptions during routine updates.

sudo apachectl configtest
sudo systemctl reload apache2   # Ubuntu/Debian
# or
sudo systemctl reload httpd     # RHEL family

For content deploys, update files atomically and avoid partial writes:

rsync -az --delete site_build/ /var/www/example.com/public/

Basic troubleshooting

When something breaks, change one thing at a time and check logs. Most issues trace to DNS, firewall rules, vhost selection, or file permissions. Validate each layer from the network inward so you avoid chasing symptoms.

# Is Apache listening on 80/443?
sudo ss -tlnp | egrep ':80|:443'
# Which vhost will answer a given Host header?
sudo apachectl -S
# Quick syntax check
sudo apachectl configtest
# Live log view while reproducing the error
sudo tail -f /var/log/apache2/error.log /var/log/apache2/access.log   # Ubuntu/Debian
# or
sudo tail -f /var/log/httpd/error_log /var/log/httpd/access_log       # RHEL family

If HTTPS fails, verify that the certificate files exist and that renewal timers are active. If rewrites misbehave, temporarily disable .htaccess overrides in the vhost and move rules into the main config for clarity.

What you have now

You installed Apache, opened the firewall, created a dedicated vhost and document root, enabled HTTP/2 with TLS, added security headers, tuned the event MPM, and prepared PHP or reverse proxying for application backends. You also set log rotation, hardened version disclosure, and learned a reproducible way to test and reload changes. From here, add a CI-driven deploy, integrate metrics with mod_status behind access control, and run a short load test to resize MaxRequestWorkers and thread counts.

Quick status recap

# Service and enabled state
systemctl status apache2 || systemctl status httpd
# Active vhosts
apachectl -S
# Certificate expiry
sudo certbot certificates

With this baseline, you can safely host a static site, a PHP application, or a proxied service on your Linux server. Next, script these steps for repeatable environments and consider an image snapshot in your provider so scaling out remains straightforward.

Was this helpful?

Thanks for your feedback!
Alex is the resident editor and oversees all of the guides published. His past work and experience include Colorlib, Stack Diary, Hostvix, and working with a number of editorial publications. He has been wrangling code and publishing his findings about it since the early 2000s.

Leave a comment

Your email address will not be published. Required fields are marked *