Web Check on Ubuntu 24.04 on Azure User Guide
Overview
Web Check is an open-source, self-hosted OSINT and security-analysis dashboard for websites. You enter a URL and it gathers what is publicly observable about that site into a single report of around 35 cards: DNS and TXT records, SSL/TLS certificate detail and TLS connection information, HTTP security headers, HSTS, open ports, DNSSEC, redirect chains, cookies, detected technology stack, WHOIS and registration data, mail configuration, crawl rules, social tags and an estimated carbon footprint.
The cloudimg image runs the Web Check Node.js server behind nginx on port 80, hardened and ready on first boot. Web Check has no authentication of its own, and its analysis API will fetch arbitrary URLs on a requester's behalf, so this image is secure by default: the Node server is bound to the loopback interface, nginx gates the whole dashboard and its API behind per-VM HTTP Basic-Auth, and a unique password is generated on the first boot of every instance and written to a root-only file. A public, unauthenticated health endpoint is exposed at /health. Built from Web Check 2.2.2 (MIT), backed by 24/7 cloudimg support.
What is included:
- Web Check
2.2.2, built from the official source at/opt/web-check/src - Node.js 22 running the analysis server as the non-login
webcheckuser, bound to127.0.0.1:3000 - nginx on
:80as the single public listener, gating the dashboard and API behind per-VM HTTP Basic-Auth - A pinned headless Chrome at
/opt/web-check/puppeteer, so the screenshot and quality checks work out of the box - A first-boot service that generates a unique
adminpassword into a root-only0600file - An SSRF guard that blocks analysis of cloud instance metadata and private network ranges
- A public, unauthenticated health endpoint at
/health web-check.service,nginx.serviceandweb-check-firstboot.serviceas systemd units, enabled on every boot- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point. NSG inbound: allow 22/tcp from your management network and 80/tcp for the dashboard. Outbound HTTPS and DNS must be permitted, because Web Check reaches out to the sites you analyse. Web Check serves plain HTTP on port 80; for production, terminate TLS in front of it with your own domain.
Step 1 - Deploy from the Azure Marketplace
In the Azure portal choose Create a resource, search for Web Check on Ubuntu 24.04 LTS by cloudimg, and select Create. Pick your subscription, resource group and region, choose the Standard_B2s size, and supply your SSH public key for the azureuser account. On the Networking tab allow inbound 22/tcp from your management network and 80/tcp for the dashboard, then review and create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group my-resource-group \
--name my-web-check \
--image cloudimg:web-check-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the platform and version
Confirm the base image, the Node.js runtime, the Web Check release and the bundled headless browser:
. /etc/os-release && echo "$PRETTY_NAME"
node --version
jq -r '.name + " " + .version' /opt/web-check/src/package.json
You see the Ubuntu release, the Node.js 22 runtime and Web Check 2.2.2:
Ubuntu 24.04.4 LTS
v22.23.2
web-check 2.2.2

Step 5 - Confirm the services are running
Web Check runs as a systemd service behind nginx, and a one-shot first-boot service prepares the per-VM login. Confirm all three, and that the analysis server is listening on loopback only:
systemctl is-active web-check.service nginx.service
systemctl is-enabled web-check-firstboot.service web-check.service nginx.service
ss -ltn | grep -E ':(80|3000)\b'
test -f /var/lib/cloudimg/web-check-firstboot.done && echo "first boot complete"
Both services report active, all three are enabled, and note that port 3000 is bound to 127.0.0.1 only while nginx owns 0.0.0.0:80 - the analysis API is not reachable except through the authenticated proxy:
active
active
enabled
enabled
enabled
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 511 127.0.0.1:3000 0.0.0.0:*
LISTEN 0 511 [::]:80 [::]:*
first boot complete

Step 6 - Verify the authentication gate
Web Check has no login of its own, so nginx gates the dashboard and the analysis API behind HTTP Basic-Auth. The public /health endpoint returns 200 without credentials, while both the dashboard and the API return 401 without credentials and 200 with the per-VM login:
curl -s -o /dev/null -w 'health: %{http_code}\n' http://127.0.0.1/health
curl -s -o /dev/null -w 'unauth UI: %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'unauth API: %{http_code}\n' 'http://127.0.0.1/api/dns?url=https://example.com'
sudo bash -c 'U=$(grep ^WEB_CHECK_USERNAME= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); P=$(grep ^WEB_CHECK_PASSWORD= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); curl -sL -o /dev/null -w "auth UI: %{http_code}\n" -u "$U:$P" http://127.0.0.1/'
You see 200 for the public health probe, 401 for both the unauthenticated dashboard and the unauthenticated API, and 200 once the per-VM credential is supplied:
health: 200
unauth UI: 401
unauth API: 401
auth UI: 200

Step 7 - Read your per-VM login
The unique per-VM credential (username admin) is written to a root-only file on first boot:
sudo stat -c 'file mode: %a owner: %U:%G' /var/lib/cloudimg/web-check-credentials.txt
sudo grep -c . /var/lib/cloudimg/web-check-credentials.txt
The file is mode 600, owned by root:root. Read it to get your password:
file mode: 600 owner: root:root
Print the file when you need the password (keep it off shared terminals):
sudo cat /var/lib/cloudimg/web-check-credentials.txt
# Web Check 2.2.2 on Ubuntu 24.04 (cloudimg Azure Marketplace image)
# Per-VM HTTP Basic-Auth credentials - UNIQUE to this VM, minted at first boot.
WEB_CHECK_URL=http://<vm-public-ip>/
WEB_CHECK_USERNAME=admin
WEB_CHECK_PASSWORD=<generated-per-vm-password>
Step 8 - Open Web Check
Browse to http://<vm-public-ip>/ in any modern browser. Your browser prompts for the HTTP Basic-Auth login: enter admin and the password from Step 7. Web Check opens on its analyse screen with a single URL box, and lists every supported check underneath so you can see what the report will cover.

