OneLinersCommand workbench
Guides
Services & Applications / Security

Install Apache on Ubuntu with a production baseline

Install Apache 2.4, replace the default page, constrain the listener, set a server identity, open only required firewall ports, and prove restart safety.

35 min8 stepsChanges system stateRevision 2
Save or explore
Save to collectionCreate a collection in the sidebar first.
0 of 8 steps completed
Goal

Produce a clean Apache host that is ready for named virtual hosts without exposing sample content or an accidental default configuration.

Supported environments
  • Ubuntu Server 24.04 LTS
  • Apache HTTP Server 2.4.x
Prerequisites
  • Console and recovery access Keep an independent console session and a copy of the current Apache configuration before changing listeners, modules, or access rules.
  • DNS and network plan Resolve every production hostname to the intended public address and list the exact management and client networks that require access.getent ahosts {{domain}} && ip route show
  • Port ownership Confirm that TCP port 80 is free or already belongs to the intended Apache instance.sudo ss -lntp 'sport = :80'
  • Rollback artifact Snapshot the host or create an archive of the active configuration and web roots before the first material change.
Operating boundary

OneLiners never runs these steps or stores secrets. Review placeholders, versions, current state, and change-control requirements before using a command.

Full guide

What you will build

System
  • An Ubuntu-supported Apache 2.4 service with recorded package provenance, listener ownership, boot state, configuration map, log evidence, and a root-owned rollback archive.
  • A minimal global identity and disclosure baseline plus a deterministic health document instead of Ubuntu's sample landing page.
  • A deliberately public HTTP listener protected by explicit firewall policy, syntax-before-reload discipline, named local routing tests, and a clean handoff to virtual hosts and TLS.
Observable outcome
  • Apache is enabled and active, only the intended process owns TCP/80, complete configuration parses, and the server answers the requested FQDN with the expected health marker.
  • ServerTokens and ServerSignature avoid unnecessary version/OS disclosure, ServerName is unambiguous, and sample content is absent.
  • The operator can explain every public port, active module/site/config include, firewall rule, document owner, journal event, and rollback action before adding applications.

Architecture

How the parts fit together

Ubuntu packages Apache as the `apache2` systemd service and split configuration into mods-, conf-, and sites-available with enabled symlinks. A small global conf supplies only server identity and disclosure policy. The default document root contains a root-owned static health marker. Apache parses the complete include graph before reload, while UFW admits public HTTP only after the intended listener and upstream network boundary are understood.

apache2 package/serviceProvides the Ubuntu-maintained daemon, modules, helper commands, log integration, and boot lifecycle.
Global baseline confDefines ServerName, ServerTokens, and ServerSignature without mixing site routing policy.
Default health documentReturns deterministic content for baseline routing tests without exposing sample installation data.
Configuration parserUses apache2ctl configtest and -S to prove syntax and virtual-host selection before reload.
UFW and upstream ACLExpose TCP/80 only according to the intended public HTTP design; authentication remains an application concern.
  1. Inventory Ubuntu release, interfaces, routes, all listeners, web packages, firewall policy, failed units, DNS, and console recovery.
  2. Archive any existing Apache configuration and web roots before the package transaction or file replacement.
  3. Install from signed Ubuntu repositories and record the packaged Apache version rather than assuming the upstream patch.
  4. Enable a minimal global identity baseline and replace public sample content with a deterministic root-owned marker.
  5. Parse the entire configuration and virtual-host map before changing firewall or service state.
  6. Apply the reviewed HTTP firewall rule, enable/reload Apache, route a named request to loopback, and inspect service logs plus listener state.

Assumptions

  • The host is Ubuntu Server 24.04 LTS, uses the approved Ubuntu repositories, and does not already run an undocumented web server, container proxy, or socket activator on TCP/80.
  • The server FQDN has an intended DNS purpose even if public DNS is not yet delegated; local `curl --resolve` provides deterministic pre-DNS testing.
  • Public HTTP is actually required. A private management UI or internal service should receive a source-restricted rule instead of the public Apache profile.
  • Console access and upstream firewall control remain available while service and host-firewall state changes.
  • This baseline serves only a static health marker. Application deployment, TLS, virtual-host isolation, dynamic runtimes, headers, logging, and monitoring require later guides.

