A Chromium extension (Chrome, Vivaldi, Edge, Brave): pressing Back when the tab has no back history left closes the tab instead of doing nothing.
Get it on the Chrome Web Store — works in Chrome, Vivaldi, Edge and Brave.
Or run it from source:
- Open
vivaldi://extensions(orchrome://extensions) - Turn on Developer mode (top right)
- Load unpacked → pick this folder
No further configuration. ⌘←, ⌘[ and the mouse back button all work as they
are — you do not need to rebind anything in Vivaldi.
Extensions get no access to the browser's own UI, so:
| Back trigger | Works? |
|---|---|
| Mouse back button over a page | Yes |
⌘← while a page has focus |
Yes |
⌘[ while a page has focus |
Yes |
| Extension shortcut you assign yourself | Yes, on every page |
| Toolbar ‹ button | No — extensions cannot see clicks on browser UI |
| Two-finger swipe back | No — the gesture never reaches the page |
The first three need a page in focus, and cannot work where extensions are not
allowed to run: vivaldi://…, the Chrome Web Store, the PDF viewer, the start
page. Assigning a shortcut (below) covers those.
If nothing happens for you, the likely reason is that you are using the toolbar
button or the trackpad swipe. Check with the mouse button or ⌘← on an ordinary
web page first.
Mouse software often intercepts the thumb buttons and replays something else instead of passing the raw button through, so the page never sees a back button at all. Logitech Options+ does this: its Back preset for a button is a macro playback, not a passthrough.
Check what your button actually emits — open any page's console and run
addEventListener('mousedown', e => console.log('mousedown button', e.button))
addEventListener('keydown', e => console.log('keydown', e.key, 'meta=' + e.metaKey))then press the button. button 3, or a ⌘[ / ⌘← keydown, are all handled.
If nothing is logged, your mouse software is sending something the page cannot
observe.
Options+ applies a per-application profile, so fix it for Vivaldi only:
- Open Options+ → add an application profile for Vivaldi
- Assign the back thumb button the keystroke
⌘[
That replaces the opaque Back macro with a real keystroke, which the page
receives as a normal keydown and the extension handles. Nothing else to
configure — no extension shortcut, no instant mode.
Confirmed working on a Logitech M720 Triathlon.
If your mouse software has no per-app profiles, or the above is not available:
- In your mouse software, assign the button a custom keystroke that Vivaldi
does not already use —
⌃⌥⌘Bis a safe choice. - In
vivaldi://extensions/shortcuts, assign that same combo to Back to Close Tab. - In the extension's options, turn on Assigned shortcut responds instantly.
This path does not depend on the page seeing anything, so it also works on
vivaldi:// pages, the start page and the PDF viewer.
The extension ships with no key bound, on purpose — auto-claiming a key
Vivaldi already uses makes both act, which sends you back two pages. To assign
one, open vivaldi://extensions/shortcuts and set a key on Back to Close Tab.
Assigning a key Vivaldi already uses for Back is safe: Vivaldi navigates, the extension notices and stands down. Nothing needs to be unbound.
Right-click the extension → Options:
- Mouse back button — on
- In-page ⌘[ / ⌘← — on
- Never close pinned tabs — on
- Never close the last tab in a window — off (so the window closes with it)
- Only close tabs opened from another tab — off
- Assigned shortcut responds instantly — off. Turn on only when the key you assigned is not also a Vivaldi shortcut (see above); it removes a ~0.25s delay.
A closed tab comes back with ⌘⇧T.
The extension never guesses whether back history exists — a wrong guess closes a
tab that should have navigated. (history.length cannot tell "no back history"
from "already went back", and navigation.canGoBack reports false for every
cross-origin entry, so both would close tabs constantly.) Instead:
- A page-level listener notices the back gesture and reports it. It never calls
preventDefault(), so Vivaldi's own back behaviour is untouched. - The service worker watches that tab for ~250 ms (
WATCH_MSinbackground.js). If a top-frame navigation starts, Vivaldi handled it and the extension stops there. This is what makes double-handling impossible. - If nothing navigated, it calls
chrome.tabs.goBack(). That call is the ground truth: it navigates if there was history, and rejects with "Cannot find a next page in history" if not — the only case that closes the tab.
Step 3 also covers the reverse: if Vivaldi did not act on the gesture at all, the extension performs the back navigation itself.
./test/test.shLaunches Vivaldi with a throwaway profile (your real profile is untouched),
side-loads the extension over the CDP pipe, and drives real input events against
a local test site. BROWSER_BIN=/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
switches browsers — though Chrome 137+ blocks the side-load path this uses.
There is a second, optional suite that drives the browser with real macOS input events (CGEvent) rather than CDP injection — the same layer a mouse driver operates at, and below the browser's own shortcut handling, which CDP injection bypasses:
./test/os-input/run.shIt needs Accessibility permission for your terminal. It is what caught the
mouse-hold bug below, so it is worth running after changes to content.js.
Verified 18/18 in both Vivaldi 8.1.4087.68 (Chromium 150) and Chrome 151.0.7922.138, plus 8/8 real-input in each:
PASS page receives button-3 mousedown
PASS page receives Cmd+Left keydown
PASS Cmd+Left with history goes back EXACTLY one page
PASS mouse back with history goes back EXACTLY one page
PASS mouse back, no history -> tab closes
PASS back to first entry then back again -> tab closes
PASS SPA pushState back navigates in-document, no close
PASS with in-page key trigger OFF, Cmd+Left still closes (= command path)
PASS Cmd+[ with history goes back EXACTLY one page
PASS Cmd+[ with no history -> tab closes
PASS pinned tab with no history is NOT closed
PASS registered command shortcut
PASS mouse back held 50ms goes back EXACTLY one page
PASS mouse back held 300ms goes back EXACTLY one page
PASS mouse back held 700ms goes back EXACTLY one page
PASS mouse back held 1500ms goes back EXACTLY one page
PASS command path: guarded mode closes a no-history tab
PASS command path: instant mode goes back one page
and with real OS-level input, 8/8 — including mousedown button=3, ⌘[ and
⌘← all reaching the page, and each closing a no-history tab.
The browser navigates back on mouse up. Timing the watch window from mouse down meant any press held longer than 250 ms expired the window while the button was still down, so the extension navigated and then the browser did too:
| hold | result before the fix |
|---|---|
| 50 ms | one page back |
| 300 ms | two pages back |
| 700 ms | two pages back |
A deliberate press easily exceeds 250 ms, so this hit constantly. The gesture is now reported on release, with the press timestamp sent along so the worker also recognises a navigation that happened during the hold.
./build.shproduces dist/back-to-close-tab-<version>.zip — runtime files only, no tests or
docs. See store/STORE.md for the listing copy, the permission
justifications, and the submission checklist.
The extension requests only tabs, webNavigation and storage. It does not
declare host_permissions: chrome.tabs.goBack() works without it (verified by
the suite passing with it removed). Broad host access still appears at install,
because the content script matches <all_urls> — that is unavoidable for an
extension that must notice a gesture on any page.
manifest.json— MV3 manifest, permissions, commandbackground.js— navigation bookkeeping and the back-or-close decisioncontent.js— in-page gesture detection (observe only)options.html/options.js— settingstest/— CDP end-to-end harnesstest/os-input/— optional real-macOS-input harnessbuild.sh— produces the Web Store upload zipstore/— listing copy, permission justifications, screenshotPRIVACY.md,LICENSE