How to Install Apache on Debian 13, 12 and 11

Last updated Monday, April 27, 2026 12:47 pm Joshua James 12 min read

A Debian web server starts quickly with Apache because the default repositories already include the Apache HTTP Server as the apache2 package. Debian does not use the Red Hat-style httpd package or httpd.service unit, so to install Apache on Debian 13 (trixie), Debian 12 (bookworm), or Debian 11 (bullseye), install apache2 and verify apache2.service before opening web traffic.

The base install serves Debian’s default Apache page immediately and includes helper commands such as apache2ctl. From there, the workflow moves into UFW firewall rules, name-based virtual hosts, Let’s Encrypt certificates, status checks, log review, updates, removal, and common startup or permission fixes.

Install Apache on Debian

Update Debian Packages Before Apache

Refresh the APT package index and apply available package updates before installing a network service. The -y flag confirms the upgrade prompt so the command can run without stopping for input.

sudo apt update && sudo apt upgrade -y

These commands use sudo because package installation and service management need root privileges. If your account is not ready for sudo yet, follow the Debian guide to add a user to sudoers on Debian.

Install Apache2 Package on Debian

Install the apache2 package from Debian’s default repositories. This package installs the web server, default site configuration, helper commands, and the systemd service unit.

sudo apt install apache2 -y

Debian does not use a separate httpd package or httpd.service unit for Apache. Use apache2 for package operations and apache2.service for service commands.

Check Apache Package Versions on Debian

Debian’s default APT sources currently provide Apache 2.4.66 across the supported releases. The full package revision differs by Debian release and security pocket, which is why package-manager output is more precise than a rounded upstream version number.

Debian ReleaseCodenameCurrent apache2 CandidatePackage Source
Debian 13trixie2.4.66-1~deb13u2trixie/main
Debian 12bookworm2.4.66-1~deb12u1bookworm/main
Debian 11bullseye2.4.66-1~deb11u1bullseye-security/main

Verify Apache Service on Debian

Use sudo apache2ctl -v for the version check. Debian places Apache helper commands under /usr/sbin, and that directory may not appear in a regular user’s shell PATH.

If you see /usr/sbin/apachectl in service output or older Apache references, it comes from the same Debian Apache package. Use apache2ctl in Debian commands because that name matches Debian’s package and service naming.

sudo apache2ctl -v

Debian 13 currently reports Apache 2.4.66 from the default package sources:

Server version: Apache/2.4.66 (Debian)
Server built:   2026-03-01T13:26:45

Debian 12 and Debian 11 also report Apache 2.4.66 at the moment, but the build date and package revision differ by release.

Check the systemd unit next. The --no-pager flag keeps the status output in the terminal instead of opening an interactive pager.

systemctl status apache2 --no-pager

A working install shows the service enabled and active:

● apache2.service - The Apache HTTP Server
     Loaded: loaded (/usr/lib/systemd/system/apache2.service; enabled; preset: enabled)
     Active: active (running) since [date] [time] UTC; 2min ago
       Docs: https://httpd.apache.org/docs/2.4/
    Process: 1234 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SUCCESS)
   Main PID: 1235 (apache2)
      Tasks: 55 (limit: 4915)
     Memory: 12.5M
        CPU: 150ms
     CGroup: /system.slice/apache2.service
             ├─1235 /usr/sbin/apache2 -k start
             ├─1236 /usr/sbin/apache2 -k start
             └─1237 /usr/sbin/apache2 -k start

Debian 12 and Debian 11 may show /lib/systemd/system/apache2.service instead of the newer /usr/lib/systemd/system/apache2.service path. The important checks are the same: the unit is loaded, enabled, and active.

If the service is inactive, start Apache and enable it for future boots in one step. The --now flag starts the service immediately after enabling it.

sudo systemctl enable apache2 --now

At this point, Apache is listening on port 80 and can serve the default Debian landing page or your own virtual hosts.

Configure UFW Firewall for Apache on Debian

Install UFW for Apache on Debian

UFW is optional on Debian, but it is a practical way to open only the ports Apache needs on a public server. Install UFW from the default repositories, or use the dedicated guide to install UFW on Debian if you want broader firewall setup and removal details.

sudo apt install ufw -y

The -y flag accepts the package transaction prompt automatically.

Enable UFW Without Blocking SSH on Debian

Allow SSH before enabling UFW if you manage the server remotely. Otherwise, the firewall can close your current session before the Apache rules are in place.

If you changed the SSH port, replace ssh with your actual port rule, such as sudo ufw allow 2222/tcp, before enabling the firewall.

sudo ufw allow ssh

