Skip to main content

Disable right-click and DevTools detection are not JavaScript protection

Right-click blockers, Ctrl+U traps, and DevTools detection annoy honest users and do not hide your JavaScript. The file is already on the network. This article explains the usual bypasses and what actually raises the cost of copying client code.

Updated 5 min readMarkdown

Everyone has seen the snippet. document.addEventListener("contextmenu", e => e.preventDefault()). A keydown handler that looks for F12, Ctrl+Shift+I, or Ctrl+U. A debugger loop. A full-page message: "DevTools is open — this site is disabled."

These tricks annoy honest users and do nothing to a person who wants your code. This article explains why, and what actually raises the cost of copying client JavaScript.

What these snippets actually do

Right-click blocking stops the context menu. "View source" blocking tries to stop Ctrl+U. DevTools detection tries to notice that the debugger is attached, then redirect, clear the DOM, or sit in an infinite debugger statement.

All of that is user-interface theater. It assumes the attacker is a person clicking around in a normal Chrome window, and that if you make that window unpleasant they will go away.

They will not, because they do not need that window.

The file is already on the network

Your JavaScript arrives in HTTP responses. Anyone can:

  • Open DevTools before navigating, or use a browser with DevTools already undocked.
  • curl the script URL taken from the HTML.
  • Use a proxy (mitmproxy, a local interceptor) or a Service Worker they control.
  • Open the page in Firefox or Safari if your detection is Chrome-specific.
  • Disable JavaScript, read the <script src>, then fetch the file.
  • Install a userscript or an extension that strips your handlers before they run.

A sketch of how little the context-menu handler matters:

// this stops a context menu, not a download
document.addEventListener("contextmenu", (e) => e.preventDefault());

// the bundle is still listed as a resource
performance.getEntriesByType("resource")
  .filter((e) => e.name.includes(".js"))
  .forEach((e) => console.log(e.name));

Or, from outside the page:

curl -sS "$PAGE_URL" | grep -oE 'src="[^"]+\.js[^"]*"'
curl -sS "$SCRIPT_URL" -o bundle.js

No right-click required. No DevTools required.

DevTools detection is a cat-and-mouse game you lose

Common tricks:

  • Measure time around a debugger statement and assume a pause means DevTools is open.
  • Look at outerWidth - innerWidth, or fire a getter on console.debug.
  • Watch element-size heuristics that supposedly detect the docked panel.

Every one of these has a bypass: a detached DevTools window, a different browser, a Chrome DevTools Protocol session with no UI, Playwright or Puppeteer, or simply patching the detector because the detector is itself JavaScript in the bundle. If your defense is a function in the page, the analyst comments it out.

Infinite debugger statements are likewise a setting away from being ignored — the "Never pause here" checkbox, or a CDP session that does not surface the pause. They also punish your own support and QA people.

What these tricks cost you

Right-click and shortcut traps break ordinary use. Password managers paste from a context menu. Accessibility tools and input methods rely on those menus. Developers on your own team cannot inspect a customer bug. You train users to think your site is broken or hostile, and you have not hidden a single byte of the bundle.

If you ship this class of script, delete it. It is not a layer of defense. It is noise.

What actually raises the cost

Assume the attacker already has the file. Design from that.

  1. Do not put secrets, private API keys, or authoritative pricing in client JavaScript. The server is the authority.
  2. Turn production source maps off the public CDN, or upload them only to a private crash tracker.
  3. Minify production bundles. That is a size optimization, not protection, but it is the right default.
  4. For proprietary client logic that must run in the browser — a preview engine, an offline solver, a license mapper — use real code protection: identifier renaming and string encoding as a floor, and VM-style protection for the small set of functions that would hurt you if they were copied.
  5. Leave honest users their right-click, their paste, and their keyboard shortcuts.

A JS VM obfuscator compiles selected functions to a custom bytecode and ships a small interpreter, so the original control flow is not sitting in the pretty-printed chunk. That does not make the logic a secret. It makes casual copy-paste and wholesale reuse more expensive, which is the honest goal.

VEILVM is a browser-based JSVMP workspace you can point at one extracted function before you wire protection into CI. Interpreted code is slower, so protect the sensitive function and leave the rest of the page alone.

Checklist

  • No secret or authoritative check lives only in the client.
  • You do not rely on contextmenu, keydown traps, or DevTools detection as protection.
  • Production source maps are private.
  • You can name the few functions that deserve more than minification.
  • Honest users can still right-click, paste, and use assistive tools.
  • End-to-end tests run against the production build, including protected functions.

If you want to try VM protection on a single extracted function, VEILVM runs in the browser with no sign-up: 3 free runs a day, up to 5 KB per file (resets at 00:00 UTC). A free account raises the input limit to 30 KB. Pro is $9/month or $79/year and adds an API so the protection step can run in CI. Standard requests are processed in memory and are not written to logs. Start at https://vm.ldvmp.com.

No comments yet. Be the first to comment.