Installing Apache on Ubuntu or Debian provides a fast, standard way to host websites, reverse-proxy applications, or publish internal documentation from a well-supported web server.

On these distributions, the Apache2 package ships a systemd service named apache2 plus a configuration layout under /etc/apache2 that separates global settings, enabled modules, and enabled virtual hosts. Helper commands such as a2enmod, a2dismod, a2ensite, and a2enconf manage symlinks between /etc/apache2/*-available and /etc/apache2/*-enabled, while apache2ctl validates configuration and performs graceful restarts.

Configuration mistakes can prevent apache2 from starting, so syntax checks should run before any reload or restart. Exposing ports 80 and 443 may require firewall changes, and a default install is reachable locally before DNS and TLS are configured.

Steps to install and customize Apache on Ubuntu and Debian:

  1. Open a terminal with sudo privileges.
  2. Update the apt package index.
    $ sudo apt update
    
    WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
    
    Hit:1 http://ports.ubuntu.com/ubuntu-ports noble InRelease
    Get:2 http://ports.ubuntu.com/ubuntu-ports noble-updates InRelease [126 kB]
    Get:3 http://ports.ubuntu.com/ubuntu-ports noble-backports InRelease [126 kB]
    Get:4 http://ports.ubuntu.com/ubuntu-ports noble-security InRelease [126 kB]
    ##### snipped #####
    Reading package lists...
    Building dependency tree...
    Reading state information...
    10 packages can be upgraded. Run 'apt list --upgradable' to see them.
  3. Install the Apache2 package and its dependencies.
    $ sudo apt install --assume-yes apache2
    
    WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
    
    Reading package lists...
    Building dependency tree...
    Reading state information...
    apache2 is already the newest version (2.4.58-1ubuntu8.8).
    0 upgraded, 0 newly installed, 0 to remove and 10 not upgraded.
  4. Confirm the apache2 service is running.
    $ sudo systemctl status apache2
    ● apache2.service - The Apache HTTP Server
         Loaded: loaded (/usr/lib/systemd/system/apache2.service; enabled; preset: enabled)
         Active: active (running) since Sat 2026-01-10 12:20:22 +08; 38min ago
           Docs: https://httpd.apache.org/docs/2.4/
        Process: 10941 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SUCCESS)
       Main PID: 10944 (apache2)
          Tasks: 55 (limit: 4546)
         Memory: 28.9M (peak: 29.3M)
            CPU: 280ms
         CGroup: /system.slice/apache2.service
                 ├─10944 /usr/sbin/apache2 -k start
                 ├─10946 /usr/sbin/apache2 -k start
                 └─10947 /usr/sbin/apache2 -k start
    ##### snipped #####

    Use journalctl -u apache2 and /var/log/apache2/error.log for recent startup errors.

  5. Enable the Apache2 service to start automatically on boot.
    $ sudo systemctl enable apache2
    Synchronizing state of apache2.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
    Executing: /usr/lib/systemd/systemd-sysv-install enable apache2
  6. Enable the rewrite module when URL rewriting is required.
    $ sudo a2enmod rewrite
    Module rewrite already enabled

    Common add-ons include headers, ssl, proxy, and http2.

  7. Verify that the rewrite module is loaded.
    $ sudo apache2ctl -M | grep rewrite
     rewrite_module (shared)
  8. Display the default virtual host configuration to find DocumentRoot and logging directives.
    $ sudo sed -n '1,120p' /etc/apache2/sites-available/000-default.conf
    <VirtualHost *:80>
    	# The ServerName directive sets the request scheme, hostname and port that
    	# the server uses to identify itself. This is used when creating
    	# redirection URLs. In the context of virtual hosts, the ServerName
    	# specifies what hostname must appear in the request's Host: header to
    	# match this virtual host. For the default virtual host (this file) this
    	# value is not decisive as it is used as a last resort host regardless.
    	# However, you must set it for any further virtual host explicitly.
    	#ServerName www.example.com
    
    	ServerAdmin webmaster@localhost
    	DocumentRoot /var/www/html
    
    	# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
    	# error, crit, alert, emerg.
    	# It is also possible to configure the loglevel for particular
    	# modules, e.g.
    	#LogLevel info ssl:warn
    
    	ErrorLog ${APACHE_LOG_DIR}/error.log
    	CustomLog ${APACHE_LOG_DIR}/access.log combined
    
    	# For most configuration files from conf-available/, which are
    	# enabled or disabled at a global level, it is possible to
    	# include a line for only one particular virtual host. For example the
    	# following line enables the CGI configuration for this host only
    	# after it has been globally disabled with "a2disconf".
    	#Include conf-available/serve-cgi-bin.conf
    </VirtualHost>
  9. Edit the default virtual host file to customize per-site directives.
    $ sudo vi /etc/apache2/sites-available/000-default.conf

    Incorrect syntax in /etc/apache2 can prevent apache2 from reloading and cause downtime.

  10. Modify the global Apache configuration when server-wide settings are needed.
    $ sudo vi /etc/apache2/apache2.conf
  11. Test the configuration for errors before applying changes.
    $ sudo apache2ctl configtest
    Syntax OK

    Syntax OK indicates a valid configuration even when AH00558 is shown.

  12. Restart the Apache2 service to apply the changes.
    $ sudo systemctl restart apache2

    Use sudo systemctl reload apache2 for a graceful reload when a full restart is unnecessary.

  13. Allow incoming HTTP traffic through UFW when a firewall is enabled.
    $ sudo ufw allow http
    Rules updated
    Rules updated (v6)

    Debian commonly uses nftables/iptables instead of UFW.

  14. Allow incoming HTTPS traffic through UFW when a firewall is enabled.
    $ sudo ufw allow https
    Rules updated
    Rules updated (v6)
  15. Verify the default site responds locally over HTTP.
    $ curl -sI http://127.0.0.1/
    HTTP/1.1 200 OK
    Date: Sat, 10 Jan 2026 04:59:21 GMT
    Server: Apache/2.4.58 (Ubuntu)
    Last-Modified: Sat, 10 Jan 2026 04:10:01 GMT
    ETag: "29af-64800d0d6e15b"
    Accept-Ranges: bytes
    Content-Length: 10671
    Vary: Accept-Encoding
    Content-Type: text/html