# Password-Protect an HTML Report (and Why You Often Shouldn't)

Canonical: https://commareports.com/password-protect-html-report
Published: 2026-08-30

> A password on a static HTML file is decoration — the JavaScript check ships with the page. What actually restricts a report: identity-based access, expiring links, and a server-side password gate.

# Password-protecting an HTML report

The usual version of this doesn't work, and it's worth being precise about
why before reaching for the thing that does.

## A password inside the HTML is decoration

Every "password protect your HTML page" snippet does the same thing:

```html
<script>
  const pw = prompt("Password?");
  if (pw !== "hunter2") document.body.innerHTML = "";
</script>
```

The check runs **in the browser you are trying to keep out**. The password
is in the source. The content is already in the source. Disable
JavaScript, or press Ctrl-U, and the gate is gone.

The encrypted variants — where the page ships ciphertext and derives a key
from the typed password — are better in that the content genuinely isn't
readable without the passphrase. They are still one shared secret, handed
to everyone, revocable only by re-encrypting and re-sending. Fine for a
single file to a single recipient; not an access model.

## What you usually actually want

Nine times in ten, "password-protect this" means **"not everyone should be
able to open this"** — and identity answers that better than a secret
does, because it survives forwarding and it tells you who saw what.

Comma sets visibility per report:

| Level        | Who can open it                        |
| ------------ | -------------------------------------- |
| `private`    | You and people you invite by name      |
| `team`       | Members of the report's team           |
| `registered` | Any signed-in Comma user with the link |
| `public`     | Anyone with the link, no account       |

Independently, the link carries a permission — `view`, `comment`, `edit`,
or `none` — and individuals can hold a role (owner, admin, editor,
commenter, viewer) that wins over whatever the link grants.

All of the above is on the **free tier**. A private report shared with
three named reviewers is not a lesser version of a password; it is the
thing a password was a bad approximation of.

## When you do want a password or an expiry

Two gates are **Enterprise**:

- **Password-protected links** — a password required in addition to the
  link, checked server-side, where the content is not sent until it
  passes. This is the real version of the snippet at the top of the page.
- **Expiring links** — links that stop working on a date you pick. Useful
  for a report handed to an auditor, a client, or a candidate.

**Domain-gated viewing** is Enterprise too: `domain` visibility checks the
viewer's verified email domain server-side, so everyone at your company
can read it with no per-viewer invites. Full model in
[sharing & access control](/docs/sharing).

## When the content is the secret

If the report shouldn't be readable by the server that stores it — not
just by other users — use a **zero-knowledge encrypted report**. It is
encrypted in your browser with a key derived from your passphrase, and the
server keeps only ciphertext.

The tradeoff is deliberate and worth stating plainly: encrypted reports
can't be link-shared, scheduled, or sent to team destinations. The server
can't read them, so it can't serve them to anyone but you. See the
[security model](https://commareports.com/security).

## Setting it from a script

Access is writable over the API under a dedicated `sharing` scope, so a
token that publishes reports can't quietly widen who can see them:

```bash
curl -fsS -X PUT "https://commareports.com/api/v1/reports/$REPORT_ID/share" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "visibility": "team", "public_permission": "none" }'
```

Agents do the same through the MCP tool `set_report_sharing`. Reports
created over the API start `private` unless the create call says
otherwise — see [API tokens](/docs/api-tokens) and the
[API reference](/docs/api).

## What about basic auth on my own server?

It is a genuine server-side check, so it beats the JavaScript prompt by a
wide margin. It also leaves you with one shared credential to distribute
and rotate by hand, no record of who opened what, and no way to remove one
person's access without changing it for everyone.

That is the whole argument for identity over secrets, in one paragraph.

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited
revision history. Password, expiry and domain gates are Enterprise.

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

### Related

- [Sharing & access control](/docs/sharing) · [Security model](https://commareports.com/security)
- [Share an HTML file with a client](/share-html-file-with-client)
- [Cloudflare Access alternatives](/alternatives/cloudflare-access-alternatives)
- [SharePoint and OneDrive HTML previews](/sharepoint-html-preview)
