Configure Apache on the Ubuntu operating system

The Ubuntu operating system uses a different Apache layout than those used in non-Debian-based operating systems. The differences are small, but helpful in configuring and deploying websites. This article explains some of the site and module configuration settings for Apache on the Ubuntu operating system, and describes how to enable and disable sites and modules as needed.

Apache Installation

Use aptitude to install Apache on your server running the Ubuntu operating system.

The advantage of using aptitude is that you will get any security updates from the Ubuntu operating system (if and when distributed), and dependencies are automatically installed.

Installation Of Apache2:

### Update your system package repository and install Apache:

$ sudo apt update
Hit:1 https://mirror.rackspace.com/ubuntu noble InRelease
Hit:2 https://mirror.rackspace.com/ubuntu noble-updates InRelease
Hit:3 https://mirror.rackspace.com/ubuntu noble-backports InRelease
Hit:4 https://mirror.rackspace.com/ubuntu noble-security InRelease
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
154 packages can be upgraded. Run 'apt list --upgradable' to see them.

$ sudo apt install apache2
Reading package lists... Done
Building dependency tree... Done  
......  
Created symlink /etc/systemd/system/multi-user.target.wants/apache2.service → /usr/lib/systemd/system/apache2.service.
Created symlink /etc/systemd/system/multi-user.target.wants/apache-htcacheclean.service → /usr/lib/systemd/system/apache-htcacheclean.service.
Processing triggers for ufw (0.36.2-6) ...
Processing triggers for man-db (2.12.0-4build2) ...
Processing triggers for libc-bin (2.39-0ubuntu8.4) ...

Verify the installation and check Apache version:


$ sudo apache2 -v
Server version: Apache/2.4.58 (Ubuntu)
Server built:   2025-08-11T11:10:09

Starting and Enabling Apache:

$ sudo apache2ctl -t
$ sudo systemctl enable --now 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

$ sudo systemctl is-active apache2.service
active

$ sudo systemctl status apache2.service   ## (You should see active (running) in the output.)

Configuring the Firewall:

If you are using the Firewall at the OS end, the details would be helpful for you.

Allow HTTP and HTTPS traffic through Ubuntu's UFW firewall:

$ sudo ufw status numbered
$ sudo ufw allow 'Apache Full'
$ sudo ufw reload
Firewall reloaded
$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
Apache Full                ALLOW       Anywhere
Apache Full (v6)           ALLOW       Anywhere (v6)

Verifying the Installation

Open your web browser and navigate to your server's IP address:

http://your_server_ip

Or you can use CURL 

$ curl -IkL http://your_server_ip (The output will look like below)
HTTP/1.1 200 OK
Date: Tue, 13 Jan 2026 13:24:16 GMT
Server: Apache/2.4.58 (Ubuntu)
Last-Modified: Tue, 13 Jan 2026 13:19:33 GMT
ETag: "29af-64844d7b1e0ea"
Accept-Ranges: bytes
Content-Length: 10671
Vary: Accept-Encoding
Content-Type: text/html

You should see the default Apache welcome page indicating successful installation.

Understanding Apache Directory Structure

Apache on Ubuntu follows a specific organizational pattern designed for easy management:

Main Configuration:

/etc/apache2/apache2.conf       - Primary server configuration file
/etc/apache2/ports.conf         - Port listening configuration
/etc/apache2/envvars            - Environment variables for Apache

Site Configurations:

/etc/apache2/sites-available/   - Available site configuration files
/etc/apache2/sites-enabled/     - Active site configurations (symlinks)

Module Management

/etc/apache2/mods-available/    - Available Apache modules
/etc/apache2/mods-enabled/      - Enabled modules (symlinks)

Configuration Snippets:

/etc/apache2/conf-available/    - Additional configuration snippets
/etc/apache2/conf-enabled/      - Enabled configuration snippets

Web Content:

By default, Apache serves web content from /var/www/html, but you can change the document root to any directory that meets your requirements. Just make sure the new location has the correct permissions and ownership (typically www-data or apache), and update the DocumentRoot directive in your virtual host configuration

/var/www/html/                  - Default document root
/var/www/                       - Recommended location for hosting multiple sites

Log Files:

