← Back to Wiki
Networking / Security

Allow One Device Past a UniFi Category Content Filter

Most home routers with a zone-based firewall block a whole traffic category for an entire network in one rule. Social media, gaming, whatever. Sooner or later one device needs an exception without lifting the block for everyone else. The obvious fix is a more specific "allow" rule. That has a real, non-obvious catch on at least one popular controller's API.

Share on X

The setup: a category block scoped to a whole network

Zone-based firewalls match traffic by app or traffic category, not just IP and port. UniFi, pfSense, OPNsense and similar. "Block this entire network from reaching anything tagged Social Media", for example. It is a clean way to apply a blanket restriction without maintaining a domain or IP blocklist by hand. Parental controls, focus rules, whatever the reason. The rule's source is the network as a whole, its destination is the traffic category, and the action is block.

The obvious way to carve out one exception is a second rule. Match that specific device by its client identity, not the whole network, against the same category, action allow. Then make sure it evaluates before the broad block. Zone-based firewalls stop at the first matching rule, so a specific allow ranked ahead of a broad block does exactly what you expect. Everyone else still hits the block. This one device hits the allow first and never reaches it.

The gotcha: the API can create rules, but it won't reorder them

BE WARNED: a POST to create a new rule silently ignores any priority you send. It always appends to the end of that rule chain. A PUT to an existing rule to change its priority returns a success response while leaving the order untouched. Neither call errors. Both quietly skip the ordering part of what you asked. Create your "allow this device" rule expecting it to land above the existing block, test, and find the device still blocked, and the instinct is to assume the new rule is misconfigured. The real problem is that it sits after the block in evaluation order and never gets reached.

Confirm that before you debug anything else. Pull the full rule list back down and sort by whatever priority field the API returns. If your specific-allow rule has a higher index number than the broad block it is supposed to preempt, that is the whole problem. The rule content is fine. The position is wrong.

The reliable fix: delete and recreate the rule you can't move

New rules always append to the end of the chain, so use that. Create your specific exception rule first. Then delete the broad block rule and immediately recreate it with identical settings. The recreated block gets a fresh append-to-the-end placement, which now lands it after your exception rule, because that one was created first and already occupies an earlier slot. No drag-and-drop reordering. No manual index math. Delete then recreate, in the right sequence.

BE WARNED: this opens a brief window where the broad block is completely absent. From the moment you delete it to the moment the recreated copy is confirmed live. Typically a couple of seconds over a scripted API call. For a low-stakes category block, which is what this guide is about, that risk is negligible. For anything security-relevant the gap is not acceptable and this technique is the wrong tool. Isolating an untrusted network, blocking a known-bad destination. Use the controller's UI drag-and-drop reorder for those. It applies as a single atomic change, with no window where the rule is simply gone.

Verifying it actually worked

Re-fetch the full rule list after recreating the block and confirm the final order directly. Do not infer it from "the delete succeeded" and "the create succeeded" individually. Then test from the exempted device itself, not by reading the config back. Load something that falls under the blocked category and confirm it loads. A correctly ordered rule set that does not match the traffic the way you assumed is still a failure. Just a quieter one.

A note on scale: exempt the one device, not everyone else

There is a second way to solve this. Instead of a higher-priority allow for the one device you want to exempt, flip the broad block's own source from "the whole network" to "every device on this network except the one I want exempted". That works and it does not scale. You maintain an explicit list of every other device's identity by hand. On any network with phones or tablets using per-network randomized identifiers, that list drifts out of date as those identifiers rotate, silently un-blocking devices that were supposed to stay restricted. Exempting the one device you care about, rather than enumerating everyone else, is less work and does not rot.