Deployment guide

Apache Httpd 2.4 with SSL

Videos

Overview: Apache HTTP Server 2.4 with SSL on Azure: overview (watch on YouTube)

Launch and log in: Apache HTTP Server 2.4 with SSL on Azure: launch and log in (watch on YouTube)

Deep Dive: Apache HTTP Server 2.4 with SSL on Azure: deep dive (watch on YouTube)

Overview

This guide covers deploying and configuring Apache HTTP Server 2.4 with SSL on Ubuntu 24.04 on Azure, using the SecureAnyCloud Azure Marketplace image. It is the SSL-focused variant of the SecureAnyCloud apache-httpd offering. It has the same hardened Apache 2.4 base, with Certbot and the python3-certbot-apache plugin pre-installed and the systemd renewal timer enabled at build time.

The image ships Apache 2.4 from the official Ubuntu noble main repository with the event MPM and four modules pre-enabled (mod_ssl, mod_rewrite, mod_headers, mod_http2). A per-VM self-signed TLS certificate is generated at first boot, so HTTPS works out of the box.

To get a trusted certificate, point a domain at the VM and put your domain and email in /stage/scripts/sac-cert.conf. Then re-run the firstboot script. Certbot replaces the self-signed certificate with a real Let's Encrypt certificate in about 60 seconds, and auto-renewal then takes over silently via certbot.timer.

What you get:

Apache HTTP Server 2.4 from the official Ubuntu noble main repository

Certbot and the python3-certbot-apache plugin

certbot.timer enabled at install (twice-daily renewal check; renews any certificate with 30 days or less to expiry)

Modules enabled: ssl, rewrite, headers, http2

Event MPM (the Ubuntu 24.04 default)

Default virtual hosts on TCP 80 and TCP 443 serving /var/www/html

Per-VM self-signed RSA 2048-bit TLS certificate generated at first boot, used until you swap to a Certbot-issued certificate

Certbot config at /stage/scripts/sac-cert.conf, with DOMAIN and EMAIL lines for you to fill in

Security hardening: ServerTokens Prod, ServerSignature Off, TraceEnable Off

Helper conf at /etc/apache2/conf-available/zz-security-sac.conf (loads after the stock security.conf so its values win)

Systemd services: apache2.service, apache-firstboot.service, certbot.timer

Ubuntu 24.04 LTS base with latest security patches applied at build time

Azure Linux Agent for cloud integration and SSH key injection

24/7 SecureAnyCloud support with a guaranteed 24-hour response SLA

Prerequisites

Before you deploy this image, make sure you have:

An active Azure subscription

A subscription to the Apache HTTP Server 2.4 with SSL on Ubuntu 24.04 listing on Azure Marketplace

An SSH public key for VM authentication

A registered domain name where you can set DNS A records (required for Let's Encrypt certificate issuance)

A virtual network and subnet in the target region

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM). Apache with the event MPM is efficient on small instances. Scale up for high request rates.

1

Deploy from the Azure Portal

Open Marketplace in the Azure Portal and search for Apache HTTP Server 2.4 with SSL. Select the SecureAnyCloud publisher entry, then choose Create to start the wizard.

On the Basics tab, choose your subscription, target resource group and region, and set the VM name. Choose SSH public key as the authentication type. Set the username (the examples below use azureuser) and paste your SSH public key. Standard_B2s is the recommended starting size.