Key concepts

ServerName
Apache's canonical global server identity used when a request or context does not provide a more specific name.
ServerTokens
Controls how much server version detail Apache emits in protocol fields such as the Server response header.
ServerSignature
Controls generated footer signatures on server-generated pages; it does not remove every fingerprint.
Enabled configuration
An Ubuntu available file selected through a symlink under the corresponding *-enabled directory.
Graceful reload
A configuration activation that starts new workers with new policy while allowing established requests to finish where possible.

Before you copy

Values used in this guide

{{serverFqdn}}

Canonical baseline hostname used in ServerName and deterministic Host routing tests.

Example: web01.example.net
{{domain}}

Representative DNS name checked by the shared prerequisite before site-specific configuration.

Example: web01.example.net

Security and production boundaries

  • Apache privilege separation still leaves the daemon able to read served content. Keep configuration root-owned and deploy content through a separate controlled account.
  • ServerTokens reduces casual disclosure but is not a vulnerability control; patching, module minimization, TLS, application hardening, and monitoring remain required.
  • The packaged `Apache` UFW profile normally permits public IPv4 and IPv6 HTTP. Inspect generated policy instead of assuming its scope.
  • Do not serve backup archives, source repositories, secret files, private keys, environment files, or writable upload paths from the document root.
  • A successful HTTP response does not prove correct virtual-host isolation or HTTPS. This baseline is intentionally a commissioning stage.

Stop before continuing if

  • Stop if TCP/80 belongs to another process, existing Apache state is not archived, or a production web root has no verified backup.
  • Do not install or reload after repository signature, package, configtest, or virtual-host-map errors.
  • Do not open the firewall until the exact listener, address families, public/private intent, and upstream ACL are understood.
  • Do not accept the baseline while the response, Server header, listener owner, systemd state, or journal differs from expectation.
01

verification

Inventory the host before installation

read-only

Capture release, addresses, listeners, firewall state, installed web packages, and failed units so the new service cannot silently collide with an existing one.

Why this step matters

A full starting inventory prevents package installation from silently colliding with an existing ingress owner, firewall policy, failed unit, or retained web application.

What to understand

Capture release, interfaces, routes, process-owned TCP listeners, IPv4/IPv6 firewall rules, web packages, systemd failures, DNS, and current HTTP responses.

Run socket inspection with sudo so process ownership is visible; a free IPv4 port does not prove IPv6 or a socket unit is free.

Identify every existing `/etc/apache2`, `/var/www`, `/srv/www`, certificate, log, and deployment owner before treating the host as fresh.

System changes

  • No persistent change; reads host, network, package, firewall, service, and web state.

Syntax explained

ip -brief address
Produces a compact address inventory.
ss -lntp
Lists numeric listening TCP sockets and owning processes.
ufw status verbose
Displays active status, defaults, logging, and rules.
systemctl --failed
Lists services already in a failed state.
Command
cat /etc/os-release && ip -brief address && sudo ss -lntp && sudo ufw status verbose && dpkg-query -W 'apache2*' 'nginx*' 2>/dev/null || true; systemctl --failed --no-pager
Example output / evidence
PRETTY_NAME="Ubuntu 24.04 LTS"
Status: active
0 loaded units listed.

Checkpoint: Checkpoint: inventory

Continue whenUbuntu 24.04 and intended addresses are recorded, TCP/80 ownership is known, and no unrelated failed service remains.

Stop whenAn unknown listener, package origin, web root, firewall rule, failed service, or inaccessible console exists.

If this step fails

Port 80 looks free but installation later reports address in use.

Likely causeAn IPv6 listener, socket unit, container, or race was omitted.

