Safari’s Declarative Content Blocking: A Practical Engineering Decision for Web Performance and Privacy
Learn how Safari’s declarative content blocker lets you filter network requests before they’re issued, the limits you’ll hit, and a concrete example that cuts third‑party analytics traffic. Perfect for extension developers weighing performance vs. flexibility.
15 Aug 2026, 00:17 UTC

Why Declarative Content Blocking Matters
When a browser loads a page, every src or href triggers a network request. In Safari, the Content Blocking API lets you drop those requests before they reach the page’s JavaScript engine. The result is twofold:
- Fewer bytes cross the network, cutting bandwidth and improving perceived load time.
- Blocked requests never hit the page, so no script can re‑inject them or leak user data.
Unlike script‑based blockers that run on the main thread, Safari compiles a JSON ruleset into a highly optimised lookup table in the network process. That means no extra CPU cycles, no jank, and no risk of breaking the page’s JavaScript.
When to Choose the Declarative API
If your goal is to block a known set of resources—ads, trackers, analytics, or a family of image hosts—and you can express that set with URL patterns, the declarative API is the cleanest, most efficient path. It’s also the only way to guarantee that the request never leaves the network stack on iOS, where extensions run in a sandboxed environment.
Rule Syntax and Limits (Safari 17.0)
The rules are plain JSON objects that match on three properties:
trigger– containsurl-filter(regex‑style wildcard),resource-type(e.g., "image", "script", "stylesheet"), and optionalif-domain/unless-domain.action– currently only supportstype: "block"ortype: "ignore‑sub‑resources".- Optional
priority(lower numbers fire first).
WebKit enforces hard caps that vary by platform:
- Maximum ruleset size: 1 MB (compressed).
- Maximum rule count: 10 000 (macOS), 5 000 (iOS).
- Maximum
url-filterlength: 500 characters.
These limits keep memory usage predictable and matching fast. If you exceed them, Safari silently truncates the ruleset, so you’ll need to profile to detect missing blocks.
Integrating into a Safari Web Extension
1. Create a content_blocker.json in your extension’s root.
[
{
"trigger": {
"url-filter": ".*\.googlesyndication\.com/.*",
"resource-type": ["image", "script"] },
"action": {
"type": "block" }
},
{
"trigger": {
"url-filter": "^https://analytics\.example\.com/.*",
"resource-type": ["script"] },
"action": {
"type": "block" }
}]
2. Declare the file in manifest.json:
{
"name": "MyPrivacyBlocker",
"description": "Blocks third‑party trackers.",
"version": "1.0",
"manifest_version": 3,
"content_blocking": {
"rules": [
{
"path": "content_blocker.json",
"type": "blocking"
}
]
}
}
3. Load the extension in Safari’s Extensions panel (macOS) or Settings > Safari > Extensions (iOS). Reloading the extension recompiles the rules automatically; no JavaScript handshake is required.
Verifying That the Rules Are Working
- Open Safari Web Inspector (⌘+Option+I). Navigate to the Network tab.
- Enable Show All Requests to see the full list.
- Visit a test page that loads the blocked domains (e.g., a page that includes
https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js). - Observe that the blocked requests never appear in the log. If they do, the rule syntax is wrong or the ruleset exceeded limits.
- Use the Debugger console to run
window.webkit.messageHandlers.myExtension.postMessage({cmd: 'getRulesCount'});if you’ve added a debug handler to report the compiled rule count.
Note: Because the network process filters before the request is issued, you’ll not see any 200/404 status for blocked URLs; they simply don’t exist in the log.
Trade‑Offs and Limitations
While the declarative API is fast, it’s intentionally simple:
- No ability to inspect response bodies or headers.
- No dynamic logic (e.g., “block if the page contains a
div#ads”). - Limited to a handful of resource types; complex matching on cookies or referrers isn’t supported.
- Rule limits mean you can’t block thousands of unique domains in a single ruleset; you’ll need to merge or prune patterns.
If your use case requires runtime decision‑making based on page content, you’ll need a script‑based approach that runs on the page thread, accepting the performance penalty.
Actionable Takeaways
- Start with a declarative ruleset if you only need to block known third‑party trackers.
- Keep the JSON compact: combine patterns with wildcards, and use
resource-typeto narrow matches. - Profile your ruleset size and count on both macOS and iOS to stay within limits.
- Use Safari’s Network Inspector to confirm that requests are filtered before they hit the page.
- When you hit the 10 000 rule limit, consider splitting the blocker into multiple extensions or consolidating patterns.
By leveraging Safari’s declarative content blocking, you can deliver a privacy‑first experience with minimal runtime overhead—a compelling choice for extensions that prioritize performance and reliability over complex logic.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.