anthropics / anthropics/claude-code

[FEATURE] Desktop browser preview: auto-allow RFC 6761 reserved TLDs (*.test) that resolve to loopback, like *.localhost

Open
#92,698 2 comments 1 reaction 0 assignees View on GitHub
area:desktop area:permissions enhancement platform:windows
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

### Preflight Checklist

- [x] I have searched [existing requests](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20label%3Aenhancement) and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)

### Problem Statement

The Claude Desktop app (Code tab) built-in Browser pane treats only `localhost`, `*.localhost`, `127.0.0.1` and `::1` as local dev servers. Every other origin goes through the "allow this site" flow, and on top of that through a second, separate gate for private-network read tools (`read_page`, `get_page_text`, screenshots).

Laravel Herd, Valet and Laragon serve **every** site on `*.test` by default. That is the standard local PHP/Laravel/WordPress setup on macOS and Windows. `.test` is a TLD reserved by [RFC 6761](https://www.rfc-editor.org/rfc/rfc6761#section-6.2), exactly like `.localhost`, and can never be a routable public domain.

In practice this means that for each project I have to:

1. Approve navigation to `https://myapp.test` ("Always allow").
2. Approve it again for `http://myapp.test` if the redirect or a link uses plain http (scheme counts as a separate origin).
3. Approve the private-network read gate separately, otherwise `read_page` / screenshot fail with "This site requires per-action approval; Browser read tools are not available on it".

Each approval lands as a separate literal entry in `claude_desktop_config.json`. After a few projects my `preferences` block looks like this (real, trimmed):

```json
"launchPreviewAllowedOrigins": [
"https://app-one.test",
"http://app-two.test",
"https://app-three.test",
"https://app-four.test",
"http://app-four.test",
"https://app-five.test"
],
"launchPreviewPrivateNetworkReadOrigins": [
"https://app-four.test",
"https://app-one.test"
],
"launchPreviewPrivateNetworkReadPins": {
"https://app-four.test": ["127.0.0.1"],
"https://app-one.test": ["127.0.0.1"]
}
```

Note the `Pins` block: the app **already resolves these origins and pins them to `127.0.0.1`**. So the loopback check exists, it just is not used to short-circuit the permission flow.

The alternative, switching Herd to `*.localhost`, is a global setting that affects every site on the machine and breaks tooling that assumes the Herd default (`herd link`, `herd secure`, shared `.env` files across a team).

### Proposed Solution

Any one of these would solve it; the first is the cleanest:

1. **Auto-allow reserved local TLDs pinned to loopback.** On first visit, if the host ends in `.test` / `.localhost` and resolves to a loopback address (`127.0.0.0/8`, `::1`), allow it silently **and record the resolved address in `launchPreviewPrivateNetworkReadPins`**, exactly as the manual "Always allow" flow does today. On every later visit require both conditions: the host still resolves to loopback **and** the result matches the stored pin. Any drift (different address, non-loopback, NXDOMAIN) drops the automatic trust and falls back to the current prompt. The pin mechanism already exists; this only changes who creates the first pin (the app instead of a click). Trusting the live lookup alone would remove the one check that catches a `.test` name being re-pointed later.
2. **Wildcard / pattern support** in `launchPreviewAllowedOrigins` and `launchPreviewPrivateNetworkReadOrigins`, e.g. `"https://*.test"`, `"*://*.test"`.
3. **A trusted-local-TLD preference**, e.g. `"launchPreviewTrustedLocalTlds": ["test"]`, applied with the same loopback check as in (1).

Also, regardless of the above: an "Always allow" approval for one origin should cover both the navigation gate and the private-network read gate, and `http`/`https` for the same host. Today they are three independent prompts, which is what makes this feel broken rather than merely strict.

### Alternative Solutions

- **Manually editing `claude_desktop_config.json`** while the app is closed, adding the origin to both lists plus a `127.0.0.1` pin. Works, but is undocumented, fragile, and has to be repeated per project and per scheme.
- **Herd on `*.localhost`**: global change, breaks team conventions and Herd defaults, see above.
- **`.claude/launch.json` with `"url": "https://myapp.test"`**: still hits both prompts; `launch.json` only auto-trusts `localhost` origins.
- **Using Claude in Chrome instead of the built-in pane**: loses `preview_start` / `preview_logs` integration and the whole point of the built-in Browser pane.

### Priority

High - Significant impact on productivity

### Feature Category

Configuration and settings

### Use Case Example

1. `herd link` in `D:\Herd\myapp` → site is served on `https://myapp.test`, resolving to `127.0.0.1`.
2. Ask Claude Code Desktop to fix a layout bug and verify it in the preview.
3. Claude runs `preview_start` with the `.test` URL → prompt 1 (navigate). Approve.
4. Claude runs `read_page` / screenshot → prompt 2 (private-network read). Approve.
5. Page redirects to `http://` once → prompt 3.
6. Next project, repeat from step 3.

With the proposal: step 3 onward is silent, same as it is today for `http://localhost:3000`.

### Additional Context

- Related: #91495 (open, `area:desktop` / `area:permissions`, same Herd `*.test` scenario, per-call prompts), #78315 (closed as invalid, describes the navigate-vs-read two-gate inconsistency), #27263 (closed as completed, external URL whitelist).
- Docs: https://code.claude.com/docs/en/desktop.md, section "Open the preview at a specific URL", lists the four auto-allowed local patterns.
- RFC 6761 §6.2 reserves `.test` for testing; §6.3 reserves `.localhost`. Browsers already special-case `.localhost` as loopback (Chrome resolves `*.localhost` to loopback without DNS). `.test` is the de-facto equivalent for local PHP tooling.
- Environment: Windows 11 Pro 10.0.26220, Claude Desktop (Code tab, built-in browser), Claude Code CLI 2.1.250, Laravel Herd (Windows) serving `*.test`.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the desktop.md section on opening previews at a specific URL, then trace the preview_start, read_page, and screenshot permission flows. Compare the existing localhost handling with launchPreviewAllowedOrigins, launchPreviewPrivateNetworkReadOrigins, and launchPreviewPrivateNetworkReadPins. Done means loopback-resolved .test origins follow the agreed trust and pin behavior without weakening fallback prompts.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
desktop, networking, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.