Safe checks
  • sudo ss -lntp '( sport = :80 )'
  • systemctl list-sockets --all

ResolutionFind the owning lifecycle and resolve it deliberately before starting Apache.

Security notes

  • Inventory output can reveal private topology and package weaknesses; protect the change record.

Alternatives

  • Use authoritative CMDB and configuration-management evidence when it includes the same live socket checks.

Stop conditions

  • An unknown listener, package origin, web root, firewall rule, failed service, or inaccessible console exists.
02

command

Preserve any existing web configuration

caution

Create a root-owned archive only when /etc/apache2 already exists; record its checksum so rollback does not depend on memory.

Why this step matters

A root-owned, checksummed archive preserves configuration and content selection so package or file changes can be reversed without relying on memory.

What to understand

The conditional archive distinguishes a genuinely fresh host from one with Apache state; do not let a missing archive masquerade as success on an existing host.

Store the archive outside served roots with mode 0700 on its parent, record checksum, size, included paths, and restore command.

A file archive is not application data consistency; dynamic sites need their own database/storage backup.

System changes

  • Creates `/root/oneliners-backups` and, when prior state exists, a compressed archive plus checksum evidence.

Syntax explained

install -d -m 0700
Creates a root-only backup directory.
tar -C / -czf
Archives paths relative to filesystem root using gzip compression.
sha256sum
Records an integrity digest for later restore verification.
Command
sudo install -d -m 0700 /root/oneliners-backups && if test -d /etc/apache2; then sudo tar -C / -czf /root/oneliners-backups/apache-before-install.tgz etc/apache2 var/www; fi && sudo sha256sum /root/oneliners-backups/apache-before-install.tgz 2>/dev/null || echo 'Fresh install: no prior Apache archive'
Example output / evidence
Fresh install: no prior Apache archive

Checkpoint: Checkpoint: backup

Continue whenExisting state produces a non-empty root-only archive whose checksum verifies; a fresh host is explicitly recorded.

Stop whenExisting files are present but archive/checksum fails, storage is insufficient, or application data lies outside scope.

If this step fails

tar reports a file changed while reading.

Likely causeA deployment or service is modifying content during capture.

Safe checks
  • ps -ef | grep -E '[a]pache|[r]sync|[t]ar'

ResolutionQuiesce the owning deployment or use an application-consistent snapshot, then create a new archive.

Security notes

  • The archive may contain credentials and private configuration; never place it under `/var/www`.

Alternatives

  • Use a provider/filesystem snapshot with verified restore when it captures all required paths consistently.

Stop conditions

  • Existing files are present but archive/checksum fails, storage is insufficient, or application data lies outside scope.
03

command

Install Apache from Ubuntu repositories

caution

Refresh signed Ubuntu metadata, install the distribution package, and record the exact package version rather than assuming the upstream patch level.

Why this step matters

Installing the signed Ubuntu package gives Apache a maintained service unit, helper tooling, module layout, log integration, and security-update path.

What to understand

Inspect apt-cache policy before installation to detect a PPA or third-party candidate.

Record both `apache2ctl -v` and dpkg version because Ubuntu may backport fixes without changing the upstream-looking version as expected.

Treat signature, dependency, disk, and maintainer-script failures as blockers; do not curl an alternate installer.

System changes

  • Installs Apache packages, configuration tree, service unit, default site, web root, logs, runtime account, and may start TCP/80.

Syntax explained

apt update
Downloads and verifies repository indexes.
apt install apache2
Installs Ubuntu's Apache package and dependencies.
--yes
Accepts the reviewed transaction noninteractively.
dpkg-query -W
Records the installed package revision.
Command
sudo apt update && sudo apt install --yes apache2 && apache2ctl -v && dpkg-query -W apache2
Example output / evidence
Server version: Apache/2.4.x (Ubuntu)
apache2 2.4.x-ubuntu

Checkpoint: Checkpoint: install

