bluemonday
A fast, allowlist-based HTML sanitizer for Go that strips XSS vectors from untrusted content while preserving safe markup.
Repository Health
Technical Analysis
bluemonday is a Go library for sanitizing untrusted HTML fragments before they reach a browser. Instead of trying to blocklist dangerous tags and attributes, it works the opposite way: you build (or reuse) a policy describing exactly which HTML elements, attributes, and inline CSS properties are permitted, and everything else is stripped. This allowlist-first design, modeled on the OWASP Java HTML Sanitizer and HTML Purifier, closes off the constant cat-and-mouse game of trying to enumerate every possible XSS payload.
It ships two ready-made policies — StrictPolicy(), which strips all markup down to plain text, and UGCPolicy(), a broad but safe allowlist tuned for user-generated content such as blog comments or Markdown-rendered posts — plus a fluent builder API (AllowElements, AllowAttrs, AllowStyles, AllowURLSchemes) for constructing custom policies. Built policies are safe to reuse across goroutines, and the library includes dedicated handling for tricky cases like relative URLs, data URIs, and automatically adding rel="nofollow noopener noreferrer" to untrusted links.
What You Get
- Two production-ready default policies:
StrictPolicy()for plain-text-only output andUGCPolicy()for rich but safe user-generated content - A fluent policy-builder API to allow specific elements, attributes, and regex-matched attribute values on a per-element or global basis
- Inline CSS style validation via a dedicated
csssubpackage, including value-matching handlers and enum-based allowed values - URL safety controls: parseable-URL enforcement, relative URL allow/deny, scheme allowlisting, and data-URI image validation
- Automatic link hardening —
rel="nofollow",rel="noreferrer", andtarget="_blank"+rel="noopener"handling for fully-qualified links - Three input/output shapes —
Sanitize(string),SanitizeBytes([]byte), andSanitizeReader(io.Reader)— for different performance needs
Common Use Cases
- Sanitizing Markdown-rendered HTML (e.g. from Blackfriday or Pandoc) before storing or serving it
- Cleaning WYSIWYG editor output submitted by end users on comments, forums, or CMS content
- Stripping HTML entirely from fields that should be plain text, such as titles, using
StrictPolicy() - Building a custom allowlist for a specific rich-content feature (e.g. only allowing basic formatting tags and safe links)
- Protecting federated/ActivityPub or CMS platforms (used in production by projects like Gitea/Forgejo) from stored XSS in remote content
Under The Hood
Architecture
The library separates policy definition from the sanitization engine. policy.go defines the Policy struct as a set of allowlist maps — per-element attribute rules (elsAndAttrs), regex-matched element rules (elsMatchingAndAttrs), global attributes, per-element and global CSS style rules, and allowed URL schemes — built up through a fluent Allow* API and marked initialized so a zero-value Policy{} can’t be used unsafely. sanitize.go implements the actual sanitizer as a streaming, forward-only walk over the golang.org/x/net/html tokenizer, checking every token’s element and attributes against the policy’s maps rather than attempting to parse and repair malformed HTML. A separate css subpackage (css/handlers.go) owns inline-style validation using the douceur CSS parser, keeping style-property logic decoupled from the element/attribute logic. policies.go layers StrictPolicy() and UGCPolicy() on top of the same builder API used by consumers, so the shipped defaults are just examples of the public surface. Because every consumer path (custom policies and the two canned ones) funnels through the same elsAndAttrs/elsAndStyles maps and the same tokenizer walk in sanitize.go, that pair of files is the seam the whole library pivots on.
Tech Stack
A minimal-dependency Go module (go.mod targets Go 1.19) with exactly two direct dependencies: golang.org/x/net for the html package’s tokenizer, and aymerick/douceur for CSS parsing (which pulls in gorilla/css transitively). There is no web framework, ORM, or database involved — this is a pure processing library invoked via go get and called directly from application code. CI (GitHub Actions) runs against both the pinned Go 1.19.x and the latest Go release across Ubuntu, macOS, and Windows, running go vet, staticcheck, and go test -race on every push and pull request.
Code Quality
Test coverage is extensive and specific: sanitize_test.go alone contains around 48 test functions covering individual XSS vectors, malformed markup, and edge cases in URL/CSS handling, backed by further tests in policy_test.go, policies_test.go, and helpers_test.go, plus runnable example_test.go files that double as documentation. CI enforces go vet, staticcheck, and the race detector on every change across multiple OSes and Go versions, giving strong confidence against regressions and data races in concurrent policy use. Errors are handled by explicit return values rather than panics — Sanitize() documents that malformed input degrades to an empty string rather than surfacing an error, a deliberate simplicity trade-off for a security-focused API. Naming and structure follow idiomatic Go conventions throughout, with doc comments on every exported type and method.
What Makes It Unique
Most hand-rolled HTML sanitizers try to blocklist dangerous tags and attributes, which is fragile against novel XSS vectors. bluemonday instead defaults to fail-closed: nothing is permitted unless the policy explicitly allows it, an approach it inherited from the OWASP Java HTML Sanitizer and HTML Purifier projects. Beyond the core allowlist mechanism, it goes deep on link and URL safety specifically — parseable-URL enforcement, relative-URL control, data-URI image validation with mimetype checks, and automatic rel="noopener" injection for target="_blank" links — details that simpler sanitizers commonly miss. This depth, combined with its concurrency-safe policy objects, has made it the de facto standard HTML sanitizer in the Go ecosystem, used in production by projects such as Gitea and Forgejo.
Used by 11 apps in this directory
Apache Answer
Community
Open-source Q&A platform for communities, help centers, and knowledge bases with AI assistant and plugin extensibility
Coder
Devops · Developer Tools · Code Editors
Self-hosted cloud development environments and AI coding agents — defined in Terraform, connected via WireGuard, automatically shut down when idle.
Fider
Product Management · Customer Support
Open-source feedback portal where customers submit, vote on, and track feature requests so product teams build what actually matters.
Gitea
Devops · Developer Tools · Project Management
Self-hosted DevOps in a single Go binary — Git hosting, GitHub Actions-compatible CI/CD, and 30+ package registries without any SaaS dependency.
Gogs
Developer Tools
The painless self-hosted Git service that runs on anything from a Raspberry Pi to a $5 cloud droplet, delivering GitHub-like workflows as a single Go binary.
Mattermost
Team Chat · Collaboration · Devops
Open core, self-hosted team collaboration with chat, AI agents, voice calling, and deep DevOps integrations — all under your control.
Navidrome
File Storage
Run your own personal Spotify — stream your entire music collection from any device, anywhere, forever.
Notifuse
Marketing
Open-source, self-hosted alternative to Mailchimp, Brevo, and Klaviyo — send newsletters and transactional emails without per-email pricing or vendor lock-in.
ntfy
Developer Tools · Marketing
Send push notifications to your phone or desktop from any script or service using a single HTTP PUT or POST—no sign-up required.