GitHub - taciturnaxolotl/unduckified: A fast, local-first "redirection engine" for !bang users with a few extra features ^-^

GitHub

7 min read Original article ↗

dark and light modes of the app

This is a fork of t3dotgg/unduck. Check out Theo's hosted version at unduck.link for the original experience.

Quick Start

Add this URL as a custom search engine to your browser to use DuckDuckGo's bangs, but faster:

https://s.dunkirk.sh?q=%s

How is it that much faster?

DuckDuckGo does their redirects server side. Depending on how fast your internet connection is this can mean a network round trip that could cause a noticable delay vs directly loading the other website.

This is solved by doing all of the work client side. Once you've went to https://s.dunkirk.sh once, the JS is all cached and will never need to be downloaded again. Your device does the redirects, not unduck or any other server.

The exception is the very first search on a new browser profile, which has no Service Worker yet. That one navigation already carries your query to the edge in order to ask for the page, so it gets answered there with a redirect instead of a page plus a 189 KiB catalog download. The worker installs during that same visit and takes over from the second search onward. If you have custom bangs or a custom default provider, the edge steps aside and your browser resolves even that first search, since your settings never leave your device.

Performance

A Service Worker intercepts the search before any page loads, so once the bang catalog is cached the redirect resolves on your device in about half a millisecond with no network at all, and the very first search of a profile is answered at the edge instead of waiting on a catalog download. Here are a few comparison benchmarks: median time from pressing Enter to the redirect committing, all measured the same way (see BENCHMARK.md). Absolute numbers vary a lot with your distance to each service's servers and your hardware but the ordering should be fairly stable.

