Understanding the :visited Privacy Vulnerability
The CSS :visited pseudo-class represents links that users have already visited in their browser history. While this feature helps users track their browsing activity, it also created one of the longest-running privacy vulnerabilities in web browser history. For over two decades, malicious websites could detect which pages users had visited by checking the computed styles of links on a page.
The fundamental issue stemmed from how browsers applied styles to visited links. When a website included a list of URLs, it could use JavaScript to check the computed color of each link. Since visited and unvisited links displayed different colors, the website could determine which URLs existed in the user's browser history. Attackers could test thousands of URLs against a user's browser, building a comprehensive map of their browsing habits including visited banking sites, healthcare pages, social networks, and other sensitive destinations. This information could be used for identity profiling, targeted phishing attacks, or simply invading user privacy without consent.
Understanding these browser security considerations is essential for any web development professional building modern, privacy-conscious applications.
Why This Mattered
The real-world implications of history sniffing attacks were significant and far-reaching. Malicious websites could profile users without their knowledge or consent, building detailed dossiers of their online behavior. A shopping site could detect if users had visited competitor websites and adjust pricing accordingly. Employers could monitor which job sites employees had visited. Advertisers could build detailed interest profiles based on the websites users had visited. The attack required no user interaction beyond visiting a webpage--JavaScript code could automatically iterate through hundreds or thousands of links, checking their computed styles and reporting back which ones matched the visited color pattern.
This vulnerability was particularly concerning because it affected all major browsers for over twenty years, exposing millions of users to potential privacy violations. The fact that it remained exploitable for so long demonstrates the challenges of balancing feature functionality with user privacy in web browsers. For web developers, understanding this history is essential for building secure applications and making informed decisions about how to use CSS selectors responsibly. Modern browsers have implemented strict protections, but the story of :visited provides valuable lessons about web security that continue to influence browser development today.
A Brief History of the Vulnerability
The :visited privacy issue dates back to the earliest implementations of CSS in web browsers. Security researchers identified the problem early, but it took years of public pressure before browsers implemented meaningful protections.
Timeline of Key Events
- 2000: Princeton researchers Felten and Schneider publish "Timing Attacks on Web Privacy"
- 2002: Andrew Clover posts proof-of-concept attack to Bugtraq mailing list
- 2002: Mozilla developer David Baron files bug report that eventually leads to major mitigations
- 2009: StartPanic website demonstrates the vulnerability publicly, urging vendors to act
- 2010: Browser vendors begin implementing mitigations based on David Baron's proposal
- 2011: Carnegie Mellon researchers demonstrate six bypass techniques in "I Still Know What You Visited Last Summer"
- 2025: Chrome 136 releases partitioning fix, finally closing the security gap
The year 2000 marked the first academic documentation of how web browsers could leak private information through CSS. Researchers Edward Felten and Michael Schneider from Princeton University published their findings in "Timing Attacks on Web Privacy," establishing the theoretical foundation for understanding this vulnerability. Their work demonstrated that timing differences in style application could reveal information about the user's browsing history.
By 2002, security researcher Andrew Clover had developed a practical proof-of-concept attack and published it to the Bugtraq mailing list, bringing the vulnerability to wider attention within the security community. Around the same time, Mozilla developer David Baron filed a bug report that would eventually lead to the first major browser mitigations. Baron would go on to develop the protection strategy that Mozilla implemented in 2010.
The issue remained largely theoretical until April 2009, when a website called StartPanic was published specifically to demonstrate the vulnerability and push browser vendors toward implementing fixes. Another site called "Have Your Friends Been There" appeared in February 2010, advertising itself as a way to "See what naughty websites your friends have been looking at." These public demonstrations finally created enough pressure for browser vendors to act.
The arms race between security researchers finding new bypass techniques and browser vendors racing to patch them continued for years, culminating in Chrome 136's partitioning solution in 2025 that fundamentally changed how browsers store and expose visited link data. This decades-long effort highlights why modern SEO practices must account for browser security features and user privacy protections.
How the Attack Worked
The history sniffing attack exploited the fundamental design of how browsers apply styles to visited links. Understanding the attack mechanism helps explain why browser protections were necessary and why they took so long to implement effectively.
The Color Detection Method
The attack worked by leveraging the fact that browsers apply different styles to visited and unvisited links. A malicious website would include hundreds or thousands of hidden links to various URLs--banking sites, healthcare providers, social networks, and other destinations. JavaScript code would then use the getComputedStyle API to query the computed color of each link. Since visited links displayed a different color than unvisited links, the script could determine which URLs existed in the user's history.
// Simplified attack concept demonstrating the privacy vulnerability
var links = document.querySelectorAll('a');
for (var i = 0; i < links.length; i++) {
var color = window.getComputedStyle(links[i]).color;
if (color === 'rgb(85, 26, 139)') { // Purple for visited
// Mark this link as visited in attacker's database
reportVisit(links[i].href);
}
}
This process was remarkably fast--attackers could test thousands of URLs in just a few seconds. The attack required no special permissions, no user interaction beyond visiting the page, and left no visible traces on the user's screen. Sophisticated attackers could build detailed profiles of user behavior, including sensitive information about medical conditions, financial services used, political affiliations, and personal relationships.
As browser security has evolved, understanding these historical vulnerabilities becomes crucial for AI-powered automation systems that interact with web content and need to account for privacy-preserving browser behaviors.
Why Early Mitigations Failed
Initial browser responses to the vulnerability were insufficient because they addressed symptoms rather than the underlying architecture. Mozilla was the first to implement mitigations in 2010, following a proposal by David Baron. The browser modified getComputedStyle to always return the unvisited style value, even for links that had actually been visited. Additionally, browsers limited which CSS properties could be applied to :visited selectors, removing the ability to use layout-affecting properties.
However, these mitigations were ultimately incomplete. The fundamental problem was that browsers still maintained a global list of visited URLs that any website could query. Researchers continued to find novel ways to distinguish between visited and unvisited links, including timing-based attacks that measured how long the browser took to apply styles. A 2011 research paper from Carnegie Mellon University titled "I Still Know What You Visited Last Summer" demonstrated six different history sniffing exploits that bypassed the proposed mitigations. The browser vendors found themselves in an ongoing arms race, constantly patching new bypass techniques while the underlying architecture remained fundamentally vulnerable.
This history explains why it took until 2025 for a truly comprehensive solution--browser vendors needed an architectural change rather than incremental patches to finally close this decades-old security gap.
1:link {2 color: #0066cc;3}4 5:visited {6 color: #551a8b;7}8 9/* What happens with modern browser protections */10a.malicious-attempt:visited {11 background-image: url('tracker.png'); /* Ignored by browsers */12 color: purple; /* Works as expected for UX */13 width: 200px; /* Ignored - layout properties blocked */14}CSS Properties for :visited
Modern browsers strictly limit which CSS properties can be applied to :visited selectors. These restrictions prevent sophisticated timing and side-channel attacks while still allowing basic visual feedback for users. The limitations are essential for understanding what :visited can and cannot do in modern web development.
Allowed Properties
The following properties can be styled for visited links without compromising user privacy:
- color - Text and link colors, the most common use case
- background-color - Background colors for visual distinction
- border-color - All border color properties (border-top-color, border-right-color, etc.)
- column-rule-color - Multi-column layout separator lines
- outline-color - Outline borders around links
- text-decoration-color - Underlines and line-through decorations
- fill - SVG element fill colors
- stroke - SVG element stroke colors
Restricted Properties
Most CSS properties cannot be used with :visited due to their potential for creating attack vectors:
- background-image - Prevented to stop image loading timing attacks
- Layout properties - width, height, margin, padding cannot be modified
- font properties - font-size, font-family, and other typography settings
- Any property affecting element size - Prevents layout-based detection
- border-style and border-width - Can be set but have no effect on detection
These restrictions ensure that websites cannot extract meaningful timing information or detect visited links through side channels. Developers who attempt to use restricted properties will find that they simply do not apply to visited links, with the browser falling back to the unvisited style instead.
| Property | Allowed | Purpose and Attack Prevention |
|---|---|---|
| color | Yes | Link text color - safe for visual feedback |
| background-color | Yes | Background color - safe for visual feedback |
| border-color | Yes | Border colors - safe for visual feedback |
| outline-color | Yes | Outline color - safe for visual feedback |
| column-rule-color | Yes | Multi-column lines - safe for visual feedback |
| text-decoration-color | Yes | Underline colors - safe for visual feedback |
| fill (SVG) | Yes | SVG fill colors - safe for visual feedback |
| stroke (SVG) | Yes | SVG stroke colors - safe for visual feedback |
| background-image | No | Timing attack risk through image loading |
| font-size | No | Layout detection risk |
| width/height | No | Layout detection and timing attacks |
| margin/padding | No | Layout detection and timing attacks |
| font-family | No | Layout and rendering timing attacks |
How to use :visited effectively and securely
Visual Feedback Only
Use :visited for user experience improvements, not tracking or analytics. The selector cannot expose visit data to external servers due to browser protections.
Color Contrast
Ensure visited and unvisited states have sufficient contrast for accessibility. Don't rely solely on color--consider adding icons or underlines for users with color vision deficiencies.
Brand-Consistent Styling
Use your brand's color palette for both states. A muted version of your primary color works well for visited links while maintaining visual consistency.
No Security Dependence
Never rely on :visited for security purposes. Use proper authentication and authorization instead. The selector is purely cosmetic.
1/* Clear visual distinction with sufficient contrast */2a:link {3 color: #0066cc;4 text-decoration: underline;5}6 7a:visited {8 color: #663399;9 text-decoration: underline;10}11 12/* Optional: Add indicator icons for additional distinction */13a.visited-indicator:visited::after {14 content: " ✓";15 font-size: 0.85em;16}17 18/* Ensure contrast ratio of at least 4.5:1 */19a:visited {20 color: #5a3d8a; /* Sufficient contrast against white */21}Frequently Asked Questions
Can websites still track my browsing history?
No, not through :visited. Chrome 136 and similar protections in other browsers prevent cross-site history detection. Websites can only see links visited on their own domain, which fundamentally blocks history sniffing attacks.
Why can I only use certain CSS properties with :visited?
Properties like background-image or layout-affecting styles could be used for timing attacks or layout-based detection. The restriction limits attack vectors while maintaining basic visual feedback functionality for users.
Does :visited work for tracking users?
No. All modern browsers prevent :visited from being used for tracking. JavaScript cannot accurately detect visited links, and any data collected cannot be exfiltrated to external servers due to browser protections.
Should I still use :visited in my designs?
Yes! :visited remains useful for providing visual feedback to users within your website. Just understand it's purely for user experience, not for tracking or security purposes.
How do I clear my browser's visited link data?
Clear your browsing history through browser settings. Most browsers also offer private/incognito modes where visited links are not recorded, providing additional privacy protection.
Sources
-
MDN Web Docs - Privacy and :visited selector - Comprehensive official documentation covering browser privacy protections, restricted CSS properties, and developer impact.
-
Chrome Security Blog - Visited Links - Official Google announcement of the partitioning fix in Chrome 136.
-
Mozilla Security Blog - Plugging the CSS History Leak - Mozilla's original mitigation approach from 2010.
-
L. David Baron - Preventing attacks on user's history through CSS :visited - Original 2010 technical proposal outlining the privacy problem and solution approach.
-
The Register - Chrome to patch decades-old flaw - News coverage of Chrome 136's fix explaining the 23-year history of the vulnerability.