Continue whenPackage origin and version are approved, installation exits zero, and any automatically started listener is Apache on intended interfaces.

Stop whenRepository trust, package plan, maintainer script, listener ownership, or version is unexpected.

If this step fails

Package config fails because the service cannot bind port 80.

Likely causeAn existing process or socket acquired the port.

Safe checks
  • sudo ss -lntp 'sport = :80'
  • sudo journalctl -u apache2 --since '10 minutes ago' --no-pager

ResolutionKeep the firewall closed, identify the owner, and restart package configuration only after resolving ingress ownership.

Security notes

  • Package installation can enable a default public listener; verify firewall and upstream ACL immediately.

Alternatives

  • Stage installation in an image or configuration-management pipeline with the same signed packages.

Stop conditions

  • Repository trust, package plan, maintainer script, listener ownership, or version is unexpected.
04

config

Set an explicit global ServerName

caution

Use the server FQDN to suppress ambiguous identity warnings while keeping site-specific names inside virtual hosts.

Why this step matters

A minimal global identity removes ambiguous startup behavior and reduces generated disclosure while keeping domain routing inside virtual hosts.

What to understand

ServerName should be a stable administrative FQDN, not a user-controlled Host value or temporary address.

ServerTokens Prod reduces header detail and ServerSignature Off removes generated-page footers; neither replaces patching.

Use a conf-available file and a2enconf so selection is explicit and rollback removes one symlink/file.

System changes

  • Creates and enables `/etc/apache2/conf-available/10-oneliners-baseline.conf` with three global directives.

Syntax explained

ServerName
Defines Apache's canonical global server identity.
ServerTokens Prod
Limits protocol banner detail to the product name.
ServerSignature Off
Suppresses Apache footer signatures on generated pages.
a2enconf
Creates the Ubuntu conf-enabled selection symlink.
File /etc/apache2/conf-available/10-oneliners-baseline.conf
Configuration
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

printf 'ServerName {{serverFqdn}}\nServerTokens Prod\nServerSignature Off\n' | sudo tee /etc/apache2/conf-available/10-oneliners-baseline.conf && sudo a2enconf 10-oneliners-baseline
Example output / evidence
Enabling conf 10-oneliners-baseline.

Checkpoint: Checkpoint: identity

Continue whenThe conf is root-owned, enabled exactly once, parser accepts it, and runtime dump shows the intended ServerName.

Stop whenFQDN is unapproved, the file is writable by deployment users, duplicate directives conflict, or parser warns.

If this step fails

The FQDN warning remains.

Likely causeThe conf is not enabled, a syntax error prevents loading, or another context overrides identity.

Safe checks
  • sudo a2query -c 10-oneliners-baseline
  • sudo apache2ctl -t -D DUMP_RUN_CFG

ResolutionFix the enabled include and rerun complete configtest before reload.

Security notes

  • Do not publish internal naming that violates the intended external identity policy.

Alternatives

  • Use an IP-safe placeholder only on isolated test hosts while a stable name is assigned.

Stop conditions

  • FQDN is unapproved, the file is writable by deployment users, duplicate directives conflict, or parser warns.
05

config

Replace the distribution landing page

caution

Create a minimal health document owned by root and served read-only; remove public sample information without deleting the web root.

Why this step matters

Replacing sample content prevents public disclosure of installation details and gives routing checks a deterministic marker owned outside the web worker.

What to understand

The health file is static, contains no secret/runtime data, and is root-owned mode 0644 so Apache reads but cannot modify it.

Keep the document root directory non-writable by `www-data`; application uploads belong outside executable/static roots with explicit handling.

This temporary default is replaced by an explicit rejecting host when named sites are introduced.

System changes

  • Replaces `/var/www/html/index.html` content and enforces root ownership plus read-only public mode.

Syntax explained