Step 9 - Run your first analysis
Type a domain into the box - example.com is a good first target - and select Analyze!. Web Check fans out around 38 checks in parallel and streams the results in as each one answers, so the report fills in progressively over roughly ten to thirty seconds.
The report opens with an Advisory panel that groups what it found by severity: Critical, Issues, Warnings, Informational and Passes. This is the fastest way to see what is wrong with a site - here it has flagged an imminent domain expiry and five missing security headers.

Step 10 - Read the certificate, header and DNS cards
Scroll down and the individual report cards carry the detail behind the advisory. The SSL Certificate card shows the subject, issuer, trust status, curve, serial number, fingerprint and both the expiry and renewal dates. The Headers card lists the live response headers exactly as the server returned them, DNS Records shows the resolved A, AAAA, MX, NS, SOA and TXT records, and HTTP Security grades the site against the standard protective headers.

Step 11 - Inspect TLS, DNS infrastructure and DNSSEC
Further down, the TLS Connection card reports the negotiated protocol, cipher suite, forward secrecy, session resumption and OCSP stapling. DNS Server resolves the authoritative nameservers to their addresses, Firewall identifies any WAF in front of the site, Threats checks reputation feeds, and DNSSEC lists the zone signing keys with their algorithms.

Step 12 - Query the analysis API directly
Every check is also a JSON endpoint, so you can drive Web Check from scripts and pipelines. Each endpoint takes a url parameter, and the same per-VM credential authenticates it:
sudo bash -c 'U=$(grep ^WEB_CHECK_USERNAME= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); P=$(grep ^WEB_CHECK_PASSWORD= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); curl -s -u "$U:$P" "http://127.0.0.1/api/dns?url=https://example.com" | jq "{A,NS}"'
sudo bash -c 'U=$(grep ^WEB_CHECK_USERNAME= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); P=$(grep ^WEB_CHECK_PASSWORD= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); curl -s -u "$U:$P" "http://127.0.0.1/api/ssl?url=https://example.com" | jq "{subject,issuer}"'
Each endpoint returns the same data the matching dashboard card displays:
{
"A": [
"104.20.23.154",
"172.66.147.243"
],
"NS": [
"elliott.ns.cloudflare.com",
"hera.ns.cloudflare.com"
]
}
{
"subject": {
"CN": "example.com"
},
"issuer": {
"C": "US",
"O": "SSL Corporation",
"CN": "Cloudflare TLS Issuing ECC CA 3"
}
}

Request /api with no check name to run every check at once and return the whole report as a single JSON document.
Step 13 - Review the built-in SSRF guard
Because Web Check fetches whatever URL it is given, this image ships a blocklist so the appliance cannot be aimed at cloud instance metadata or at your own private network. Confirm it is in place:
grep '^API_BLOCKED_HOSTS=' /etc/web-check/web-check.env
The blocklist covers the Azure instance metadata endpoint and every RFC1918, CGNAT, loopback and link-local range:
API_BLOCKED_HOSTS=169.254.0.0/16,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10,localhost
To analyse a host inside your own network deliberately, remove that range from the list and restart the service - but only do so on an instance you keep private.
Step 14 - Enable the optional third-party checks
Around 35 of the checks work immediately with no configuration. A small number call external services that need a free API key of your own - Shodan, Google Safe Browsing threat data, global ranking, subdomain discovery and page quality. Without a key those cards report that the key is missing and the rest of the report is unaffected; no key is baked into this image.
To enable one, add it to the runtime environment file and restart:
grep -E '^#\s+[A-Z_]+_API_KEY|^#\s+TRANCO|^#\s+WHO_API_KEY' /etc/web-check/web-check.env
The commented key names are listed ready to fill in:
# GOOGLE_CLOUD_API_KEY=
# SHODAN_API_KEY=
# SECURITY_TRAILS_API_KEY=
# URL_SCAN_API_KEY=
# CLOUDMERSIVE_API_KEY=
# WHO_API_KEY=
# TORRENT_IP_API_KEY=
Uncomment the one you want, paste in your key, then run sudo systemctl restart web-check to pick it up.
Maintenance
Web Check is stateless - it stores no database and no report history - so maintenance is limited to the service and the OS.
systemctl status web-check.service --no-pager | head -5
journalctl -u web-check.service --no-pager -n 10
Restart the analysis server with sudo systemctl restart web-check, and reload nginx with sudo systemctl reload nginx after editing /etc/nginx/sites-available/web-check. The OS receives unattended security updates automatically.
To rotate the dashboard password, write a new one with htpasswd:
sudo htpasswd -B /etc/nginx/.web-check-htpasswd admin
Note that the rate limiter upstream ships for its public instance is deliberately disabled here (API_ENABLE_RATE_LIMIT=false in /etc/web-check/web-check.env), because a single full analysis makes around 38 API calls and the public limits would throttle you on your own instance. If you expose this VM to a wider audience, set it to true and restart the service.
Support
cloudimg provides 24/7 technical support for this image by email at support@cloudimg.co.uk and via live chat, with a one hour average response time for critical issues. We help with deployment, reverse proxy and TLS configuration, managing the per-instance login, enabling the optional third-party integrations, tuning the blocked host rules, headless browser troubleshooting and version upgrades.