Skip to content

fix: serve the Vite DevTools WebSocket on Nuxt's HTTPS listener - #1156

Merged
antfu merged 1 commit into
mainfrom
fix/https-devtools-ws
Nov 2, 2026
Merged

antfu merged 1 commit into
mainfrom
fix/https-devtools-ws

Conversation

@antfubot

@antfubot antfubot commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

With devServer.https on Nuxt 4, DevTools never connects (#1152). Nuxt runs Vite in middleware mode, so viteServer.httpServer is null. Vite DevTools then opens its RPC WebSocket on a plain-HTTP side-car port (9777/9778). The HTTPS page connects with wss://localhost:9778/__ws, and the TLS handshake fails. The client never reaches the authorization step, so no code is printed.

Nuxt 5's vite-builder already sets httpServer to Nuxt's listener (nuxt:dev-listener). This PR adds the same step for Nuxt 4, in a new nuxt:devtools:dev-listener Vite plugin (packages/devtools/src/utils/dev-listener.ts):

  • On the client Vite server, when httpServer is unset and nuxt._devServerListener is a TLS server, it hands the listener over. Vite DevTools then attaches its socket to the page's own origin and TLS, and advertises { "path": "/__devtools/__ws" } instead of a port.
  • nuxi forwards every upgrade outside buildAssetsDir to nuxt.server.upgrade. On Nitro 2, that proxies the socket to the Nitro worker, which ends it under crossws. The result is ERR_STREAM_WRITE_AFTER_END and a Nuxt restart loop. I hit this while testing a version without the guard. The plugin wraps nuxt.server.upgrade once so it skips /__devtools/ paths.

Plain-HTTP listeners are not changed and keep the side-car, which works for them today. On Nuxt 5 the plugin does nothing, because httpServer is already set.

fixes #1152

Verification

  • pnpm lint: passes.
  • pnpm typecheck: passes.
  • pnpm test:unit: 21 files, 129 tests pass. This includes the new packages/devtools/test/dev-listener.test.ts, which covers: the HTTPS handover, Nitro skipping only DevTools upgrades, wrapping once across Vite restarts, plain HTTP left on the side-car, and the SSR server / existing httpServer / no listener left alone.
  • Manual check with a scratch app using nuxt@4.6.3, @nuxt/devtools@4.0.0 and devServer: { https: true }:
    • Before: /__devtools/__connection.json advertised {"port":9777,"path":"__ws"}.
    • After, with this change patched into the installed dist: it advertised {"path":"/__devtools/__ws"}.
    • A wss://localhost:3443/__devtools/__ws upgrade returned 101, and Vite HMR still upgraded on /_nuxt/.
    • In a browser, opening the dock printed the authorization code in the terminal. After authorizing, the Nuxt DevTools panel loaded. There was no restart loop.
  • Not verified: Nuxt 4 with Nitro v3, and Nuxt 5 running Nitro outside Vite. In the second case nuxt.server still has upgrade, but this plugin does nothing there because Nuxt 5 sets httpServer itself.

Written with the help of Claude Code.

Closes #1152

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: edf006e2-025c-4301-a994-15424be93078
Commits

Reviewing files that changed from the base of the PR and between d6a282e and 7f9671c.

Files selected for processing: 3
  • packages/devtools/src/module-main.ts
  • packages/devtools/src/utils/dev-listener.ts
  • packages/devtools/test/dev-listener.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.

Walkthrough

The module registers a serve-only Vite plugin that can assign Nuxt’s TLS listener to Vite and filter DevTools-path upgrade requests. Other upgrade requests are forwarded to Nuxt’s existing handler. Tests cover the HTTPS and upgrade behaviour, repeated configuration, and cases where the listener is not assigned.

Estimated code review effort: 2 (Simple) | ~15 minutes

Severity of issue fixed: Medium

Merge Risk: Minimal · up to 7f967

DevTools can use the HTTPS listener; no material merge risk was established.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title names the main change: the Vite DevTools WebSocket is now served on Nuxt's HTTPS listener. The changeset adds a Nuxt 4 Vite plugin (dev-listener.ts) that hands the TLS listener to Vite and f…
Description check Passed The description relates directly to the changeset. It explains the root cause (middleware-mode Vite with a null httpServer, and a plain-HTTP side-car port that breaks the wss:// connection), describes…
Linked Issues check Passed [#1152] Objective: with devServer.https on Nuxt 4, Vite DevTools must serve its RPC WebSocket on the page's own HTTPS origin, so the client can connect and reach authorization. Implementation at the r…
Out of Scope Changes check Passed The changes are limited to three files: packages/devtools/src/utils/dev-listener.ts (new plugin and upgrade filter), packages/devtools/src/module-main.ts (registration of the plugin with { server: fal…
Full details: Title check

Explanation

The title names the main change: the Vite DevTools WebSocket is now served on Nuxt's HTTPS listener. The changeset adds a Nuxt 4 Vite plugin (dev-listener.ts) that hands the TLS listener to Vite and filters DevTools upgrades in nuxt.server.upgrade. The title uses the 'fix:' prefix, which fits the linked bug (#1152). It is short, specific and free of file lists or vague terms.

Full details: Description check

Explanation

The description relates directly to the changeset. It explains the root cause (middleware-mode Vite with a null httpServer, and a plain-HTTP side-car port that breaks the wss:// connection), describes the new dev-listener plugin and its guards, lists verification steps, and links issue #1152. It matches the code and tests in the changeset.

Full details: Linked Issues check

Explanation

[#1152] Objective: with devServer.https on Nuxt 4, Vite DevTools must serve its RPC WebSocket on the page's own HTTPS origin, so the client can connect and reach authorization. Implementation at the reviewed head: packages/devtools/src/utils/dev-listener.ts adds the nuxt:devtools:dev-listener plugin. In its configureServer hook (order 'pre'), it assigns nuxt._devServerListener to server.httpServer when the build is not SSR, Vite has no httpServer, and the listener is a TLS server. module-main.ts registers the plugin with { server: false }. The plugin also wraps nuxt.server.upgrade so that upgrades under DEVTOOLS_MOUNT_PATH are not forwarded to Nitro. This prevents the Nitro proxy from ending the DevTools socket. Plain-HTTP listeners keep the existing side-car behaviour, which the issue does not require to change. The PR reports a manual check on nuxt@4.6.3 and @nuxt/devtools@4.0.0: the connection endpoint advertised {"path":"/__devtools/__ws"}, a wss upgrade returned 101, and the dock connected after authorization. The PR also reports unit tests for the handover and upgrade filtering. I did not re-run these checks. The assessment relies on the source read and the author's reported results. Nuxt 4 with Nitro v3 is not verified, but the issue does not name that configuration.

Full details: Out of Scope Changes check

Explanation

The changes are limited to three files: packages/devtools/src/utils/dev-listener.ts (new plugin and upgrade filter), packages/devtools/src/module-main.ts (registration of the plugin with { server: false }, with comments referencing #1152), and packages/devtools/test/dev-listener.test.ts (unit tests for the new plugin). Each change connects directly to the #1152 objective. The upgrade wrapper is needed for the handover to work on Nuxt 4 because nuxt.server.upgrade would otherwise end the DevTools socket. The test file supports the new plugin. No unrelated behaviour change is visible in the reviewed sources. The module-main.ts diff is the only change to existing code, and it adds a single registration line plus comments.

Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 3 functions across 3 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@antfu
antfu merged commit 47ed265 into main Nov 2, 2026
9 checks passed
@antfu
antfu deleted the fix/https-devtools-ws branch November 2, 2026 03:41
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.

DevTools v4 can’t connect when the Nuxt dev server uses HTTPS

2 participants