Skip to content

Improve reverse proxy forbidden page to match connection error UI#6488

Open
chiqors wants to merge 4 commits into
netbirdio:mainfrom
chiqors:forbidden-page-ui
Open

Improve reverse proxy forbidden page to match connection error UI#6488
chiqors wants to merge 4 commits into
netbirdio:mainfrom
chiqors:forbidden-page-ui

Conversation

@chiqors

@chiqors chiqors commented Jun 20, 2026

Copy link
Copy Markdown

Describe your changes

This updates the reverse proxy forbidden page to better match the existing NetBird connection error page visual language.

The forbidden state now uses the same overall structure and styling cues as the connection error page, while keeping messaging specific to private-access / policy-blocked requests.

Main updates:

  • redesign the reverse proxy forbidden page to align with the existing connection error page layout
  • add a policy-focused status flow for the 403 forbidden state
  • keep request ID and timestamp visible on the forbidden page
  • update the embedded proxy web bundle so the new UI is included in the shipped image

Screenshots

Before
current_forbidden

After
image

Issue ticket number and link

N/A

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)
  • This change does not modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — or I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See CONTRIBUTING.md.

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

Why: this is a UI-only change for the reverse proxy forbidden page and does not change configuration, API behavior, or deployment flow.

Docs PR URL (required if "docs added" is checked)

N/A

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced the access-denied experience for forbidden requests with clearer messaging, policy/destination failure indicators, request ID fallback (“Unavailable”), ISO timestamp, and improved refresh/redirect and documentation actions.
  • Bug Fixes

    • Standardized 403 forbidden responses so both security gate failures and IP-restriction denials render the same access-denied page with descriptive details (including consistent request-ID handling when available).

@CLAassistant

CLAassistant commented Jun 20, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 967fbd36-50e4-42ab-9bd7-b7635c38eb9a

📥 Commits

Reviewing files that changed from the base of the PR and between c3f66fe and e3a381c.

⛔ Files ignored due to path filters (1)
  • proxy/web/dist/assets/index.js is excluded by !**/dist/**
📒 Files selected for processing (1)
  • proxy/web/src/ErrorPage.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • proxy/web/src/ErrorPage.tsx

📝 Walkthrough

Walkthrough

The PR adds a dedicated "forbidden" error page variant. A new serveForbiddenPage helper on the middleware centralizes 403 responses by calling web.ServeAccessDeniedPage, which now passes variant: "forbidden" to the template. The frontend ErrorPage routes to a new ForbiddenPage component when this variant is present.

Changes

Forbidden Page Variant

Layer / File(s) Summary
Data contract, template renderer, and middleware helper
proxy/web/src/data.ts, proxy/web/web.go, proxy/internal/auth/middleware.go
ErrorData adds an optional variant field ('connection' | 'forbidden'). ServeAccessDeniedPage passes variant: "forbidden" in template data. Middleware replaces bare http.Error forbidden calls with a new serveForbiddenPage helper that extracts requestID from context and delegates to web.ServeAccessDeniedPage.
ForbiddenPage component and ErrorPage routing
proxy/web/src/ErrorPage.tsx
A new ForbiddenPage component renders the 403 view with policy/destination status cards, generated timestamp, request ID (falling back to "Unavailable"), and refresh/documentation actions. ErrorPage short-circuits to ForbiddenPage when variant === "forbidden"; the existing view's primary button and request metadata rendering are also updated.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • braginini
  • mlsmaycon

Poem

🐇 Hop hop, the door is closed today,
A shield now guards the forbidden way.
No bland "Forbidden" text in plain sight—
A styled 403 shines with delight!
Policy cards and a timestamp neat,
The bunny's access-denied is now complete! 🛡️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: improving the reverse proxy forbidden page UI to align with the connection error page design.
Description check ✅ Passed The description is mostly complete with all major sections filled: changes described, checklist items marked appropriately, and documentation decision explained clearly.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
proxy/web/src/ErrorPage.tsx (1)

124-124: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add fallback for missing requestId to match ForbiddenPage behavior.

The middleware may provide an empty requestId when CapturedData is nil. When requestId is undefined or empty, rendering {requestId} displays nothing, leaving "REQUEST-ID: " with a blank value. ForbiddenPage uses a fallback to "Unavailable" for better UX; the standard error page should do the same for consistency.

🔧 Suggested fix
-          <span className="text-nb-gray-400">REQUEST-ID:</span> {requestId}
+          <span className="text-nb-gray-400">REQUEST-ID:</span> {requestId || "Unavailable"}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@proxy/web/src/ErrorPage.tsx` at line 124, The RequestId display in ErrorPage
does not have a fallback value when requestId is undefined or empty, resulting
in blank text after "REQUEST-ID:". Add a fallback to display "Unavailable" when
requestId is empty or undefined, similar to how ForbiddenPage handles missing
requestId values. Update the JSX expression for requestId to use the logical OR
operator or conditional logic to provide the fallback value for consistency
across error pages.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@proxy/web/src/ErrorPage.tsx`:
- Line 124: The RequestId display in ErrorPage does not have a fallback value
when requestId is undefined or empty, resulting in blank text after
"REQUEST-ID:". Add a fallback to display "Unavailable" when requestId is empty
or undefined, similar to how ForbiddenPage handles missing requestId values.
Update the JSX expression for requestId to use the logical OR operator or
conditional logic to provide the fallback value for consistency across error
pages.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5a2004bc-6d2f-4c4f-8f86-86f51efde455

📥 Commits

Reviewing files that changed from the base of the PR and between 79df060 and c3f66fe.

⛔ Files ignored due to path filters (2)
  • proxy/web/dist/assets/index.js is excluded by !**/dist/**
  • proxy/web/dist/assets/style.css is excluded by !**/dist/**
📒 Files selected for processing (1)
  • proxy/web/src/ErrorPage.tsx

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants