Design & Implementation: lite.duckduckgo.com
8 min read
lite.duckduckgo.com (LDDG) is the ‘Lite’ experience of DuckDuckGo (DDG), a web search engine. Lite site variants typically forego all images and have very little-to-no JavaScript. LDDG manages to deliver a full search engine result page (SERP) consisting of approximately 30 results in just 10kB over the wire (compressed). How’s this possible?
I will be looking at the SERP for “example” in this article:
!['Example' SERP on lite.duckduckgo.com.](/_astro/exampleSERP.4299bdcb_1fpVP4.webp)
lite.duckduckgo.com.
is defined as a CNAME for duckduckgo.com.
. Whenever making a request to LDDG, you are speaking to the same DDG frontend servers. The Host
header is used to determine what application (LDDG or DDG) to serve you.
duckduckgo.com
is hosted on Microsoft Azure. It leverages ECS routing to connect you to the datacenter closest to the DNS resolver you use.
European users may be connected to a server in Ireland:
![An Irish IP owned by Microsoft Azure hosting duckduckgo.com](/_astro/azurehosted-eu.cc395269_Fd9Lj.webp)
Whereas users connecting from the East Coast of the US might hit a server in Washington:
![A US IP owned by Microsoft Azure hosting duckduckgo.com](/_astro/azurehosted-us.e312090b_Zdjsid.webp)
DDG uses a wildcard certificate for *.duckduckgo.com
signed by DigiCert with a 1-year expiration date. It offers HTTP2 (HTTP3 is not supported) and TLS 1.3 with perfect forward secrecy. TLS 1.2 is offered for compatibility. Older versions are not available for legacy clients, in order to protect against downgrade attacks. Strict HSTS is enabled via the HTTP header. Surprisingly, duckduckgo.com
is not on the HSTS Preload List as of the time of writing.
LDDG advertises using nginx
via the Server
header. It offers modern Brotli compression.
Content Security Policy
Read section Content Security PolicyA very strict CSP is set. Assets may only be loaded from duckduckgo.com, its subdomains, and its onion service, data
and blob
URLs are allowed for some assets (fonts and web workers).
content-security-policy: content-security-policy: default-src 'none' ; connect-src https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; manifest-src https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; media-src https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; script-src blob: https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; font-src data: https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; img-src data: https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; style-src https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; object-src 'none' ; worker-src blob: ; child-src blob: https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; frame-src blob: https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; frame-ancestors 'self' ; base-uri 'self' ; block-all-mixed-content ;
content-security-policy: content-security-policy: default-src 'none' ; connect-src https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; manifest-src https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; media-src https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; script-src blob: https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; font-src data: https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; img-src data: https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; style-src https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; object-src 'none' ; worker-src blob: ; child-src blob: https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; frame-src blob: https://duckduckgo.com https://*.duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/ ; frame-ancestors 'self' ; base-uri 'self' ; block-all-mixed-content ;
Static assets
Read section Static assetsAssets are hosted on duckduckgo.com
, and also advertise being served by nginx.
HTML/CSS
Read section HTML/CSSLDDG is a very simple HTML page. Its head provides an Open Search Description so users can easily use it as their preferred search engine.
A blast from the past: the page’s body uses
for spacing and even uses a 1x1px transparent GIF for tracking. HTML tables are used to render the search results inside a <center>
element. This makes the page usable even with CSS disabled.
A POST form to /lite
is used for pagination and to submit a search with a different query, although query parameters may also be used to link to a specific SERP.
A tiny stylesheet spruces up the page a little. The font stack is 'Helvetica Neue','Segoe UI', Arial, sans-serif
.
Oddly, some BEM-styled selectors are included in this file that are not used on the page. The .badge--yahoo
selector references server-side template substitutiton that is not actually performed:
.badge--yahoo {
background-image: url("<TMPL_VAR name="base_url_ext">/assets/attribution/yahoo.v103.png");
background-size: 55px 13px;
width: 55px;
height: 13px;
}
.badge--yahoo {
background-image: url("<TMPL_VAR name="base_url_ext">/assets/attribution/yahoo.v103.png");
background-size: 55px 13px;
width: 55px;
height: 13px;
}
This asset is available on duckduckgo.com. <TMPL_VAR>
originates from the HTML::Template Perl library.
The selectors used by the page itself have arbitrary naming. Some selectors also target specific elements globally. This makes sense given the simple nature of the page, but wouldn’t scale to a larger site.
Console
Read section ConsoleChromium issues a warning for the incorrect formatting of the page’s viewport declaration. There should not be a semicolon at the end of its content:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=3.0, user-scalable=1;" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=3.0, user-scalable=1;" />
Firefox warns that LDDG is rendered in “almost standards” mode (similar to quirks mode), due to the page’s HTML 4 doctype:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
The page itself does not log anything to the console. This is unsurprising: the page has no JavaScript.
Lighthouse
Read section LighthouseLighthouse reports excellent performance (a score of 99/100) and great Core Web Vitals scores. The main improvement that could be made to page speed would be to inline the stylesheet. The amount of CSS used is so small that perhaps even using inline styles would be a viable choice.
The tracking pixel is referenced for missing an alt attribute and explicit width/height. This doesn’t affect the experience however, as it is at the end of the document.
View the Lighthouse report here
Accessibility
Read section Accessibilityaxe reports several serious accessibility issues:
- The
<html>
tag lacks thelang
attribute. - The tracking pixel lacks an
alt
. Additionally, it should be hidden (aria-hidden
). - The search form and does not have a label. This is also confusing for sighted users.
- The
<select>
elements, used for region and recency filters, does not have a corresponding label.<option value selected>Label</option>
is used for sighted users, but this is not semantic. - There is no landmark on the page, like a
main
element, that screen reader users could use to quickly skip to the search results. Therole
attribute could be used as an alternative to HTML5 elements if maximum compatibility is required. - The
viewport
definition setsmaximum-scale=3.0
for no reason. This should be omitted. - There is no
<h1>
on the page. TheDuckDuckGo
text is styled as a heading but is not tagged as such.
There are no major issues with the tabbing order. The user must tab between search results. Arrow key navigation is supported only by duckduckgo.com through the use of scripting as a progressive enhancement.
Summary
Read section Summarylite.duckduckgo.com offers a fast, lightweight experience. Its servers are located globally to improve the end-user experience. Its implementation is extremely simple. The team went to painstaking lengths using HTML tables to ensure the page provides a usable experience even without CSS. The DuckDuckGo team should review and fix its accessibility issues.
What could be improved
Read section What could be improved- The HTML5 doctype should be used
- duckduckgo.com should be added to the HSTS Preload List
- The page’s stylesheet should be inlined in the head, and unused classes should be removed
- axe’s accessibility recommendations should be applied