tee ... >/dev/null
Writes exact deterministic content while suppressing duplicate stdout.
chown root:root
Prevents the Apache runtime account from modifying served content.
chmod 0644
Allows public read but only root write.
File /var/www/html/index.html
Configuration
printf '<!doctype html><title>Apache ready</title><h1>Apache ready</h1>\n' | sudo tee /var/www/html/index.html >/dev/null && sudo chown root:root /var/www/html/index.html && sudo chmod 0644 /var/www/html/index.html
Example output / evidence
-rw-r--r-- 1 root root /var/www/html/index.html

Checkpoint: Checkpoint: default site

Continue whenThe exact marker exists, is root-owned 0644, and no sample or secret files are reachable in the root.

Stop whenThe path is a symlink/unexpected mount, retained application content would be overwritten, or runtime user can write it.

If this step fails

The file changes but HTTP still shows old content.

Likely causeAnother vhost/root, cache, proxy, or address receives the request.

Safe checks
  • sudo apache2ctl -S
  • curl --resolve 'web01.example.net:80:127.0.0.1' http://web01.example.net/

ResolutionIdentify the selected vhost and effective DocumentRoot before changing any other file.

Security notes

  • Never use phpinfo, environment dumps, or server-status as a public health document.

Alternatives

  • Serve a fixed 204 response from an explicit health location when no document root is desired.

Stop conditions

  • The path is a symlink/unexpected mount, retained application content would be overwritten, or runtime user can write it.
06

verification

Validate the complete configuration

read-only

Run Apache's parser and virtual-host dump before reloading; do not treat an active systemd unit as proof that the new files are valid.

Why this step matters

Parsing the full include graph and dumping host selection catches errors and accidental defaults before service or firewall activation.

What to understand

configtest validates syntax but not file readability at request time, application correctness, or external DNS.

`apache2ctl -S` shows default vhosts, name/alias mapping, ports, and source file lines; review every entry.

Also inspect loaded modules and runtime config when the host was not freshly installed.

System changes

  • No persistent change; parses Apache configuration and displays the virtual-host map.

Syntax explained

configtest
Parses the complete effective Apache configuration.
-S
Shows virtual-host selection, aliases, defaults, and source files.
Command
sudo apache2ctl configtest && sudo apache2ctl -S
Example output / evidence
Syntax OK
*:80 is a NameVirtualHost

Checkpoint: Checkpoint: syntax

Continue whenSyntax OK appears and the only TCP/80 default/site is the intentional baseline with known source files.

Stop whenAny warning/error, duplicate host, unknown include/module, or unintended default appears.

If this step fails

Syntax is OK but -S shows duplicate defaults.

Likely causeMultiple enabled site symlinks define the same listener/name.

Safe checks
  • find /etc/apache2/sites-enabled -maxdepth 1 -type l -ls

ResolutionDisable only the duplicate enabled selection after confirming file ownership and intended default.

Security notes

  • Treat unknown enabled modules and includes as executable policy, not harmless clutter.

Alternatives

  • Run `apache2ctl -t -D DUMP_RUN_CFG -D DUMP_VHOSTS -D DUMP_MODULES` for deeper review.

Stop conditions

  • Any warning/error, duplicate host, unknown include/module, or unintended default appears.
07

warning

Open HTTP only after confirming the listener

danger

Preview the firewall change, allow the packaged Apache profile, and confirm no unrelated public ports were added.

Why this step matters

Previewing and then enabling only the packaged HTTP profile makes public exposure an explicit change after listener and configuration ownership are proven.

What to understand

Inspect the profile definition and generated IPv4/IPv6 rules. Public HTTP is appropriate only for a public web baseline.

Compare host policy with cloud firewall, load balancer, NAT, and routing; a permissive upstream layer or alternate IPv6 path can change exposure.

Test from a real external network after activation and keep the console open.

System changes

  • Adds persistent UFW allow rules for the Apache HTTP profile, normally public TCP/80 for enabled address families.

Syntax explained

