Understanding CSS :visited Privacy Restrictions

The CSS :visited pseudo-class allows developers to alter the appearance of links a user has already clicked. Historically, this capability introduced a severe vulnerability known as "history sniffing," enabling malicious websites to silently harvest a visitor's web browsing history. To close this loophole, modern web browsers restrict :visited styles strictly to a small set of color-related properties and lie to JavaScript about computed values, neutralizing the threat while preserving a core user-experience feature of the web.

The History-Sniffing Vulnerability

Before modern security restrictions were implemented, developers could apply virtually any CSS property to the :visited state, including background-image, font-size, padding, and display.

Attackers exploited this behavior using two primary vectors:

  1. Network-based timing attacks: A page could inject an invisible list of thousands of well-known URLs (such as banking portals, social media platforms, or sensitive forums) and assign unique background images to their :visited states. Whenever a visited URL was matched, the browser requested the background image from the attacker's server, instantly logging which sites the visitor had frequented.
  2. DOM-based layout checks: When styling properties that altered box dimensions (like display: none or changing font-size), JavaScript could query the DOM via properties like element.offsetWidth or element.clientHeight. If the dimensions differed from the unvisited baseline, the script knew the user had visited that exact link.

Because these checks ran instantly and invisibly, a site could enumerate thousands of URLs per second to construct a detailed profile of a user's browsing habits.

The Color-Only Restriction

To eliminate layout-based leaks and network callbacks, modern browser engines strictly limit :visited selectors to color properties that do not alter the geometry of the page or trigger external asset downloads.

Permitted properties are restricted to:

  • color
  • background-color
  • border-color (including top, right, bottom, and left variants)
  • outline-color
  • column-rule-color
  • SVG fill and stroke

Any attempt to apply layout-altering properties (such as margin, padding, width, or height) or resource-fetching properties (such as background-image) to a :visited link is completely ignored by the rendering engine.

Spoofing JavaScript APIs

Restricting styles to colors alone does not completely resolve the problem, as JavaScript could theoretically use window.getComputedStyle() to read the applied text or background color and determine whether the link was visited.

To counter this, browsers implement "style spoofing." When JavaScript requests the computed style of a visited link, the browser intentionally returns the values of the unvisited link state. Scripting engines are effectively blind to the :visited styling; only the internal rendering pipeline receives the true color data used to draw pixels on the screen.

Alpha Channel and Sibling Limitations

Browsers also restrict how transparency works with :visited rules. If an unvisited link has a transparent background, setting a solid background-color on :visited could alter the perceived color of underlying parent elements. Consequently, browsers enforce that the alpha channel (opacity) of the visited state cannot be modified independently. The browser uses the alpha component from the unvisited style to blend with the visited color, preventing complex timing or composite-pixel attacks.

Additionally, CSS combinators that depend on the visited state—such as styling an adjacent sibling selector based on whether a preceding link is visited (a:visited + span)—are disabled. Only the link itself can take on visited color styles, ensuring that visited status cannot leak outward into the rest of the document tree.