Enable UFW after the SSH rule exists. The --force flag skips the interactive confirmation prompt.

sudo ufw --force enable

Expected output:

Firewall is active and enabled on system startup

By default, UFW denies incoming connections and allows outgoing connections. Apache still needs explicit HTTP and HTTPS rules before visitors can reach the site.

Check Apache UFW Profiles on Debian

UFW application profiles give you named shortcuts for common ports. Filter the profile list to the web and SSH entries so the useful lines are easier to read.

sudo ufw app list | grep -E 'Apache|WWW|OpenSSH|SSH'

Relevant output on Debian 13 includes both Apache and legacy WWW profile names:

  Apache
  Apache Full
  Apache Secure
    OpenSSH
    SSH
    WWW
    WWW Cache
    WWW Full
    WWW Secure

Debian 12 and Debian 11 show the WWW profiles instead:

  OpenSSH
    SSH
    WWW
    WWW Cache
    WWW Full
    WWW Secure

Use Apache Full on Debian 13 or WWW Full on Debian 12 and Debian 11 when you want both HTTP port 80 and HTTPS port 443 open.

Allow Apache HTTP and HTTPS Through UFW

Choose the rule that matches how the site will be served. Most public sites should open both ports because HTTP requests usually redirect to HTTPS after certificates are configured.

To allow HTTP (Port 80) only:

# Debian 13:
sudo ufw allow 'Apache'
# Debian 12/11:
sudo ufw allow 'WWW'

For HTTPS (Port 443) only:

# Debian 13:
sudo ufw allow 'Apache Secure'
# Debian 12/11:
sudo ufw allow 'WWW Secure'

For both HTTP and HTTPS:

# Debian 13:
sudo ufw allow 'Apache Full'
# Debian 12/11:
sudo ufw allow 'WWW Full'

Verifying UFW Firewall Rules

Check the active firewall rules after adding the web profile:

sudo ufw status

Debian 13 shows Apache Full when both web ports are open:

Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
Apache Full                ALLOW       Anywhere
22/tcp (v6)                ALLOW       Anywhere (v6)
Apache Full (v6)           ALLOW       Anywhere (v6)

Debian 12 and Debian 11 show WWW Full for the same HTTP and HTTPS access:

Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
WWW Full                   ALLOW       Anywhere
22/tcp (v6)                ALLOW       Anywhere (v6)
WWW Full (v6)              ALLOW       Anywhere (v6)

Both profile names allow visitors to reach Apache over HTTP and HTTPS.

Test the Apache Default Page

After configuring UFW, verify that a browser can reach the Apache default page. Use your server’s IP address first:

http://your_server_ip

For local installations or testing on the same machine where Apache is installed, use localhost, which points to 127.0.0.1:

http://localhost

A working install shows the Debian Apache2 default landing page.

Create Virtual Hosts for Apache HTTP

Name-based virtual hosts let one Apache instance serve separate domains from separate document roots. The example below uses example.com; replace that domain everywhere with your real hostname.

Create and Set Permissions for Apache Directories

Apache serves /var/www/html by default. Keep that directory as the fallback site, then create a separate document root for example.com.

Create a document root for the example domain:

sudo mkdir -p /var/www/example.com/html

Assign ownership to your current user with the $USER environment variable. The shell expands $USER before sudo runs, so the directory becomes editable by your account while Apache can still read it.

sudo chown -R "$USER":"$USER" /var/www/example.com

Setting ownership to your user account makes static-site editing easier. For production applications where Apache, PHP, or another service writes files, use the ownership model required by that application instead of applying one rule to every site.

Verify the correct permissions are set for the web root:

ls -l /var/www/

The output should show the new site directory and the default web root:

drwxr-xr-x 3 username username 4096 [date] example.com
drwxr-xr-x 2 root     root     4096 [date] html

If the directory is not readable and searchable by Apache, correct the directory permissions:

sudo chmod 755 /var/www/example.com /var/www/example.com/html

Create a sample index.html page with nano:

nano /var/www/example.com/html/index.html

Paste the following HTML content:

<html>
    <head>
        <title>Welcome to example.com</title>
    </head>
    <body>
        <h1>Success! The example.com virtual host is working.</h1>
    </body>
</html>

After writing, save the file (CTRL+O), then exit (CTRL+X).

Create a Virtual Host for Apache HTTP

Create a virtual host configuration file under /etc/apache2/sites-available/:

sudo nano /etc/apache2/sites-available/example.com.conf

Paste this HTTP-only virtual host, replacing the domain names and document root if your site uses different values:

<VirtualHost *:80>
    ServerAdmin webmaster@example.com
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/html
    ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
    CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>