--dry-run
Prints generated UFW policy without applying it.
allow 'Apache'
Applies the packaged Apache HTTP application profile.
status numbered
Shows active rule order and deletion identifiers.
Command
sudo ufw --dry-run allow 'Apache' && sudo ufw allow 'Apache' && sudo ufw status numbered
Example output / evidence
[ 1] Apache ALLOW IN Anywhere

Checkpoint: Checkpoint: firewall

Continue whenOnly intended TCP/80 rules are added and no unrelated public port becomes reachable.

Stop whenThe service is private, profile includes unexpected ports, upstream policy is unknown, or console recovery is absent.

If this step fails

Remote HTTP remains unreachable.

Likely causeUpstream ACL, routing, NAT, IPv6, listener binding, or UFW order blocks traffic.

Safe checks
  • sudo ufw status numbered
  • sudo tcpdump -ni any tcp port 80 -c 20

ResolutionTrace the intended flow and correct the smallest layer; never open arbitrary ports.

Security notes

  • Opening HTTP allows any reachable client to exercise Apache and served applications; deploy monitoring and patching before content.

Alternatives

  • Use a source-restricted `ufw allow from CIDR to any port 80 proto tcp` for private services.

Stop conditions

  • The service is private, profile includes unexpected ports, upstream policy is unknown, or console recovery is absent.
08

verification

Enable, reload, and test the service locally

read-only

Enable boot start, reload the validated configuration, request the health document by name, and inspect the latest service messages.

Why this step matters

Enabling boot state, gracefully loading validated configuration, and sending a named local request prove the actual service path rather than only static files.

What to understand

`enable --now` changes both future boot behavior and current service state; reload then applies the latest validated configuration.

`curl --resolve` routes the requested FQDN to loopback while preserving the Host header, isolating Apache from DNS propagation.

Inspect journal, response headers/body, listener owner, and failed units after activation; a 200 alone is incomplete evidence.

System changes

  • Enables and starts apache2, reloads configuration, creates request/access-log evidence, and preserves boot activation.

Syntax explained

systemctl enable --now
Enables boot start and starts the service immediately.
systemctl reload
Gracefully applies already validated configuration.
curl --fail
Returns failure for HTTP error status.
--resolve host:80:127.0.0.1
Overrides DNS while retaining the intended Host header.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

sudo systemctl enable --now apache2 && sudo systemctl reload apache2 && curl --fail --silent --show-error --resolve '{{serverFqdn}}:80:127.0.0.1' http://{{serverFqdn}}/ && sudo journalctl -u apache2 -n 20 --no-pager
Example output / evidence
<h1>Apache ready</h1>
Started apache2.service - The Apache HTTP Server.

Checkpoint: Checkpoint: activate

Continue whenService is enabled/active, named request returns only the marker, logs show a clean reload, and Apache alone owns TCP/80.

Stop whenStart/reload fails, response routes elsewhere, header exposes unexpected detail, journal errors, or listener ownership differs.

If this step fails

Reload succeeds but the old response remains.

Likely causeThe request targets another vhost/cache or file path, not a stale Apache process.

Safe checks
  • sudo apache2ctl -S
  • curl -v --resolve 'web01.example.net:80:127.0.0.1' http://web01.example.net/

ResolutionTrace vhost selection and cache headers before restarting or deleting content.

Security notes

  • Do not leave the temporary default as the final multi-tenant fallback; introduce a rejecting default before named sites.

Alternatives

  • Use `apache2ctl graceful` through the managed service when systemd reload is unavailable and policy permits.

Stop conditions

  • Start/reload fails, response routes elsewhere, header exposes unexpected detail, journal errors, or listener ownership differs.

Finish line

Verification checklist

Configuration and boot statesudo apache2ctl configtest && systemctl is-enabled apache2 && systemctl is-active apache2Syntax OK, enabled, and active.
HTTP responsecurl --fail --head --resolve '{{serverFqdn}}:80:127.0.0.1' http://{{serverFqdn}}/Returns HTTP/1.1 200 OK with ServerTokens not exposing the OS version.
Listener scopesudo ss -lntp 'sport = :80'Only Apache owns the intended port 80 listeners.

