# Share an Nmap Scan Report — HTML Link, Access-Controlled

Canonical: https://commareports.com/share-nmap-report
Published: 2026-08-24

> nmap -oX plus xsltproc gives you a readable HTML scan report. Publish it to Comma as a private report with anchored comments, so 'that port is the load balancer' is recorded, not re-explained.

# Share an Nmap scan report

Nmap's XML output is machine-readable and unreadable by people; the terminal
output is readable and unpasteable. The bridge has shipped with Nmap for
years and almost nobody uses it — `nmap.xsl`, which turns the XML into a
clean HTML report with the host table, open ports, and detected service
versions.

The reason nobody uses it is not the stylesheet. It is that the HTML has
nowhere to go afterwards, and a network scan is the last artifact anyone wants
to drop into a chat window.

## Convert and publish

```bash
nmap -sV -oX scan.xml 10.0.0.0/24
xsltproc scan.xml -o scan.html

curl -fsS -X POST "https://commareports.com/api/v1/reports" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html scan.html \
        --arg title "Perimeter scan — $(date +%Y-%m-%d)" \
        '{title: $title, html: $html, visibility: "private"}')"
```

Scan only what you are authorized to scan. Assuming that box is ticked, the
sharing question is the one this page is about.

**Set visibility first, not later.** An open-port inventory with service
versions is a starting point for an attacker. Create it `private`, then widen
to your team, an email domain, or named reviewers. See the
[sharing model](/docs/sharing).

## The findings that are not findings

Most of a recurring scan is noise you already understand — and understand
_again_, from scratch, every quarter, because the explanation lived in a
DM. Anchored comments fix exactly that:

- **"tcp/8443 — internal load balancer, expected."**
- **"This host is decommissioned, DNS record is stale, ticket OPS-88."**
- **"New since last scan. Nobody owns this. Escalating."**

Each one attaches to the host it is about and survives the next scan, because
revisions carry the threads forward. See
[commenting on HTML](/comment-on-html).

## Recurring scans

```bash
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html scan.html '{html: $html}')"
```

One report id per scope. The diff between two revisions answers the only
question a recurring scan exists to answer: _what is open now that was not
open before._ A [scheduled routine](/features/routines/scheduled-html-reports)
can run it without anyone remembering to.

## Limits

- **HTML body: 5 MB.** A `/16` with service detection will exceed it — split
  by subnet and publish one report per scope, which is how you would want to
  read it anyway.
- **Scripts run, sandboxed:** `allow-scripts`, no `allow-same-origin`.
- **60 requests/minute per token.**

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision
history.

**[Publish a scan report →](https://commareports.com/)**

### Related

- [Share a ZAP report](/share-zap-report) · [Share a Trivy report](/share-trivy-report)
- [Share a security scan report](/share-security-scan-report) · [Share a Dependency-Check report](/share-dependency-check-report)
- [Security at Comma](/security) · [Scoped tokens](/docs/api-tokens)