The ServerName directive handles the primary host, ServerAlias covers the www name, and DocumentRoot points Apache at the directory created earlier.

After setting the configuration, save the file (CTRL+O) and exit (CTRL+X).

Activate the Apache HTTP Virtual Host

Enable the new virtual host with Apache’s helper commands. a2ensite and a2dissite manage the symbolic links under /etc/apache2/sites-enabled/ for you.

Disable the default site if this server should answer with your new virtual host instead:

sudo a2dissite 000-default.conf

Enable the new virtual host:

sudo a2ensite example.com.conf

Test your configuration for syntax errors before reloading Apache:

sudo apache2ctl configtest

A successful test ends with:

Syntax OK

You may also see an AH00558 fully qualified domain name warning before Syntax OK. That warning does not mean the virtual host syntax failed; the troubleshooting section below covers the fix.

If there are errors, Apache shows the exact file and line number:

AH00526: Syntax error on line 3 of /etc/apache2/sites-enabled/example.com.conf:
Invalid command 'DocuemntRoot', perhaps misspelled

Reload Apache to activate the new virtual host without dropping established connections:

sudo systemctl reload apache2

Apache should now serve the custom landing page when requests match example.com or www.example.com. If DNS is not ready yet, test locally with a temporary hosts-file entry or a request that sends the correct Host header.

Create a Let’s Encrypt SSL Certificate for Apache

Use Let’s Encrypt after the HTTP virtual host works and your domain points at the server. Certbot can read the Apache virtual host, request the certificate, and add the HTTPS redirect automatically. For renewal troubleshooting and advanced options, use the dedicated guide to secure Apache with Let’s Encrypt on Debian.

Install the Apache Certbot Plugin

Install the Apache plugin for Certbot from Debian’s default repositories:

sudo apt install python3-certbot-apache -y

Generate the SSL Certificate

Request a certificate for both the apex domain and the www hostname. Replace the email address and domains before running the command.

sudo certbot --apache --agree-tos --redirect --email you@example.com -d example.com -d www.example.com

The main options do the following:

  • --apache: Uses Certbot’s Apache plugin to find and update the matching virtual host.
  • --redirect: Adds an HTTP-to-HTTPS redirect after the certificate is issued.
  • -d: Adds each domain name that should appear on the certificate.

Add stronger options such as HSTS only after you confirm the HTTPS site is correct. HSTS tells browsers to keep using HTTPS for the domain, so a mistaken rollout can linger for visitors.

Validating SSL Configuration

After Certbot succeeds, the site should load over https://example.com and https://www.example.com. Requests to the old HTTP URL should redirect to HTTPS because the command used --redirect.

Open the site in a browser and check that the address bar shows HTTPS without certificate warnings. If the browser still shows HTTP, recheck DNS, the virtual host names, and the Certbot output.

Automating SSL Certificate Renewal

Let’s Encrypt certificates last 90 days. Debian’s Certbot package installs automated renewal through a systemd timer or cron job, but a dry run confirms that renewal can still complete with your current Apache configuration.

Run a renewal dry run with:

sudo certbot renew --dry-run

A successful dry run means Certbot can renew the certificate path without waiting for the real expiry window.

Monitoring Your Apache HTTP Service

Apache exposes enough status and log data to catch most configuration, traffic, and permission problems before they become outages.

Configure Apache mod_status

Debian enables the Apache status module during a default install. If it was disabled later, enable it again with:

sudo a2enmod status

The status module provides a local status page with live worker and request information. Edit its configuration before exposing it beyond localhost:

sudo nano /etc/apache2/mods-enabled/status.conf

You should see a configuration block like this:

<Location /server-status>
    SetHandler server-status
    Require local
</Location>

By default, Require local restricts access to local connections such as 127.0.0.1. To allow a trusted address, use Require ip followed by that IP address. Avoid Require all granted on production servers because the status page exposes sensitive runtime information.

Checking Apache Server Status

Access the status page from an allowed address:

http://your_server_ip/server-status

The status page shows details such as:

  • The number of workers serving requests
  • The number of idle workers
  • The current CPU usage
  • Details about the requests being processed

Configuring Apache Log Files

Apache writes logs under /var/log/apache2/. Debian keeps that directory readable by root and the adm group, so most one-off checks should use sudo.

  • Access log (/var/log/apache2/access.log): Records each request served by the web server.
  • Error log (/var/log/apache2/error.log): Records startup, configuration, permission, and request-processing errors.

Use these logs first when a virtual host returns the wrong page, a permission problem causes 403 errors, or Apache fails to reload.

Tips for Managing Your Apache HTTP Web Server