On the Networking tab, attach a network security group. It must allow inbound TCP 22 from your management IP range, TCP 80 from 0.0.0.0/0 (required for the Let's Encrypt HTTP-01 challenge) and TCP 443 from your client networks. Port 80 must stay open from the internet so that Certbot can issue and renew certificates.

Choose Review + create and wait for validation. Then choose Create. Deployment takes around two minutes.

2

Deploy from the Azure CLI

Create the network resources, accept the Marketplace terms once, then create the VM:

RG="apache-ssl-prod" LOCATION="eastus" VM_NAME="apache-ssl-01" ADMIN_USER="azureuser" IMAGE_URN="sac1647283583153:apache-httpd-ssl-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create \
  --resource-group "$RG" \
  --name apache-vnet --address-prefix 10.91.0.0/16 \
  --subnet-name apache-subnet --subnet-prefix 10.91.1.0/24 az network nsg create --resource-group "$RG" --name apache-nsg az network nsg rule create \
  --resource-group "$RG" --nsg-name apache-nsg \
  --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" \
  --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create \
  --resource-group "$RG" --nsg-name apache-nsg \
  --name allow-http-public --priority 110 \
  --source-address-prefixes "*" \
  --destination-port-ranges 80 --access Allow --protocol Tcp az network nsg rule create \
  --resource-group "$RG" --nsg-name apache-nsg \
  --name allow-https --priority 120 \
  --source-address-prefixes "*" \
  --destination-port-ranges 443 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:apache-httpd-ssl-ubuntu-24-04:default:latest
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$IMAGE_URN" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username "$ADMIN_USER" --ssh-key-values "$SSH_KEY" \
  --vnet-name apache-vnet --subnet apache-subnet --nsg apache-nsg \
  --public-ip-sku Standard
3

Connect via SSH

Connect to the VM with its IP address:

ssh azureuser@<vm-ip>

apache2.service is already running with the per-VM self-signed certificate, and certbot.timer is already armed. The steps below swap the self-signed certificate for a real Let's Encrypt one.

4

Verify the Apache and Certbot setup

Confirm that Apache is running:

sudo systemctl status apache2.service --no-pager

Confirm that Certbot is installed and the timer is active:

certbot --version sudo systemctl status certbot.timer --no-pager

Confirm that firstboot completed and a self-signed certificate is in place:

sudo test -f /var/lib/sac/data/apache-firstboot.done && echo FIRSTBOOT_DONE
sudo ls -la /etc/ssl/sac/
5

Issue a real Let's Encrypt certificate

This is the main workflow the image is built around. It has three parts.

Step 5a - Point your DNS A record at the VM public IP

Make this change at your DNS provider (Route 53, Cloudflare, GoDaddy and so on). Wait for propagation, and check it with:

dig +short <your-domain>

Continue only when the output equals the public IP of the VM.

Step 5b - Edit the certificate config

Open the config file:

sudo nano /stage/scripts/sac-cert.conf

Uncomment and set the DOMAIN and EMAIL lines, then save and exit. The file should end up looking like this:

DOMAIN=www.example.com EMAIL=admin@example.com

Step 5c - Re-run the firstboot script

Run the script:

sudo /usr/local/sbin/apache-firstboot.sh

The script reads the config, runs certbot --apache --non-interactive --agree-tos -d $DOMAIN -m $EMAIL --redirect, and reloads Apache. From then on, certbot.timer keeps the certificate renewed.

If Certbot fails (DNS not propagated, port 80 blocked or a Let's Encrypt rate limit), the script logs the cause and falls back to the self-signed certificate. Fix the underlying issue and run the script again.

6

Verify HTTPS with the real certificate

After Certbot succeeds, confirm the certificate chain in your browser or with openssl:

echo | openssl s_client -servername <your-domain> -connect <your-domain>:443 2>/dev/null | openssl x509 -noout -issuer -subject -dates

You should see issuer=C = US, O = Let's Encrypt, CN = R3 (or similar), and the subject matches your domain.

Test HTTPS end to end:

curl -sI https://<your-domain>/ | head -5

The first line shows HTTP/2 200.

7

Confirm auto-renewal

certbot.timer runs twice daily and renews any certificate with 30 days or less to expiry. Inspect the schedule:

sudo systemctl list-timers certbot.timer --no-pager

Run a renewal in test mode to confirm the configuration is correct:

sudo certbot renew --dry-run

The output ends with Congratulations, all simulated renewals succeeded.

8

Confirm the hardened Server header

The image ships with ServerTokens Prod, so the Server response header reveals only the product name. Check it with:

curl -sI http://localhost/ | grep -i '^Server:'

The expected output is:

Server: Apache
9

Server components

Component Path

Apache HTTP daemon /usr/sbin/apache2

Certbot CLI /usr/bin/certbot

Certbot Apache plugin python3-certbot-apache (apt package)

Document root /var/www/html

Customer Certbot config /stage/scripts/sac-cert.conf

SecureAnyCloud hardening fragment /etc/apache2/conf-available/zz-security-sac.conf

SecureAnyCloud :80 vhost /etc/apache2/sites-available/000-sac.conf

SecureAnyCloud :443 vhost /etc/apache2/sites-available/000-sac-ssl.conf

Self-signed TLS cert directory /etc/ssl/sac/

Let's Encrypt cert directory /etc/letsencrypt/live//

Renewal config /etc/letsencrypt/renewal/.conf

Apache error log /var/log/apache2/error.log

Firstboot script /usr/local/sbin/apache-firstboot.sh

Firstboot sentinel /var/lib/sac/data/apache-firstboot.done

Renewal timer certbot.timer (systemd)

Inspect the installed package versions:

dpkg-query -W -f='${Package} ${Version}\n' apache2 certbot python3-certbot-apache
10

Manage the Apache service

Check the service status:

sudo systemctl status apache2.service --no-pager

Reload after a config change (no downtime):

sudo systemctl reload apache2.service

Restart the service:

sudo systemctl restart apache2.service

View the error log:

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

View the access log:

sudo tail -n 50 /var/log/apache2/access.log

Test the config before a restart:

sudo apachectl configtest
11

Troubleshooting

Certbot fails with "Connection refused" or "Timeout"

Confirm that port 80 is open from the internet in your NSG, because the Let's Encrypt HTTP-01 challenge requires inbound port 80.

Confirm that the DNS A record points at the VM public IP: dig +short <your-domain>

Confirm that Apache is running and listening on port 80: sudo ss -tln | grep ':80 '

Certbot fails with "rate limit" error

Let's Encrypt rate-limits per registered domain (50 certificates per week). Wait, or use the --staging server first to debug.

For high-volume certificate issuance, request a Let's Encrypt rate limit adjustment.

Auto-renewal is not running

Confirm that the timer is enabled: sudo systemctl is-enabled certbot.timer

Inspect the last run: sudo journalctl -u certbot --since '2 days ago' --no-pager

Force a renewal check now: sudo certbot renew

HTTPS works locally but the browser shows a certificate warning

The image ships with a self-signed certificate until you run Step 5. Browsers do not trust self-signed certificates.

Run Step 5 to swap to a Let's Encrypt certificate.

After Certbot finishes, hard-refresh your browser (Cmd+Shift+R or Ctrl+Shift+R) to clear the cached certificate.

The Server header still leaks the version

Confirm that zz-security-sac.conf is enabled: ls /etc/apache2/conf-enabled/

The zz- prefix is intentional. It makes the conf load after Ubuntu's stock security.conf, so its ServerTokens Prod wins. Do not rename it.

12

Security recommendations

Restrict port 22 to your management IP ranges only.

Keep port 80 open from the internet. It is required for the Let's Encrypt HTTP-01 challenge and for renewal. If you cannot expose port 80 publicly, switch to the DNS-01 challenge with a Certbot DNS plugin (python3-certbot-dns-route53, python3-certbot-dns-cloudflare and so on).

Add HSTS to your :443 virtual host: Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

Disable HTTP once HTTPS is working, and only if you have set up alternative certificate renewal such as DNS-01: sudo a2dissite 000-sac && sudo systemctl reload apache2

Enable mod_security to add a web application firewall: sudo apt-get install -y libapache2-mod-security2 modsecurity-crs

Monitor /var/log/apache2/access.log for unusual patterns.

Keep Apache and Certbot updated. Run sudo apt-get update && sudo apt-get upgrade -y periodically.

13

Support and licensing

Apache HTTP Server and Certbot are both licensed under the Apache License 2.0. Let's Encrypt certificates are issued free of charge, subject to the ISRG Subscriber Agreement.

SecureAnyCloud provides commercial support for this image separately from the upstream projects.

Email: sales@secureanycloud.com

Website: secureanycloud.com

Support hours: 24/7, with a guaranteed 24-hour response SLA

Deploy on Azure

Launch Apache HTTP Server 2.4 with SSL on Ubuntu 24.04 with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure

Launch Apache HTTP Server 2.4 with SSL on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure