{"id":10271,"date":"2026-08-15T01:03:28","date_gmt":"2026-08-14T23:03:28","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/toolbox\/unifi-zone-matrix-planner\/"},"modified":"2026-08-15T01:03:28","modified_gmt":"2026-08-14T23:03:28","slug":"unifi-zone-matrix-planner","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/unifi-zone-matrix-planner\/","title":{"rendered":"UniFi Zone Matrix Planner"},"content":{"rendered":"<div class=\"gtm-analyser-container\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<p style=\"color: var(--text-secondary, #a0a0b0); margin-bottom: 20px;\">A zone matrix is easier to read than a list of rules, because it shows what is missing as well as what is there. Name the zones, set each direction, and out comes the rule list along with the openings that quietly undo the separation.<\/p>\n<p>    <label for=\"uz-zonen\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Zones, separated by commas<\/label><br \/>\n    <input type=\"text\" id=\"uz-zonen\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"Intern, IoT, Gast, Kameras, VPN, Extern\"><\/p>\n<div style=\"display: flex; gap: 10px; flex-wrap: wrap; margin: 15px 0 0;\">\n        <button id=\"uz-matrix\" class=\"button\" type=\"button\" style=\"background: transparent; color: var(--text-primary, #e8e8ee); border: 1px solid var(--border, #2a2a35); padding: 10px 18px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Rebuild matrix<\/button><br \/>\n        <button id=\"uz-btn\" class=\"button\" style=\"background: var(--accent, #7ee787); color: #0b1114; border: none; padding: 12px 24px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Derive rules<\/button>\n    <\/div>\n<div id=\"uz-gitter\" style=\"margin-top: 20px; overflow-x: auto;\"><\/div>\n<div id=\"uz-ergebnis\" style=\"margin-top: 22px;\"><\/div>\n<\/div>\n<p><script>\n(function () {\n    'use strict';<\/p>\n<p>    const T = {\"einleitung\":\"A zone matrix is easier to read than a list of rules, because it shows what is missing as well as what is there. Name the zones, set each direction, and out comes the rule list along with the openings that quietly undo the separation.\",\"label_zonen\":\"Zones, separated by commas\",\"knopf\":\"Derive rules\",\"knopf_matrix\":\"Rebuild matrix\",\"matrix_ecke\":\"from \u2192 to\",\"matrix_erklaerung\":\"Each cell holds the direction from the row to the column. Replies to an allowed connection always come back; a separate rule is only needed when the other side opens the connection itself.\",\"zustand_gesperrt\":\"blocked\",\"zustand_antwort\":\"replies only\",\"zustand_erlaubt\":\"allowed\",\"fehler_zonen\":\"At least two zones are needed.\",\"fehler_zuviele\":\"More than twelve zones make the matrix unreadable.\",\"fehler_doppelt\":\"The zone %s appears twice.\",\"h_regeln\":\"Rules that follow from it\",\"h_befunde\":\"Findings\",\"keine_regeln\":\"Every direction is blocked, which leaves no traffic at all.\",\"regel_text\":\"allow %v \u2192 %n\",\"zusammenfassung\":\"%e directions allowed, %a limited to replies, %g blocked.\",\"b_iot_intern\":\"%v may reach %n. A single compromised smart plug then stands in the middle of the trusted network - the direction that usually has to stay closed.\",\"b_gast_intern\":\"%v may reach %n. Guest access to the internal network defeats the purpose of a guest network.\",\"b_kamera_extern\":\"%v may go out to the internet. Cameras call home, and that is exactly the traffic a local recorder is meant to avoid.\",\"b_iot_extern\":\"%v goes out to the internet. Many devices only need that for their cloud, and a local integration usually replaces it.\",\"b_intern_iot_gesperrt\":\"%v cannot reach %n. That blocks the app on the phone as well - control from the trusted side normally has to stay open.\",\"b_ohne_intern\":\"No zone reads as the internal network. The findings on segmentation only work with a recognisable name.\",\"b_ohne_extern\":\"No zone reads as the internet. Without it nothing can be said about outbound traffic.\",\"b_ganz_dicht\":\"%v may not reach anything at all, not even name resolution. Something in there will stop working.\",\"b_zustand\":\"The firewall is stateful: whatever is allowed in one direction carries its replies back on its own. A second rule is only needed when the other side opens the connection.\",\"b_dns\":\"Even a tightly closed zone needs name resolution. A narrow rule to the DNS resolver belongs in every zone, otherwise the devices fall back to public resolvers or stop working.\",\"b_reihenfolge\":\"In the zone matrix the pair of zones decides, not the position in a list. Where an old rule set is migrated, order-dependent rules have to be checked one by one.\"};<\/p>\n<p>    \/\/ --- reine Rechenlogik ---<\/p>\n<p>    const ZUSTAENDE = ['gesperrt', 'antwort', 'erlaubt'];<\/p>\n<p>    function zonenLesen(text) {\n        return String(text === undefined || text === null ? '' : text)\n            .split(\/[,;\\n]+\/)\n            .map(function (z) { return z.trim(); })\n            .filter(function (z) { return z !== ''; });\n    }<\/p>\n<p>    function art(name) {\n        \/\/ Die Art der Zone wird am Namen erkannt, damit die Hinweise auch bei\n        \/\/ eigenen Bezeichnungen greifen.\n        const n = String(name).toLowerCase();\n        if (\/(extern|internet|wan|external)\/.test(n)) { return 'extern'; }\n        if (\/(iot|smart|geraet|ger\u00e4t|device)\/.test(n)) { return 'iot'; }\n        if (\/(gast|guest)\/.test(n)) { return 'gast'; }\n        if (\/(kamera|camera|cctv|nvr)\/.test(n)) { return 'kamera'; }\n        if (\/(vpn|wireguard|tailscale)\/.test(n)) { return 'vpn'; }\n        if (\/(dmz|server)\/.test(n)) { return 'dmz'; }\n        if (\/(intern|lan|trusted|vertrauen)\/.test(n)) { return 'intern'; }\n        return 'sonstige';\n    }<\/p>\n<p>    function auswerten(zonenText, richtlinien) {\n        const ergebnis = { ok: false, fehler: '', zonen: [], regeln: [], befunde: [], zaehler: { erlaubt: 0, antwort: 0, gesperrt: 0 } };\n        const zonen = zonenLesen(zonenText);<\/p>\n<p>        if (zonen.length < 2) { ergebnis.fehler = T.fehler_zonen; return ergebnis; }\n        if (zonen.length > 12) { ergebnis.fehler = T.fehler_zuviele; return ergebnis; }<\/p>\n<p>        const gesehen = {};\n        for (let i = 0; i < zonen.length; i++) {\n            const schluessel = zonen[i].toLowerCase();\n            if (gesehen[schluessel]) { ergebnis.fehler = T.fehler_doppelt.replace('%s', zonen[i]); return ergebnis; }\n            gesehen[schluessel] = true;\n        }\n\n        ergebnis.zonen = zonen.map(function (z) { return { name: z, art: art(z) }; });\n\n        const stand = richtlinien || {};\n        function zustand(von, nach) {\n            const zeile = stand[von] || {};\n            const wert = zeile[nach];\n            return ZUSTAENDE.indexOf(wert) !== -1 ? wert : 'gesperrt';\n        }\n\n        ergebnis.zonen.forEach(function (von) {\n            ergebnis.zonen.forEach(function (nach) {\n                if (von.name === nach.name) { return; }\n                const z = zustand(von.name, nach.name);\n                ergebnis.zaehler[z]++;\n                if (z !== 'gesperrt') {\n                    ergebnis.regeln.push({\n                        von: von.name, nach: nach.name, zustand: z,\n                        text: T.regel_text.replace('%v', von.name).replace('%n', nach.name)\n                            + (z === 'antwort' ? ' (' + T.zustand_antwort + ')' : '')\n                    });\n                }\n            });\n        });\n\n        \/\/ --- Befunde ---\n        function findeArt(kennung) {\n            return ergebnis.zonen.filter(function (z) { return z.art === kennung; });\n        }\n        const intern = findeArt('intern');\n        const iot = findeArt('iot');\n        const gast = findeArt('gast');\n        const kamera = findeArt('kamera');\n        const extern = findeArt('extern');\n\n        iot.forEach(function (q) {\n            intern.forEach(function (z) {\n                if (zustand(q.name, z.name) === 'erlaubt') {\n                    ergebnis.befunde.push({ schwer: true, text: T.b_iot_intern.replace('%v', q.name).replace('%n', z.name) });\n                }\n            });\n        });\n        gast.forEach(function (q) {\n            intern.forEach(function (z) {\n                if (zustand(q.name, z.name) === 'erlaubt') {\n                    ergebnis.befunde.push({ schwer: true, text: T.b_gast_intern.replace('%v', q.name).replace('%n', z.name) });\n                }\n            });\n        });\n        kamera.forEach(function (q) {\n            extern.forEach(function (z) {\n                if (zustand(q.name, z.name) === 'erlaubt') {\n                    ergebnis.befunde.push({ schwer: true, text: T.b_kamera_extern.replace('%v', q.name) });\n                }\n            });\n        });\n        iot.forEach(function (q) {\n            extern.forEach(function (z) {\n                if (zustand(q.name, z.name) === 'erlaubt') {\n                    ergebnis.befunde.push({ schwer: false, text: T.b_iot_extern.replace('%v', q.name) });\n                }\n            });\n        });\n        intern.forEach(function (q) {\n            iot.forEach(function (z) {\n                if (zustand(q.name, z.name) === 'gesperrt') {\n                    ergebnis.befunde.push({ schwer: true, text: T.b_intern_iot_gesperrt.replace('%v', q.name).replace('%n', z.name) });\n                }\n            });\n        });\n\n        if (!intern.length) { ergebnis.befunde.push({ schwer: false, text: T.b_ohne_intern }); }\n        if (!extern.length) { ergebnis.befunde.push({ schwer: false, text: T.b_ohne_extern }); }\n\n        const gesperrteZonen = ergebnis.zonen.filter(function (z) {\n            return ergebnis.zonen.every(function (andere) {\n                return andere.name === z.name || zustand(z.name, andere.name) === 'gesperrt';\n            });\n        });\n        gesperrteZonen.forEach(function (z) {\n            if (z.art !== 'extern') {\n                ergebnis.befunde.push({ schwer: false, text: T.b_ganz_dicht.replace('%v', z.name) });\n            }\n        });\n\n        ergebnis.befunde.push({ schwer: false, text: T.b_zustand });\n        ergebnis.befunde.push({ schwer: false, text: T.b_dns });\n        ergebnis.befunde.push({ schwer: false, text: T.b_reihenfolge });\n\n        ergebnis.ok = true;\n        return ergebnis;\n    }\n\n    function voreinstellung(zonen) {\n        \/\/ Ein brauchbarer Ausgangspunkt: nach aussen erlaubt, nach innen dicht.\n        const stand = {};\n        zonen.forEach(function (von) {\n            stand[von.name] = {};\n            zonen.forEach(function (nach) {\n                if (von.name === nach.name) { return; }\n                let wert = 'gesperrt';\n                if (nach.art === 'extern' &#038;&#038; von.art !== 'kamera') { wert = 'erlaubt'; }\n                if (von.art === 'intern' &#038;&#038; nach.art !== 'extern') { wert = 'erlaubt'; }\n                if (von.art === 'vpn' &#038;&#038; nach.art === 'intern') { wert = 'erlaubt'; }\n                if (von.art === 'kamera' &#038;&#038; nach.art === 'extern') { wert = 'gesperrt'; }\n                stand[von.name][nach.name] = wert;\n            });\n        });\n        return stand;\n    }\n\n    if (typeof window !== 'undefined') {\n        window.LW_TEST = window.LW_TEST || {};\n        window.LW_TEST.UZ = { auswerten: auswerten, zonenLesen: zonenLesen, art: art, voreinstellung: voreinstellung };\n    }\n\n    \/\/ --- Anbindung an das Dokument ---\n\n    const btn = document.getElementById('uz-btn');\n    const matrixBtn = document.getElementById('uz-matrix');\n    const gitter = document.getElementById('uz-gitter');\n    const out = document.getElementById('uz-ergebnis');\n    const zonenFeld = document.getElementById('uz-zonen');\n    if (!btn || !matrixBtn || !gitter || !out || !zonenFeld) { return; }\n\n    const KASTEN = 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 6px; padding: 14px; margin-bottom: 18px;';\n    const UEBERSCHRIFT = 'color: var(--text-primary, #e8e8ee); font-weight: 700; margin-bottom: 8px;';\n\n    function el(tag, css, inhalt) {\n        const knoten = document.createElement(tag);\n        if (css) { knoten.setAttribute('style', css); }\n        if (inhalt !== undefined &#038;&#038; inhalt !== null) { knoten.textContent = inhalt; }\n        return knoten;\n    }\n\n    function gitterBauen() {\n        const namen = zonenLesen(zonenFeld.value);\n        gitter.textContent = '';\n        if (namen.length < 2) {\n            gitter.appendChild(el('div', 'color: #ffb86c;', T.fehler_zonen));\n            return;\n        }\n        const zonen = namen.map(function (z) { return { name: z, art: art(z) }; });\n        const stand = voreinstellung(zonen);\n\n        const tabelle = el('table', 'border-collapse: collapse; min-width: 100%;');\n        const kopf = el('tr', '');\n        kopf.appendChild(el('th', 'padding: 8px; color: var(--text-secondary, #a0a0b0); font-size: 0.78rem; text-align: left; white-space: nowrap;', T.matrix_ecke));\n        zonen.forEach(function (z) {\n            kopf.appendChild(el('th', 'padding: 8px; color: var(--text-secondary, #a0a0b0); font-size: 0.78rem; white-space: nowrap;', z.name));\n        });\n        tabelle.appendChild(kopf);\n\n        zonen.forEach(function (von) {\n            const zeile = el('tr', '');\n            zeile.appendChild(el('td', 'padding: 8px; color: var(--text-primary, #e8e8ee); font-size: 0.82rem; white-space: nowrap; border-top: 1px solid var(--border, #2a2a35);', von.name));\n            zonen.forEach(function (nach) {\n                const zelle = el('td', 'padding: 4px; border-top: 1px solid var(--border, #2a2a35); text-align: center;');\n                if (von.name === nach.name) {\n                    zelle.appendChild(el('span', 'color: var(--border, #2a2a35);', '-'));\n                } else {\n                    const auswahl = document.createElement('select');\n                    auswahl.setAttribute('data-von', von.name);\n                    auswahl.setAttribute('data-nach', nach.name);\n                    auswahl.setAttribute('style', 'padding: 5px; background: var(--bg-panel, #1e1e24); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 5px; font-family: monospace; font-size: 0.75rem;');\n                    ZUSTAENDE.forEach(function (z) {\n                        const option = document.createElement('option');\n                        option.value = z;\n                        option.textContent = T['zustand_' + z];\n                        auswahl.appendChild(option);\n                    });\n                    auswahl.value = stand[von.name][nach.name];\n                    zelle.appendChild(auswahl);\n                }\n                zeile.appendChild(zelle);\n            });\n            tabelle.appendChild(zeile);\n        });\n\n        gitter.appendChild(el('div', 'color: var(--text-secondary, #a0a0b0); font-size: 0.82rem; margin-bottom: 8px;', T.matrix_erklaerung));\n        gitter.appendChild(tabelle);\n    }\n\n    function richtlinienLesen() {\n        const stand = {};\n        const felder = gitter.querySelectorAll('select[data-von]');\n        for (let i = 0; i < felder.length; i++) {\n            const von = felder[i].getAttribute('data-von');\n            const nach = felder[i].getAttribute('data-nach');\n            if (!stand[von]) { stand[von] = {}; }\n            stand[von][nach] = felder[i].value;\n        }\n        return stand;\n    }\n\n    matrixBtn.addEventListener('click', gitterBauen);\n\n    btn.addEventListener('click', function () {\n        if (!gitter.querySelector('select[data-von]')) { gitterBauen(); }\n        const erg = auswerten(zonenFeld.value, richtlinienLesen());\n\n        out.textContent = '';\n        if (!erg.ok) {\n            out.appendChild(el('div', 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-left: 3px solid #ffb86c; color: var(--text-secondary, #a0a0b0); padding: 14px; border-radius: 6px;', erg.fehler));\n            return;\n        }\n\n        out.appendChild(el('div', UEBERSCHRIFT, T.h_regeln));\n        const kasten = el('div', KASTEN);\n        if (!erg.regeln.length) {\n            kasten.appendChild(el('div', 'color: var(--text-secondary, #a0a0b0);', T.keine_regeln));\n        }\n        erg.regeln.forEach(function (r, i) {\n            const z = el('div', 'display: flex; gap: 12px; align-items: baseline; padding: 5px 0; border-bottom: 1px solid var(--border, #2a2a35);');\n            z.appendChild(el('span', 'width: 28px; color: var(--text-secondary, #a0a0b0); font-family: monospace; font-size: 0.8rem;', String(i + 1)));\n            z.appendChild(el('span', 'flex: 1; color: var(--text-primary, #e8e8ee); font-family: monospace; font-size: 0.85rem;', r.text));\n            kasten.appendChild(z);\n        });\n        kasten.appendChild(el('div', 'color: var(--text-secondary, #a0a0b0); font-size: 0.85rem; padding-top: 10px;',\n            T.zusammenfassung.replace('%e', String(erg.zaehler.erlaubt)).replace('%a', String(erg.zaehler.antwort)).replace('%g', String(erg.zaehler.gesperrt))));\n        out.appendChild(kasten);\n\n        out.appendChild(el('div', UEBERSCHRIFT, T.h_befunde));\n        const befunde = el('div', KASTEN);\n        erg.befunde.forEach(function (b) {\n            const z = el('div', 'display: flex; gap: 10px; padding: 5px 0;');\n            z.appendChild(el('span', 'color: ' + (b.schwer ? '#ffb86c' : 'var(--text-secondary, #a0a0b0)') + '; font-family: monospace;', b.schwer ? '!' : '-'));\n            z.appendChild(el('span', 'color: var(--text-secondary, #a0a0b0); font-size: 0.9rem; line-height: 1.5;', b.text));\n            befunde.appendChild(z);\n        });\n        out.appendChild(befunde);\n    });\n\n    gitterBauen();\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Turns a set of network zones into a matrix of policies, derives the rule list from it, and points out the openings that undo the segmentation.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":38,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[91243,91707,91115],"class_list":["post-10271","page","type-page","status-publish","hentry","tag-networking","tag-unifi","tag-web-security"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>UniFi Zone Matrix Planner - Lukas Wojcik - Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/unifi-zone-matrix-planner\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"UniFi Zone Matrix Planner - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Turns a set of network zones into a matrix of policies, derives the rule list from it, and points out the openings that undo the segmentation.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/unifi-zone-matrix-planner\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/unifi-zone-matrix-planner\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/unifi-zone-matrix-planner\\\/\",\"name\":\"UniFi Zone Matrix Planner - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-08-14T23:03:28+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/unifi-zone-matrix-planner\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/unifi-zone-matrix-planner\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/unifi-zone-matrix-planner\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Toolbox\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"UniFi Zone Matrix Planner\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"luky\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"luky\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"UniFi Zone Matrix Planner - Lukas Wojcik - Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/unifi-zone-matrix-planner\/","og_locale":"en_US","og_type":"article","og_title":"UniFi Zone Matrix Planner - Lukas Wojcik - Blog","og_description":"Turns a set of network zones into a matrix of policies, derives the rule list from it, and points out the openings that undo the segmentation.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/unifi-zone-matrix-planner\/","og_site_name":"Lukas Wojcik - Blog","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/unifi-zone-matrix-planner\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/unifi-zone-matrix-planner\/","name":"UniFi Zone Matrix Planner - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-08-14T23:03:28+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/unifi-zone-matrix-planner\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/unifi-zone-matrix-planner\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/unifi-zone-matrix-planner\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Toolbox","item":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/"},{"@type":"ListItem","position":3,"name":"UniFi Zone Matrix Planner"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"luky","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"luky"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/10271","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=10271"}],"version-history":[{"count":0,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/10271\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/38"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=10271"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=10271"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}