Recovery guidance

Common problems and safe checks

APT cannot install Apache because another package or process owns TCP/80.

Likely causenginx, a container publisher, a previous Apache build, or socket activation already provides HTTP.

Safe checks
  • sudo ss -lntp 'sport = :80'
  • systemctl list-sockets --all
  • docker ps --format '{{.Names}} {{.Ports}}'

ResolutionIdentify the intended ingress owner and migrate or stop it under a separate rollback plan; never kill an unknown listener.

apache2ctl configtest reports `Could not reliably determine the server's fully qualified domain name`.

Likely causeNo valid global ServerName is enabled, the conf symlink is absent, or the configured name is malformed.

Safe checks
  • sudo a2query -c 10-oneliners-baseline
  • sudo apache2ctl -t -D DUMP_RUN_CFG

ResolutionSet one stable FQDN in the root-owned baseline conf, enable it, and repeat parser and runtime dumps.

Local curl succeeds but a remote client times out.

Likely causeUFW, provider ACL, routing, address family, NAT, or listener binding blocks the public path.

Safe checks
  • sudo ss -lntp 'sport = :80'
  • sudo ufw status numbered
  • sudo tcpdump -ni any tcp port 80 -c 20

ResolutionTrace each boundary and correct the smallest intended rule; do not add unrelated broad ports.

Apache serves the Ubuntu sample page instead of the health marker.

Likely causeThe request reached another vhost/root, a proxy/cache, or the file replacement targeted a different path.

Safe checks
  • sudo apache2ctl -S
  • curl --resolve 'web01.example.net:80:127.0.0.1' http://web01.example.net/
  • readlink -f /var/www/html/index.html

ResolutionUse the vhost map and named local request to identify the active DocumentRoot, then replace only the intended file.

After the procedure

Alternatives and next steps

Consider these alternatives

  • Bind Apache to a private address and use a source-restricted firewall rule for internal services.
  • Use nginx or another approved server when its event model, configuration ecosystem, or reverse-proxy requirements fit the platform better.
  • Bake the same files through configuration management once the manual evidence and rollback procedure are understood.

Operate it safely

  • Configure an explicit rejecting default host and one isolated named virtual host per site.
  • Add public TLS with automated renewal, hostname verification, expiry monitoring, and an HTTP-to-HTTPS redirect policy.
  • Disable unused modules and methods, add security headers at the correct application boundary, and document proxy/client address trust.
  • Configure per-site structured access/error logs, rotation, centralized retention, uptime checks, latency/error alerts, and capacity monitoring.
  • Automate package and configuration updates through canaries, configtest, graceful reload, external probes, and rollback.

Reference

Frequently asked questions

Should port 80 remain open after HTTPS?

Usually yes for HTTP-to-HTTPS redirects and ACME HTTP-01, but it should serve no sensitive application content and must be monitored.

Does ServerTokens Prod hide Apache?

It reduces banner detail but software can still be fingerprinted. Keep Apache patched and minimize modules.

Why test with curl --resolve?

It sets both destination address and HTTP Host deterministically without waiting for DNS, so routing can be proven before public cutover.

Recovery

Rollback

Disable the new service and firewall rule, then restore the archived configuration if this replaced an earlier deployment.

  1. Remove the UFW Apache rule by its numbered entry and verify port 80 is no longer public.
  2. Disable and stop apache2 before restoring files from /root/oneliners-backups/apache-before-install.tgz.
  3. Run apache2ctl configtest after restoration and start only the intended previous service.
  4. Purge apache2 only for a confirmed fresh install; never purge when /var/www contains retained application data.

Evidence

Sources and review

Verified 2026-07-25Review due 2026-10-23
Ubuntu: install Apache2officialUbuntu: configure Apache2officialApache HTTP Server 2.4 documentationofficial