The day-to-day commands below cover log review and service control after Apache is installed.

Understanding Apache Server Logs

Apache logs are the fastest way to diagnose broken virtual hosts, unexpected status codes, and suspicious traffic. The examples below use common command-line tools; the generic tail command guide and grep command guide cover the flags in more depth.

Review Recent Log Entries

Use tail with sudo to check recent log entries:

sudo tail /var/log/apache2/access.log
sudo tail /var/log/apache2/error.log

Monitor Logs in Real-Time

Watch logs in real-time while troubleshooting:

sudo tail -f /var/log/apache2/error.log

Search for Specific Errors

Use grep to find specific error codes or patterns:

sudo grep "404" /var/log/apache2/error.log
sudo grep -c "500" /var/log/apache2/error.log

Identify Unique IP Addresses Accessing the Server

Use awk, sort, and uniq to list unique IP addresses from the access log:

sudo awk '{print $1}' /var/log/apache2/access.log | sort | uniq

To identify the top 10 IP addresses by request volume:

sudo awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -10

These checks are intentionally simple. For heavier traffic analysis, use a log analyzer or ship Apache logs into your monitoring stack.

Manage Apache HTTP Service Commands

Use these systemctl commands for routine Apache service control:

Stopping the Apache Web Server:

sudo systemctl stop apache2

Starting the Apache Web Server:

sudo systemctl start apache2

Restarting the Apache Web Server:

Use restart for major changes (module installation, MPM changes):

sudo systemctl restart apache2

Reloading the Apache Web Server:

Use reload for configuration changes (virtual hosts, .htaccess) without dropping active connections:

sudo systemctl reload apache2

Disabling Apache on Server Boot:

sudo systemctl disable apache2

Enabling Apache on Server Boot:

This is typically enabled by default upon installation:

sudo systemctl enable apache2

Update and Remove Apache

Regular maintenance keeps Apache on the current Debian security build. The commands below update only the Apache package, then show a removal path that separates package removal from site-data cleanup.

Update Apache HTTP

Refresh APT metadata before checking for Apache updates:

sudo apt update

To update only Apache without upgrading all packages on your system, use the --only-upgrade flag:

sudo apt install --only-upgrade apache2

The --only-upgrade flag upgrades apache2 only if it is already installed. Back up important virtual host files before upgrading production servers, then verify the package version and service state:

sudo apache2ctl -v
systemctl status apache2 --no-pager

Remove Apache HTTP

If you enabled UFW rules only for Apache, remove those web rules before uninstalling the package. Keep UFW itself installed if it protects SSH or other services. Replace the profile name if you opened only HTTP or only HTTPS earlier.

# Debian 13:
sudo ufw delete allow 'Apache Full'
# Debian 12/11:
sudo ufw delete allow 'WWW Full'

Stop Apache first if you are removing it from an active server:

sudo systemctl stop apache2

Remove the main Apache package with APT. Review APT’s package list before confirming, especially on desktop systems where Apache can be tied to sharing helpers or other local services.

sudo apt remove apache2

Review optional dependency cleanup separately. On reused desktop or VM systems, sudo apt autoremove can also target unrelated old kernels or helpers, so read the package list before confirming.

sudo apt autoremove

To remove Apache package configuration files as well, purge the Apache package split:

sudo apt purge apache2 apache2-bin apache2-data apache2-utils

Verify that the Apache package split is no longer installed:

dpkg -l apache2 apache2-bin apache2-data apache2-utils 2>/dev/null | grep '^ii'

A removed package set returns no matching installed-state lines. If you purged the packages, dpkg -l may show no package rows or uninstalled states instead.

The next cleanup command permanently deletes the example virtual host, its web files, and optional servername.conf file created earlier. Back up any real website files before removing a production document root.

If you followed the virtual host example and no longer need those files, remove them explicitly:

sudo rm -rf /var/www/example.com
sudo rm -f /etc/apache2/sites-available/example.com.conf
sudo rm -f /etc/apache2/sites-enabled/example.com.conf
sudo rm -f /etc/apache2/conf-available/servername.conf
sudo rm -f /etc/apache2/conf-enabled/servername.conf

Confirm the example files are gone:

sudo find /var/www /etc/apache2 \( -path '*example.com*' -o -name 'servername.conf' \) -print 2>/dev/null

No output means the example document root, site files, and optional ServerName configuration were removed.

Troubleshooting Common Apache Issues

When Apache does not behave as expected, start with the exact service, log, or configuration error instead of changing multiple settings at once.

Apache Fails to Start: Port Already in Use

If Apache fails to start, check the service status for the first error message:

sudo systemctl status apache2