tool how it redirects warm search worker restart cold first search bytes to first redirect
unduckified Service Worker, plus an edge redirect for the first search ~0.47 ms ~5.0 ms ~73 ms ~0.8 KiB
flashbang Service Worker + on-demand shards ~0.45 ms ~4.1 ms ~111-134 ms ~4-11 KiB
unduck (Theo's original) page load, then JS redirect ~33 ms n/a ~211 ms ~472 KiB
rebang Cloudflare edge worker ~7.7 ms n/a ~67 ms ~0.4 KiB
DuckDuckGo server-side redirect ~30 ms n/a ~186 ms ~5 KiB
  • warm is a live Service Worker; restart is one the browser has stopped and has to boot again (the normal case if you search a few times a day); cold is a brand-new profile that has to download everything.
  • Warm and restart are on-device and never touch the network, which is the whole point: a Service Worker tool answers in half a millisecond where an edge tool pays a round trip on every search.
  • Cold used to be the tradeoff, since a new profile had to download the whole catalog before it could resolve anything. It doesn't any more: the first navigation already carries the query to our edge, so it is resolved there and the reply carries the answer instead of a 189 KiB download. That lands within a few ms of rebang, an edge-only tool, while keeping the on-device warm path rebang doesn't have.
  • Against flashbang: warm is a statistical tie (0.01 ms apart, 95% CI [-0.01, 0.03], p=0.40), restart goes to flashbang by ~0.9 ms, and cold goes to us by ~23 ms (95% CI [8, 37], p=0.003, 34/40 paired rounds).
  • The bytes column is first contact, which is a small document that registers the Service Worker on the way past. Every later search that reaches the edge is a bare 302 at ~0.4 KiB, and once the worker is installed nothing reaches the edge at all.
  • Restart is the one path where flashbang is genuinely ahead. It restores a persisted record and can answer common bangs before its full runtime is ready, where we rebuild the lookup from the cached catalog: 688 KiB read back out of Cache Storage, which is 1.4 ms of the ~5 ms. Parsing it is free by comparison, 0.03 ms.
  • Warm was 0.03 ms behind until the search counter stopped riding the redirect. An unawaited IndexedDB write still resumes as a microtask, which drains before the response is handed back, so it was on the critical path despite looking asynchronous. Batching it onto a timer took the handler from 121 to 97 microseconds of execution. See BENCHMARK.md for how to measure that directly.
  • Cold numbers are dominated by your network, not by either tool. These were all measured in one sitting from one machine, so read the gaps rather than the absolutes.

How is this different from Theo's version again?

Great question! There are a lotttt of new features in addition to the crazy performance optimization that has been done. Here is a short list:

goose walking animation

  • Bangs
  • Dark Mode
  • Settings (for things like enabling search history, changing default bang, and creating custom bangs)
  • Search counter
  • OpenSearch support
  • Fancy sounds (disabled if you have prefers-reduced-motion set and they are only 28kb)
  • Cute little text animations
  • Auto updating bangs file! (I'm using a GitHub Action to update the bangs file every 24 hours)
  • Compact binary format for the bang catelog that requires no decode step
  • local font file to avoid google fonts
  • redirects to the base page of a bang if there is no query (e.g. !g will take you to google.com and !yt will take you to youtube.com)
  • Suffix bangs (e.g. ghr! taciturnaxolotl/unduckified will take you to this github repo)
  • Quick settings (e.g. !settings or ! will take you to the settings page)
  • Custom local bangs! (thanks to @ayoubabedrabbo@mastodon.social for the suggestion)
  • Kagi bangs! We are able to grab the bangs from kagisearch/bangs and Kagi is far more responsive than DuckDuckGo when it comes to updating their bangs.
  • A service worker! This lets us respond in as little time as possible directly intercepting the browser's network requests
  • Configurable search suggestions!

Search Suggestions

On firefox based browsers you can use the dedicated search suggestions url field but on most other browsers unless they have global search suggestions you are kind of out of luck. Thanks to KobeW50 for bringing this to my attention!

search suggestions field on firefox

Unduck now completes bangs itself! Point the suggestions field at:

https://s.dunkirk.sh/suggest?q=%s

Type !git and you get !github, !githubdocs, and so on, ordered by the duckduckgo bangs rank field. By default that is the only suggestions it provides however you can opt into two more features that expose your searches to other providers.

Options

Plain-query forwarding.

With &forwarder=<provider>, a query with no bang is sent to the named engine. Providers are ddg, google, bing, brave, yahoo, kagi, qwant, and startpage. By default without this option a plain query gets no suggestions and leaves this service untouched. This also enables search suggestions when using a bang for a search engine like google, duckduckgo, or kagi.

https://s.dunkirk.sh/suggest?q=%s&forwarder=ddg

Site-specific suggestions.

With &site_specific_forward=1, a bang followed by a space forwards the rest to that service's own autocomplete where we have a suggestion url. For example !github react searches GitHub's repo search, !wikipedia rust searches Wikipedia. This does not include search engines if you have set &forwarder=<provider> as those will use your configured provider.

https://s.dunkirk.sh/suggest?q=%s&site_specific_forward=1

Plain completions

Both compose with each other and with the dropdown formatting. If your browser or search client would rather have the bare completions, drop the descriptions and favicons with &rich=0:

https://s.dunkirk.sh/suggest?q=%s&rich=0

Privacy

Unfortunately this is the one piece of unduckified that cannot run on your device. Browsers fetch suggestions from the browser process rather than from a page, so a service worker never sees the request. There is an open crbug 41389229 for Chrome (Firefox also behaves the same) but it looks unlikely to change anytime soon.

While suggestions are on, what you type in the address bar reaches the hosted edge function. Nothing is logged or stored but the request does leave your machine and there isn't a good way to audit the worker itself.

If you are strongly worried about privacy then I would disable search suggestions or self host them yourself. You can always use a suggestion provider that is different from the search engine. DuckDuckGo, Kagi, and Google's urls are included below.

https://duckduckgo.com/ac/?q=%s&type=list
https://kagisuggest.com/api/autosuggest?q=%s
https://www.google.com/complete/search?client=chrome&q=%s

Screenshots

open this to view the screenshots

Light Mode

Light Mode Light Mode with Search History Light Mode 404

Dark Mode (the superior mode)

Dark Mode Dark Mode with Search History Dark Mode 404

© 2025-present Kieran Klukas forked from t3dotgg/unduck