Handling Custom URL Schemes in NW.js with registerProtocolHandler
Learn how to expose app‑specific URLs in NW.js using registerProtocolHandler, with a step‑by‑step example, timing tips, and security trade‑offs.
08 May 2026, 01:31 UTC

The Challenge of App-Specific Navigation
When building a desktop application with NW.js (formerly Node-Webkit), you often need a way to trigger specific application logic via a URL. Whether it is a deep link from an external source or an internal shortcut to load a specific configuration file without using standard http:// or file:// protocols, standard web navigation is often too restrictive.
The solution is custom protocol registration. By defining your own scheme (e.g., myapp://), you can intercept requests and programmatically determine what content to serve or what action to take, all while keeping the logic within the JavaScript layer rather than writing native C++ plugins.
How Protocol Handlers Work
NW.js provides the webContents.registerProtocolHandler API to map a custom URL scheme to a JavaScript function. When the browser engine encounters a URL starting with your registered scheme, it pauses the standard network request and passes the request details to your handler.
The handler receives a request object containing the URL, headers, and query parameters. It must return a Response object. This allows you to dynamically generate HTML, serve a local buffer, or redirect the user based on the query string.
Critical Timing for Registration
A common point of failure is the timing of the registration. The protocol handler must be registered after the webContents object is created but before any navigation to that scheme occurs. If you attempt to navigate to myapp://home before the handler is registered, NW.js will treat it as an invalid URL or attempt to pass it to the system browser.
Implementation Example: A Virtual File System
In this example, we create a local-app:// protocol that serves specific configuration strings based on a query parameter. This avoids exposing the actual absolute file path of the application directory to the frontend.
// This code should run in the background script or main window context // Requirement: NW.js version >= 1.6.0 const { webContents } = require('nw.gui'); // Register the 'local-app' scheme nw.Window.get().window.webContents.registerProtocolHandler('local-app', (request, response) => { const url = new URL(request.url); const resource = url.pathname.replace('/', ''); console.log(`Intercepted request for resource: ${resource}`); if (resource === 'config') { const configData = 'App ConfigurationVersion 1.0.0
'; // The response must include a proper MIME type to avoid blank pages response.write(configData); response.end(); } else { // Handle 404 equivalent for custom protocols response.writeHead(404); response.end('Resource not found'); } }); // To test: Navigate to local-app://config in your app windowExecution Details
- Where to run: Execute this in the main window context or the background script.
- Permissions: Requires standard Node.js integration enabled in the
package.json. - Expected Result: Navigating to
local-app://configshould render the "App Configuration" header instead of a "Page Not Found" error.
Trade-offs and Security Limitations
While custom protocols are powerful, they introduce specific engineering trade-offs:
- MIME Type Sensitivity: If the handler returns data without a clear content-type or fails to call
.end(), the renderer may hang or display a blank page. Always ensure the response is properly closed. - Lack of Sandboxing: Because these handlers run within the Node process, they have full access to the file system and OS. If your application allows dynamic registration of protocols based on user input, you risk creating a security vulnerability where malicious URLs could trigger unauthorized Node.js functions.
- OS Interference: On macOS, registering a scheme that conflicts with a system-level registered URI (like
mailto:ortel:) can cause unpredictable behavior in how the OS launches the application.
Verifying the Implementation
To confirm the protocol is active and functioning correctly, use the following checks:
- Protocol List: Call
webContents.protocolList()in the developer console to verify that your custom scheme is present in the registered list. - Network Inspector: Open the Chrome DevTools Network tab. A successful custom protocol request will show the scheme you defined rather than
http. - Cross-Platform Check: Test the handler on both Windows and Linux; while the API is consistent, the way the OS handles the initial launch via a custom URL (deep linking) varies.
Rollback
To remove a protocol handler, you must restart the application process, as registerProtocolHandler does not currently provide a corresponding unregister method for the same webContents instance.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.