Inbox refuses all reverse-proxy requests: x-forwarded-for treated as tailnet exposure (breaks local proxies)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- node.js, typescript
Research direction
Start by locating the inbox request handler that defines SERVE_HEADERS and calls localOnly(), then read how the Host-header guard and tailnetPublished state are used. Reproduce the reported 403 with an x-forwarded-for header on a loopback request and compare it with a request without that header. Done when local proxy requests work while the tailnet protection still behaves as intended.
Written by the indexing model from the issue text.
Description
What happened
Since the 0.28.9+ security hardening ("hostname check on every server" / tailnet detection), the Inbox's localOnly check treats any request carrying an x-forwarded-for header as tailnet exposure and refuses it:
var SERVE_HEADERS = ["tailscale-headers-info", "tailscale-user-login", "x-forwarded-for"];
// ...
if (SERVE_HEADERS.some((name) => req.headers.has(name))) {
tailscale.noteServedRequest();
return Promise.resolve(localOnly());
}
Response: {"error":"This answers only on this computer, not over the tailnet.","code":"local_only"} (403).
Why this is a problem
Every reverse proxy adds x-forwarded-for — it's standard proxy behavior, not a Tailscale signature. A purely local proxy chain (loopback → local HTTPS front-end → loopback inbox) gets 403'd even though the request never left the machine. This breaks:
- local HTTPS front-ends with named domains (e.g. portless
.localhostaliases — my case:inbox.localhost→127.0.0.1:<port>) - nginx/caddy local setups
- any dev server proxying to the inbox
Verified by sending x-forwarded-for: 1.2.3.4 on an otherwise plain loopback request — 403 local_only; without the header, 200.
Suggested fix
The two tailscale-* headers are genuinely Tailscale-serve-specific and are the right signal. x-forwarded-for alone is not — it only becomes meaningful when tailscale serve is the thing adding it. Options:
- Drop
x-forwarded-forfromSERVE_HEADERS(the Host-header guard already protects loopback-only endpoints). - Only treat
x-forwarded-foras tailnet-exposure when tailscale is actually publishing (the inbox already trackstailnetPublishedstate). - Check that
x-forwarded-for's value is a non-loopback address (tailscale serve forwards the real client IP; a local proxy forwards127.0.0.1).
Workaround in the meantime
A tiny local proxy that strips x-forwarded-for (and the other forwarded headers) in front of the inbox.
Versions: plannotator 0.28.16 (darwin-arm64), inbox started via plannotator inbox --background.
- Dominant language
- TypeScript
- Stars
- 8.7k
- Forks
- 649
- Avg merge
- 5h 5m
- Merged PRs (30d)
- 325
Getting set up
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from backnotprop/plannotator
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
backnotprop/plannotator#1932 ·
Maintainers usually reply within 1 day
-
Clicking a `file.ts:42` code reference opens the file at the top, not at line 42Possibly taken @Totopo27 claimed this 1 day ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
backnotprop/plannotator#1784 ·
Maintainers usually reply within 1 day
-
Claude Code: plan annotations are silently dropped when the plan is approved (approve path never sends feedback to the agent)Possibly taken @mangxy claimed this 44 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
backnotprop/plannotator#1359 ·
Maintainers usually reply within 1 day
-
Annotate documentation omits per-file version history and privacy controlsMay be free again A pull request for this issue was closed without being merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
backnotprop/plannotator#1086 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
backnotprop/plannotator#1069 ·
Maintainers usually reply within 1 day
All issues in backnotprop/plannotator
Similar issues
-
test: make the #3408 Claude OAuth token fixture obviously fake so secret scanners stop flagging itOpen
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
code-yeongyu/senpi#3436 ·
Maintainers usually reply within 1 day
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 78/100
lingdojo/kana-dojo#32505 · 1 comment · 5 reactions ·
Maintainers usually reply within 1 day
-
code-quality refactoring
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
github/gh-aw-firewall#10040 ·
Maintainers usually reply within 1 day
-
review-followup
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
griffinwork40/agent-afk#3868 ·
Maintainers usually reply within 1 day
-
area: import-export bug status: needs-triage
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
amatya-aditya/obsidian-rss-dashboard#1191 ·
Maintainers usually reply within 1 day