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.
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.
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 StandardConnect 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.
Verify the Apache and Certbot setup
Confirm that Apache is running:
sudo systemctl status apache2.service --no-pagerConfirm that Certbot is installed and the timer is active:
certbot --version sudo systemctl status certbot.timer --no-pagerConfirm 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/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.confUncomment 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.comStep 5c - Re-run the firstboot script
Run the script:
sudo /usr/local/sbin/apache-firstboot.shThe 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.
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 -datesYou 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 -5The first line shows HTTP/2 200.
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-pagerRun a renewal in test mode to confirm the configuration is correct:
sudo certbot renew --dry-runThe output ends with Congratulations, all simulated renewals succeeded.
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: ApacheServer 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-apacheManage the Apache service
Check the service status:
sudo systemctl status apache2.service --no-pagerReload after a config change (no downtime):
sudo systemctl reload apache2.serviceRestart the service:
sudo systemctl restart apache2.serviceView the error log:
sudo tail -n 50 /var/log/apache2/error.logView the access log:
sudo tail -n 50 /var/log/apache2/access.logTest the config before a restart:
sudo apachectl configtestTroubleshooting
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.
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.
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