/var/log/apache2/access.log     - Access logs
/var/log/apache2/error.log      - Error logs

View the configuration file

To view the contents of the Apache configuration file, run the following commands:

$ cd /etc/apache2
$ ls

The output should look as follows:

$ ls
apache2.conf  conf-available  conf-enabled  envvars  magic  mods-available  mods-enabled  ports.conf  sites-available  sites-enabled

Configuration Directory/Files

This section explains the configuration settings in the following folders:

  • sites-available
  • sites-enabled
  • mods-available
  • mods-enabled

sites-available

The sites-available folder contains the configurations for each site that you
want to serve. These are known as virtual hosts, or vhosts.

If you look inside this folder, you should see that there is one (default) site available:

$ ls sites-available/
...
000-default.conf  default-ssl.conf

The Apache installation has a 000-default and a default-ssl vhost available. When you
navigate to the IP address of your cloud server and get the "It works!" message, the
default file is telling Apache what to do and where the files were located.

Note: A file in the sites-available folder is not automatically active. It is simply available
for serving if you enable it.

sites-enabled

The sites-enabled folder contains symlinks to the sites that you are actually serving.

For example, you could have two vhosts configured and ready for use in the sites-available
folder, but only the vhost that has a symlink from the site-enabled folder is being served.

If you look inside this folder, you see which site is currently enabled:

$ ls -l sites-enabled/
...
lrwxrwxrwx 1 root root 35 Jan  7 10:25 000-default.conf -> ../sites-available/000-default.conf

This result shows that the default site is enabled. The symlink named 000-default
links to the 000-default file in the sites-available folder.

Note: A domain can point to your cloud
server's IP address but have no site configuration file. In such a case, the first enabled
site (alphabetically) is displayed. For example, the configuration for 000-default would be used.

mods-available

The mods-available folder contains the modules that are available to be loaded.

Look inside the folder by running the following command:

$ ls mods-available

A list of modules is available from the base installation, but they are not all enabled; they are just available for use. Just as with the vhosts files, any modules that you want to use must be enabled.

mods-enabled

The mods-enabled folder contains symlinks to the modules that are enabled.

Use the following command to look inside the folder:

$ ls mods-enabled

This resulting list is much shorter than the list of available modules, and it includes enabled modules such as mpm_event.conf.

Enable and Disable sites and modules

You can use the commands in this section to enable and disable sites and modules.

a2ensite

The a2ensite command establishes a symlink to a site that is not already enabled.
Useful for enabling the website.

  • a2: Apache2 (what Ubuntu calls Apache)
  • en: enable
  • site: website virtual host

Enable the default site with the following command:

$ sudo a2ensite sitename
$ sudo a2ensite 000-default

You should see output similar to the following:

Enabling site 000-default.
To activate the new configuration, you need to run:
  systemctl reload apache2

Reload Apache with the following command to ensure that the site is enabled:

$ sudo apache2ctl -t
$ sudo systemctl reload apache2

If you visit your cloud server's IP address in a web browser, you will see that the default page is being served.

a2dissite

The a2dissite command deletes the symlink to a site that you have previously enabled.
Useful for disabling your website.

  • a2: Apache2 (what Ubuntu calls Apache)
  • dis: disable
  • site: website virtual host

For example, to disable the default site, run the following command:

$ sudo a2dissite sitename 
$ sudo a2dissite
Your choices are: 000-default
Which site(s) do you want to disable (wildcards ok)?

OR

$ sudo a2dissite 000-default

The symlink to the 000-default site in the sites-enabled folder is deleted. You should see output similar to the following:

$ sudo a2dissite 000-default
Site 000-default disabled.
To activate the new configuration, you need to run:
  systemctl reload apache2

Reload Apache with the following command to ensure that the site is fully disabled:

$ sudo apache2ctl -t
$ sudo systemctl reload apache2

When you now visit your cloud server's IP address in a web browser, you will get a 404 Not Found message instead of the "It Works!" page.

Note: The main vhosts file in the sites-available folder still exists. The a2dissite
Command just removed the symlink to it in the sites-enabled folder.

a2dismod

The a2dismod command disables any modules you have previously enabled.

  • a2: Apache2 (what Ubuntu calls Apache)
  • dis: disable
  • mod: Module

For example, disable the mpm_event module with the following command:

$ sudo a2dismod
Your choices are: access_compat alias auth_basic authn_core authn_file authz_core authz_host authz_user autoindex deflate dir env filter mime mpm_event negotiation reqtimeout setenvif status
Which module(s) do you want to disable (wildcards ok)?

You should see output similar to the following output:

$ sudo a2dismod mpm_event
Module mpm_event disabled.
To activate the new configuration, you need to run:
  systemctl restart apache2

If you look in the mods-enabled folder, you will see that the mpm_event.conf
and mpm_event.load symlinks have been deleted.

Note: Be sure to reload Apache after each module change for your cloud server's IP address to reflect any changes that you have made.

a2enmod

The a2enmod command enables any module that is in the mods-available folder.

  • a2: Apache2 (what Ubuntu calls Apache)
  • en: enable
  • mod: Module

For example, enable the mpm_event module by running the following command:

$ sudo a2enmod mpm_event

You should see output similar to the following output:

$ sudo a2enmod mpm_event
Considering conflict mpm_worker for mpm_event:
Considering conflict mpm_prefork for mpm_event:
Enabling module mpm_event.
To activate the new configuration, you need to run:
  systemctl restart apache2

$ sudo a2enmod ssl
Considering dependency mime for ssl:
Module mime already enabled
Considering dependency socache_shmcb for ssl:
Enabling module socache_shmcb.
Enabling module ssl.
See /usr/share/doc/apache2/README.Debian.gz on how to configure SSL and create self-signed certificates.
To activate the new configuration, you need to run:
  systemctl restart apache2

If you check the mods-enabled folder, you will see the mpm_event.conf and
mpm_event.load symlinks.

Note: Be sure to reload Apache after each module change for your cloud server's
IP address to reflect any changes that you have made.

Service Control:

$ sudo apache2ctl -t
$ sudo systemctl restart apache2   # Full restart
$ sudo systemctl reload apache2    # Reload configuration
$ sudo systemctl stop apache2      # Stop service

Configuring Virtual Hosts

Virtual hosts enable hosting multiple websites on a single server, each with its own domain and configuration.

Creating Directory Structure

Create directories for your first website:

$ sudo mkdir -p /var/www/example.com/public_html
$ sudo mkdir -p /var/www/example.com/logs

Create directories for a second website:

$ sudo mkdir -p /var/www/testdomain.net/public_html
$ sudo mkdir -p /var/www/testdomain.net/logs

Set appropriate ownership:

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

Configure proper permissions:

$ sudo chmod -R 755 /var/www

Creating Sample Content

Create a test page for the first site:

$ sudo tee /var/www/example.com/public_html/index.html > /dev/null <<'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Welcome to Example.com</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
            background-color: #f4f4f4;
        }
        h1 { color: #333; }
    </style>
</head>
<body>
    <h1>Success! Example.com is Working</h1>
    <p>This Apache virtual host has been configured successfully.</p>
    <p>Server Time: <?php echo date('Y-m-d H:i:s'); ?></p>
</body>
</html>
EOF

Create a test page for the second site:

$ sudo tee > /var/www/testdomain.net/public_html/index.html > /dev/null << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Welcome to TestDomain.net</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
            background-color: #e8f4f8;
        }
        h1 { color: #0066cc; }
    </style>
</head>
<body>
    <h1>Success! TestDomain.net is Working</h1>
    <p>Your second Apache virtual host is configured correctly.</p>
</body>
</html>
EOF

Creating Virtual Host Configuration Files:

$ sudo vim /etc/apache2/sites-available/example.com.conf 
$ sudo vim /etc/apache2/sites-available/testdomain.net.conf
<VirtualHost *:80>
    ServerAdmin [email protected]
    ServerName example.com
    ServerAlias www.example.com
    
    DocumentRoot /var/www/example.com/public_html
    
    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
    
    ErrorLog /var/www/example.com/logs/error.log
    CustomLog /var/www/example.com/logs/access.log combined
    
    # Security headers
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-XSS-Protection "1; mode=block"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    
    # Disable server signature
    ServerSignature Off
</VirtualHost>

Similarly, you can do this for the second website as well.

Understanding Virtual Host Directives

ServerAdmin          : Email address displayed in error pages and used for administrative contact.
ServerName           : Primary domain name for this virtual host.
ServerAlias          : Additional domain names that serve the same content (e.g., www subdomain).
DocumentRoot         : Directory containing the website files Apache will serve.
Options              :
Indexes              : Disables directory listing (security best practice)
FollowSymLinks       : Allows following symbolic links

AllowOverride All    : Permits .htaccess files to override configuration.
Require all granted  : Allows access from all IP addresses.
ErrorLog / CustomLog : Specifies custom log file locations for this virtual host.

Enabling Virtual Hosts:

Refer previous details to enable the websites.

$ sudo a2enmod headers

Enable your newly created sites:

$ sudo a2ensite example.com.conf
$ sudo a2ensite testdomain.net.conf

Disable the default site (optional but recommended):

$ sudo a2dissite 000-default.conf

Test your configuration for syntax errors:

$ sudo apache2ctl configtest

If the test returns Syntax OK, reload Apache:

$ sudo apache2ctl -t
$ sudo systemctl reload apache2
$ sudo systemctl status apache2

Testing with Local Hosts File (Development)

Refer Modify your Hosts File

Security Configuration and Hardening

Securing Apache is critical to protecting your websites and server from vulnerabilities and attacks.

Hiding Apache Version Information:

$ sudo vim /etc/apache2/conf-enabled/security.conf
## Set these directives:
ServerTokens Prod
ServerSignature Off

### Uses
ServerTokens Prod     : Only sends "Apache" in headers (not version details).
ServerSignature Off   : Removes version information from error pages.

Disabling Unnecessary Modules

List enabled modules:

$ sudo apache2ctl -M

Disable modules you don't need (example):

$ sudo a2dismod status
$ sudo a2dismod autoindex
### Common modules to review:

autoindex    - Directory listings (usually disabled)
status       - Server status information (disable on production)
userdir      - User public_html directories (disable if unused)

Implementing Security Headers

Enable the headers module if not already enabled:

$ sudo a2enmod headers
$ sudo vim /etc/apache2/conf-available/security-headers.conf

Add comprehensive security headers:

<IfModule mod_headers.c>
    # Prevent clickjacking
    Header always set X-Frame-Options "SAMEORIGIN"
    
    # Prevent MIME type sniffing
    Header always set X-Content-Type-Options "nosniff"
    
    # Enable XSS filtering
    Header always set X-XSS-Protection "1; mode=block"
    
    # Control referrer information
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    
    # Content Security Policy (adjust based on your needs)
    Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"
    
    # Remove potentially sensitive information
    Header always unset X-Powered-By
    Header always unset X-AspNet-Version
</IfModule>

Enable the configuration:

$ sudo a2enconf security-headers
Enabling conf security-headers.
To activate the new configuration, you need to run:
  systemctl reload apache2

$ sudo apache2ctl -t
$ sudo systemctl reload apache2

Restricting Access by IP Address:

To restrict access to specific IP addresses for a directory or site:

<Directory /var/www/example.com/admin>
    Require ip 192.168.1.100
    Require ip 203.0.113.0/24
</Directory>

Preventing Directory Traversal

Add this to your virtual host file:

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

Protecting Sensitive Files

Add this to your virtual host or .htaccess file:

<FilesMatch "\.(htaccess|htpasswd|ini|log|sh|sql|conf)$">
    Require all denied
</FilesMatch>

# Protect hidden files
<FilesMatch "^\.">
    Require all denied
</FilesMatch>

Enabling ModSecurity (Optional but Recommended):

ModSecurity is a Web Application Firewall (WAF):

$ sudo apt install libapache2-mod-security2 -y
$ sudo mv /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
$ sudo nano /etc/modsecurity/modsecurity.conf

Change SecRuleEngine DetectionOnly to:

SecRuleEngine On
$ sudo apache2ctl -t
$ sudo systemctl restart apache2

SSL/TLS Certificate Implementation

HTTPS is now essential for all websites. You can purchase it from a verified vendor or can use Let's Encrypt for free SSL certificates. Use install-and-use-the-lets-encrypt-certbot-utility-on-centos7-with-apache

Understanding the SSL Virtual Host Configuration

<VirtualHost *:443>
    ServerAdmin [email protected]
    ServerName example.com
    ServerAlias www.example.com
    
    DocumentRoot /var/www/example.com/public_html
    
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    
    # Modern SSL configuration
    SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
    SSLHonorCipherOrder off
    SSLSessionTickets off
    
    # HSTS (uncomment after testing)
    # Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
    
    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
    
    ErrorLog /var/www/example.com/logs/error.log
    CustomLog /var/www/example.com/logs/access.log combined
</VirtualHost>

Enabling HTTP Strict Transport Security (HSTS)

$ sudo vim /etc/apache2/sites-available/example.com-le-ssl.conf

Uncomment or add:

Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

Reload Apache:

$ sudo apache2ctl -t
$ sudo systemctl reload apache2

Additional Content:

Optimizing Apache MPM Configuration

Check current MPM module:

$ sudo apache2ctl -V | grep MPM

For most modern servers, use the event MPM

$ sudo vim /etc/apache2/mods-available/mpm_event.conf

Optimize based on your server resources (example for 2GB RAM server):

<IfModule mpm_event_module>
    StartServers             2
    MinSpareThreads          25
    MaxSpareThreads          75
    ThreadLimit              64
    ThreadsPerChild          25
    MaxRequestWorkers        150
    MaxConnectionsPerChild   3000
</IfModule>

Restart Apache after changes:

$ sudo apache2ctl -t
$ sudo systemctl restart apache2

Enabling HTTP/2

Enable the http2 module:

$ sudo a2enmod http2

Add to your SSL virtual hosts:

<VirtualHost *:443>
    Protocols h2 http/1.1
    # ... rest of configuration
</VirtualHost>

Reload Apache:

$ sudo apache2ctl -t
$ sudo systemctl reload apache2

Optimize Apache to handle traffic efficiently and reduce resource consumption.

_Note:- Example only — review and test before applying in production.

Enabling Compression (mod_deflate)

$ sudo a2enmod deflate

Create a compression configuration:

$ sudo vim /etc/apache2/conf-available/compression.conf
<IfModule mod_deflate.c>
    # Compress HTML, CSS, JavaScript, Text, XML and fonts
    AddOutputFilterByType DEFLATE application/javascript
    AddOutputFilterByType DEFLATE application/rss+xml
    AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
    AddOutputFilterByType DEFLATE application/x-font
    AddOutputFilterByType DEFLATE application/x-font-opentype
    AddOutputFilterByType DEFLATE application/x-font-otf
    AddOutputFilterByType DEFLATE application/x-font-truetype
    AddOutputFilterByType DEFLATE application/x-font-ttf
    AddOutputFilterByType DEFLATE application/x-javascript
    AddOutputFilterByType DEFLATE application/xhtml+xml
    AddOutputFilterByType DEFLATE application/xml
    AddOutputFilterByType DEFLATE font/opentype
    AddOutputFilterByType DEFLATE font/otf
    AddOutputFilterByType DEFLATE font/ttf
    AddOutputFilterByType DEFLATE image/svg+xml
    AddOutputFilterByType DEFLATE image/x-icon
    AddOutputFilterByType DEFLATE text/css
    AddOutputFilterByType DEFLATE text/html
    AddOutputFilterByType DEFLATE text/javascript
    AddOutputFilterByType DEFLATE text/plain
    AddOutputFilterByType DEFLATE text/xml
    
    # Remove browser bugs (only needed for really old browsers)
    BrowserMatch ^Mozilla/4 gzip-only-text/html
    BrowserMatch ^Mozilla/4\.0[678] no-gzip
    BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
    Header append Vary User-Agent
</IfModule>

Enable and reload:

$ sudo a2enconf compression
$ sudo apache2ctl -t
$ sudo systemctl reload apache2

Enabling Browser Caching (mod_expires)

If your site serves static assets (images, CSS, JS, fonts), enabling browser caching significantly improves performance.
OR you can use a CDN instead.

_Note: - Example only — review and test before applying in production.

Enable the expires module:

$ sudo a2enmod expires

Create caching configuration:

$ sudo vim /etc/apache2/conf-available/caching.conf
<IfModule mod_expires.c>
    ExpiresActive On
    
    # Images
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/gif "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
    ExpiresByType image/x-icon "access plus 1 year"
    
    # Video
    ExpiresByType video/mp4 "access plus 1 year"
    ExpiresByType video/mpeg "access plus 1 year"
    
    # CSS and JavaScript
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
    ExpiresByType application/x-javascript "access plus 1 month"
    
    # Fonts
    ExpiresByType application/x-font-ttf "access plus 1 year"
    ExpiresByType font/opentype "access plus 1 year"
    ExpiresByType application/x-font-woff "access plus 1 year"
    ExpiresByType application/font-woff "access plus 1 year"
    ExpiresByType application/font-woff2 "access plus 1 year"
    
    # Others
    ExpiresByType application/pdf "access plus 1 month"
    ExpiresByType text/x-javascript "access plus 1 month"
    ExpiresByType application/x-shockwave-flash "access plus 1 month"
    
    # HTML and HTM default
    ExpiresDefault "access plus 1 week"
</IfModule>

Enable and reload:

$ sudo a2enconf caching
$ sudo apache2ctl -t
$ sudo systemctl reload apache2

_Note: - Proper log management prevents disk space issues and maintains system performance. Always review your logrotation for access and error logs or any custom log for Apache.

Troubleshooting some common Issues:

$ sudo apache2ctl configtest
Syntax OK

Common errors and solutions:
AH00526: Syntax error                     - Check for typos in directive names
AH00548: NameVirtualHost has no effect    - Remove NameVirtualHost directive (deprecated in Apache 2.4+)
Could not reliably determine the server's fully qualified domain name - Add ServerName directive to /etc/apache2/apache2.conf:

ServerName localhost

Confirm Port listening (80/443).

If another service is running or any other instance is running on ports, you are unable to start Apache.

$ sudo lsof -i :80
COMMAND    PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx   120857     root    5u  IPv4 937022      0t0  TCP *:http (LISTEN)
nginx   120857     root    6u  IPv6 937023      0t0  TCP *:http (LISTEN)
nginx   120858 www-data    5u  IPv4 937022      0t0  TCP *:http (LISTEN)
nginx   120858 www-data    6u  IPv6 937023      0t0  TCP *:http (LISTEN)
nginx   120859 www-data    5u  IPv4 937022      0t0  TCP *:http (LISTEN)
nginx   120859 www-data    6u  IPv6 937023      0t0  TCP *:http (LISTEN)

Tail logs if something's off:

$ sudo journalctl -u apache2 -e --no-pager
Jan 07 10:25:33 testing-Dev systemd[1]: Starting apache2.service - The Apache HTTP Server...
Jan 07 10:25:33 testing-Dev apachectl[6132]: AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 2001:4802:7803:104:be76:4eff:fe20:206a. Set the 'ServerName' directive globally to suppress this message
...

$ tail -f /var/log/apache2/error.log
[Wed Jan 07 10:42:08.396584 2026] [mpm_event:notice] [pid 6133:tid 126500079703936] AH00493: SIGUSR1 received.  Doing graceful restart
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 2001:4802:7803:104:be76:4eff:fe20:206a. Set the 'ServerName' directive globally to suppress this message
...

Permission Denied Errors

  • Review file ownership.
  • Review directory permissions.
  • Review file permissions.

403 Forbidden Errors

Check directory configuration allows access:

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

Best Practices

  • Always test configurations before reloading Apache in production.the
  • Keep Apache and Ubuntu up to date with the latest security patches.
  • Use HTTPS everywhere - SSL certificates are free with Let's Encrypt.
  • Implement security headers to protect against common vulnerabilities.
  • Review if enabling compression and caching can increase performance.
  • Monitor logs regularly for errors and suspicious activity.
  • Set up log rotation to prevent disk space issues.
  • Backup configurations before making changes.
  • Use virtual hosts to organize multiple websites cleanly.
  • Disable unused modules to reduce attack surface and resource usage.
  • Restrict access to sensitive directories and files.
  • Document your changes for future reference.