If the status output mentions Address already in use or could not bind to address, another service is using port 80 or 443. Find the listener with ss:

sudo ss -ltnp 'sport = :80'

The output shows which program is using the port:

State  Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0      511          0.0.0.0:80        0.0.0.0:*     users:(("nginx",pid=12345,fd=6))

In this example, Nginx is using port 80. Stop the conflicting service before starting Apache:

sudo systemctl stop nginx
sudo systemctl start apache2

403 Forbidden: Permission Denied Errors

When you see 403 Forbidden in a browser, Apache may not be able to read the website files or the virtual host may deny access. Check the error log first:

sudo tail -20 /var/log/apache2/error.log

Look for lines containing “Permission denied” or “AH01630”. A typical permission error looks like:

[timestamp] [authz_core:error] [pid 1234] [client 192.168.1.100:54321] AH01630: client denied by server configuration: /var/www/example.com/html/

Verify the web directory ownership and permissions:

ls -la /var/www/example.com/html/

The output should show files readable by Apache. The owner may be your user account for static files or www-data for applications that need Apache or PHP to write content:

drwxr-xr-x 2 username username 4096 [date] .
drwxr-xr-x 4 root     root     4096 [date] ..
-rw-r--r-- 1 username username  174 [date] index.html

If permissions are incorrect for the static example site, fix them with:

sudo chown -R "$USER":"$USER" /var/www/example.com
sudo chmod 755 /var/www/example.com /var/www/example.com/html
sudo chmod 644 /var/www/example.com/html/index.html

Configuration Syntax Errors

Test Apache syntax before reloading the service. A syntax error can prevent Apache from starting or reloading cleanly:

sudo apache2ctl configtest

A successful test shows:

Syntax OK

A configuration error shows the exact file and line number:

AH00526: Syntax error on line 8 of /etc/apache2/sites-enabled/example.com.conf:
Invalid command 'DocuemntRoot', perhaps misspelled or defined by a module not included in the server configuration

This indicates line 8 has a typo: “DocuemntRoot” should be “DocumentRoot”. Open the file and correct the error:

sudo nano /etc/apache2/sites-available/example.com.conf

Apache Shows AH00558 ServerName Warning

A fresh Apache install often prints this warning during apache2ctl configtest:

AH00558: apache2: Could not reliably determine the server's fully qualified domain name
Syntax OK

The final Syntax OK line means the configuration parsed successfully. To suppress the warning, set a global ServerName value and enable that small configuration file:

printf '%s\n' 'ServerName example.com' | sudo tee /etc/apache2/conf-available/servername.conf > /dev/null
sudo a2enconf servername
sudo apache2ctl configtest
sudo systemctl reload apache2

Virtual Host Shows Default Page Instead of Your Site

When a browser shows the Apache default page instead of your custom site, the virtual host may not be enabled or the requested host name may not match the configured ServerName. Verify enabled sites:

ls -la /etc/apache2/sites-enabled/

You should see your configuration file as a symbolic link:

lrwxrwxrwx 1 root root 39 [date] example.com.conf -> ../sites-available/example.com.conf

If missing, enable it and reload Apache:

sudo a2ensite example.com.conf
sudo systemctl reload apache2

Also verify that ServerName and ServerAlias match how you access the site. If you type example.com in your browser but the configuration only names www.example.com, Apache may route the request to the wrong virtual host.

Check Apache Error Logs for Other Issues

For issues that do not match the cases above, read the latest Apache errors:

sudo tail -50 /var/log/apache2/error.log

To watch errors appear in real-time while you test in another terminal or browser:

sudo tail -f /var/log/apache2/error.log

Press Ctrl+C to stop watching the log.

Additional Security Considerations

SSL certificates and firewall rules are only the baseline for a public web server. Add a web application firewall with ModSecurity for Apache on Debian when you host applications that need request filtering, and use Fail2ban on Debian to ban repeated hostile requests from Apache or SSH logs.

For remote administration, enable SSH on Debian with key-based authentication before relying on the server from another machine. For dynamic sites, install PHP on Debian, add a database with the MariaDB installation guide for Debian, or deploy a full site with WordPress with Apache on Debian.

Conclusion

Apache is running on Debian with a verified service unit, firewall rules, virtual host structure, and an HTTPS path through Let’s Encrypt. From here, add application support with PHP and MariaDB, or compare the stack with Install Nginx on Debian if an event-driven web server fits your workload better.

Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffee Buy me a coffee

Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<blockquote>quote</blockquote> quote block

Got a Question or Feedback?

We read and reply to every comment - let us know how we can help or improve this guide.

Let us know you are human: