{"id":9420,"date":"2026-08-10T13:37:13","date_gmt":"2026-08-10T11:37:13","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/toolbox\/wireguard-allowedips-split-tunnel-calculator\/"},"modified":"2026-08-10T13:45:04","modified_gmt":"2026-08-10T11:45:04","slug":"wireguard-allowedips-split-tunnel-calculator","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/wireguard-allowedips-split-tunnel-calculator\/","title":{"rendered":"WireGuard AllowedIPs &#038; Split-Tunnel Calculator"},"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, #9aa0aa); margin-bottom: 20px;\">Every WireGuard peer answers one question: which destination addresses belong in the tunnel. Get AllowedIPs wrong and traffic either leaks around the tunnel or vanishes into the wrong one, usually without a single error message. This calculator handles the three parts of that job.<\/p>\n<h3 style=\"color: var(--text-primary, #e8e8ee); font-size: 1.1rem; margin: 0 0 8px 0;\">1. Split tunnel: cut networks out of a range<\/h3>\n<p style=\"color: var(--text-secondary, #9aa0aa); margin-bottom: 15px; font-size: 0.9rem;\">Enter the range the tunnel should carry and the networks that must stay outside it. The result is the smallest possible set of CIDR blocks covering everything that is left, ready to paste behind AllowedIPs.<\/p>\n<div style=\"display: flex; gap: 10px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 15px;\">\n<div style=\"flex: 1; min-width: 220px;\">\n            <label style=\"color: var(--text-secondary, #9aa0aa); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Total range<\/label><br \/>\n            <input type=\"text\" id=\"wg-gesamt\" value=\"0.0.0.0\/0\" class=\"form-control\" style=\"width: 100%; padding: 12px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace;\">\n        <\/div>\n<div style=\"flex: 2; min-width: 260px;\">\n            <label style=\"color: var(--text-secondary, #9aa0aa); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Networks to exclude, one per line<\/label><br \/>\n            <textarea id=\"wg-ausnahmen\" rows=\"4\" class=\"form-control\" style=\"width: 100%; padding: 12px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; resize: vertical;\">10.0.0.0\/8<br \/>\n172.16.0.0\/12<br \/>\n192.168.0.0\/16<\/textarea>\n        <\/div>\n<\/p><\/div>\n<p>    <button id=\"wg-btn1\" class=\"button\" style=\"background: var(--accent, #4ade80); color: #0b1114; border: none; padding: 12px 24px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Calculate AllowedIPs<\/button><\/p>\n<div id=\"wg-ergebnis1\" style=\"margin-top: 20px;\"><\/div>\n<hr style=\"border: none; border-top: 1px solid var(--border, #2a2a35); margin: 30px 0;\">\n<h3 style=\"color: var(--text-primary, #e8e8ee); font-size: 1.1rem; margin: 0 0 8px 0;\">2. Overlaps between peers<\/h3>\n<p style=\"color: var(--text-secondary, #9aa0aa); margin-bottom: 15px; font-size: 0.9rem;\">Paste the AllowedIPs of each peer, one peer per line: a name, a colon, then the networks separated by commas.<\/p>\n<p>    <label style=\"color: var(--text-secondary, #9aa0aa); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Peers and their AllowedIPs<\/label><br \/>\n    <textarea id=\"wg-peers\" rows=\"5\" class=\"form-control\" style=\"width: 100%; padding: 12px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; resize: vertical; margin-bottom: 15px;\">office: 10.0.0.0\/8, 192.168.1.0\/24<br \/>\nhome: 192.168.0.0\/16<br \/>\ncloud: 172.16.0.0\/12, 10.10.0.0\/16<br \/>\nbackup: 203.0.113.0\/24<\/textarea><\/p>\n<p>    <button id=\"wg-btn2\" class=\"button\" style=\"background: var(--accent, #4ade80); color: #0b1114; border: none; padding: 12px 24px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Check for overlaps<\/button><\/p>\n<div id=\"wg-ergebnis2\" style=\"margin-top: 20px;\"><\/div>\n<hr style=\"border: none; border-top: 1px solid var(--border, #2a2a35); margin: 30px 0;\">\n<h3 style=\"color: var(--text-primary, #e8e8ee); font-size: 1.1rem; margin: 0 0 8px 0;\">3. Tunnel MTU<\/h3>\n<p style=\"color: var(--text-secondary, #9aa0aa); margin-bottom: 15px; font-size: 0.9rem;\">The tunnel wraps every packet in extra headers. Set the tunnel MTU too high and packets get fragmented or silently dropped, which usually shows up as pages that load halfway and then stall.<\/p>\n<div style=\"display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 15px;\">\n<div style=\"flex: 1; min-width: 180px;\">\n            <label style=\"color: var(--text-secondary, #9aa0aa); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Physical MTU of the outer connection<\/label><br \/>\n            <input type=\"text\" id=\"wg-mtu\" value=\"1500\" class=\"form-control\" style=\"width: 100%; padding: 12px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace;\">\n        <\/div>\n<div style=\"flex: 1; min-width: 180px;\">\n            <label style=\"color: var(--text-secondary, #9aa0aa); display: block; font-size: 0.85rem; margin-bottom: 5px;\">IP version of the outer connection<\/label><br \/>\n            <select id=\"wg-version\" class=\"form-control\" style=\"width: 100%; padding: 12px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace;\"><option value=\"ipv4\">IPv4<\/option><option value=\"ipv6\">IPv6<\/option><\/select>\n        <\/div>\n<p>        <button id=\"wg-btn3\" class=\"button\" style=\"background: var(--accent, #4ade80); color: #0b1114; border: none; padding: 12px 24px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Calculate MTU<\/button>\n    <\/div>\n<div id=\"wg-ergebnis3\"><\/div>\n<\/div>\n<p><script>\n(function () {\n    \/\/ --- reine Rechenlogik, ohne jeden DOM-Zugriff ---<\/p>\n<p>    function ipZuZahl(text) {\n        const teile = String(text).trim().split('.');\n        if (teile.length !== 4) { return null; }\n        let wert = 0;\n        for (let i = 0; i < 4; i++) {\n            if (!\/^\\d{1,3}$\/.test(teile[i])) { return null; }\n            const n = parseInt(teile[i], 10);\n            if (n > 255) { return null; }\n            wert = wert * 256 + n;\n        }\n        return wert >>> 0;\n    }<\/p>\n<p>    function zahlZuIp(n) {\n        const a = Math.floor(n \/ 16777216) % 256;\n        const b = Math.floor(n \/ 65536) % 256;\n        const c = Math.floor(n \/ 256) % 256;\n        const d = n % 256;\n        return a + '.' + b + '.' + c + '.' + d;\n    }<\/p>\n<p>    function cidrText(block) {\n        return zahlZuIp(block.basis) + '\/' + block.praefix;\n    }<\/p>\n<p>    function formatZahl(n) {\n        return String(Math.round(n)).replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, ',');\n    }<\/p>\n<p>    function parseCidr(text) {\n        const roh = String(text).trim();\n        if (roh.length === 0) { return { fehler: 'leer', roh: roh }; }\n        const teile = roh.split('\/');\n        if (teile.length > 2) { return { fehler: 'cidr', roh: roh }; }\n        const adresse = ipZuZahl(teile[0]);\n        if (adresse === null) { return { fehler: 'cidr', roh: roh }; }\n        let praefix = 32;\n        if (teile.length === 2) {\n            const p = teile[1].trim();\n            if (!\/^\\d{1,3}$\/.test(p)) { return { fehler: 'praefix', roh: roh }; }\n            praefix = parseInt(p, 10);\n            if (praefix > 32) { return { fehler: 'praefix', roh: roh }; }\n        }\n        const groesse = Math.pow(2, 32 - praefix);\n        const basis = Math.floor(adresse \/ groesse) * groesse;\n        return {\n            fehler: null,\n            roh: roh,\n            basis: basis,\n            praefix: praefix,\n            groesse: groesse,\n            ende: basis + groesse - 1,\n            normalisiert: basis !== adresse\n        };\n    }<\/p>\n<p>    \/\/ rekursives Halbieren: Haelfte ganz ausserhalb -> behalten,\n    \/\/ ganz innerhalb einer Ausnahme -> verwerfen, sonst weiter teilen\n    function subtrahiere(basis, praefix, ausnahmen) {\n        const ergebnis = [];\n        let zaehler = 0;\n        let abbruch = false;<\/p>\n<p>        function schritt(b, p) {\n            if (abbruch) { return; }\n            zaehler = zaehler + 1;\n            if (zaehler > 200000) { abbruch = true; return; }\n            const groesse = Math.pow(2, 32 - p);\n            const ende = b + groesse - 1;\n            let ueberlappt = false;\n            for (let i = 0; i < ausnahmen.length; i++) {\n                const a = ausnahmen[i];\n                if (b >= a.basis && ende <= a.ende) { return; }\n                if (b <= a.ende &#038;&#038; ende >= a.basis) { ueberlappt = true; }\n            }\n            if (!ueberlappt) { ergebnis.push({ basis: b, praefix: p }); return; }\n            if (p >= 32) { return; }\n            schritt(b, p + 1);\n            schritt(b + groesse \/ 2, p + 1);\n        }<\/p>\n<p>        schritt(basis, praefix);\n        return { bloecke: ergebnis, abbruch: abbruch };\n    }<\/p>\n<p>    \/\/ benachbarte gleich grosse Bloecke wieder zusammenfassen\n    function fasseZusammen(bloecke) {\n        let liste = bloecke.slice().sort(function (x, y) {\n            if (x.basis !== y.basis) { return x.basis - y.basis; }\n            return x.praefix - y.praefix;\n        });\n        let geaendert = true;\n        while (geaendert) {\n            geaendert = false;\n            const neu = [];\n            let i = 0;\n            while (i < liste.length) {\n                const a = liste[i];\n                const b = i + 1 < liste.length ? liste[i + 1] : null;\n                if (b &#038;&#038; a.praefix === b.praefix &#038;&#038; a.praefix > 0) {\n                    const groesse = Math.pow(2, 32 - a.praefix);\n                    if (a.basis % (groesse * 2) === 0 && b.basis === a.basis + groesse) {\n                        neu.push({ basis: a.basis, praefix: a.praefix - 1 });\n                        i = i + 2;\n                        geaendert = true;\n                        continue;\n                    }\n                }\n                neu.push(a);\n                i = i + 1;\n            }\n            liste = neu;\n        }\n        return liste;\n    }<\/p>\n<p>    function formatiere(bloecke) {\n        return bloecke.map(cidrText).join(', ');\n    }<\/p>\n<p>    function berechne(gesamtText, ausnahmenText) {\n        const g = parseCidr(gesamtText);\n        if (g.fehler === 'leer') { return { fehler: { typ: 'leer' } }; }\n        if (g.fehler === 'praefix') { return { fehler: { typ: 'praefix', wert: g.roh } }; }\n        if (g.fehler) { return { fehler: { typ: 'cidr', wert: g.roh } }; }<\/p>\n<p>        const zeilen = String(ausnahmenText === undefined || ausnahmenText === null ? '' : ausnahmenText)\n            .split(\/[\\n\\r,;]+\/)\n            .map(function (t) { return t.trim(); })\n            .filter(function (t) { return t.length > 0; });<\/p>\n<p>        const ausnahmen = [];\n        const ignoriert = [];\n        const normalisiert = [];\n        if (g.normalisiert) { normalisiert.push({ von: g.roh, nach: cidrText(g) }); }<\/p>\n<p>        for (let i = 0; i < zeilen.length; i++) {\n            const a = parseCidr(zeilen[i]);\n            if (a.fehler === 'praefix') { return { fehler: { typ: 'praefix', wert: a.roh } }; }\n            if (a.fehler) { return { fehler: { typ: 'cidr', wert: a.roh } }; }\n            if (a.normalisiert) { normalisiert.push({ von: a.roh, nach: cidrText(a) }); }\n            if (a.ende < g.basis || a.basis > g.ende) { ignoriert.push(cidrText(a)); continue; }\n            ausnahmen.push(a);\n        }<\/p>\n<p>        const roh = subtrahiere(g.basis, g.praefix, ausnahmen);\n        if (roh.abbruch) { return { fehler: { typ: 'zuviel' } }; }\n        const bloecke = fasseZusammen(roh.bloecke);<\/p>\n<p>        let abgedeckt = 0;\n        for (let k = 0; k < bloecke.length; k++) {\n            abgedeckt = abgedeckt + Math.pow(2, 32 - bloecke[k].praefix);\n        }\n\n        return {\n            fehler: null,\n            gesamt: cidrText(g),\n            bloecke: bloecke,\n            allowedIPs: formatiere(bloecke),\n            anzahl: bloecke.length,\n            abgedeckt: abgedeckt,\n            gesamtGroesse: g.groesse,\n            ignoriert: ignoriert,\n            normalisiert: normalisiert\n        };\n    }\n\n    function lesePeers(text) {\n        const zeilen = String(text === undefined || text === null ? '' : text)\n            .split(\/[\\n\\r]+\/)\n            .map(function (t) { return t.trim(); })\n            .filter(function (t) { return t.length > 0; });<\/p>\n<p>        const peers = [];\n        for (let i = 0; i < zeilen.length; i++) {\n            const zeile = zeilen[i];\n            const pos = zeile.indexOf(':');\n            if (pos < 1) { return { fehler: { typ: 'name', wert: zeile } }; }\n            const name = zeile.slice(0, pos).trim();\n            if (name.length === 0) { return { fehler: { typ: 'name', wert: zeile } }; }\n            const teile = zeile.slice(pos + 1).split(',')\n                .map(function (t) { return t.trim(); })\n                .filter(function (t) { return t.length > 0; });\n            if (teile.length === 0) { return { fehler: { typ: 'ohnebereich', wert: name } }; }\n            const bereiche = [];\n            for (let j = 0; j < teile.length; j++) {\n                const c = parseCidr(teile[j]);\n                if (c.fehler === 'praefix') { return { fehler: { typ: 'praefix', wert: c.roh } }; }\n                if (c.fehler) { return { fehler: { typ: 'cidr', wert: c.roh } }; }\n                bereiche.push(c);\n            }\n            peers.push({ name: name, bereiche: bereiche });\n        }\n        if (peers.length < 2) { return { fehler: { typ: 'wenigpeers' } }; }\n        return { fehler: null, peers: peers };\n    }\n\n    \/\/ zwei IPv4-Bloecke sind entweder verschachtelt oder disjunkt:\n    \/\/ die Schnittmenge ist immer der spezifischere Block\n    function ueberschneidungen(peers) {\n        const paare = [];\n        for (let i = 0; i < peers.length; i++) {\n            for (let j = i + 1; j < peers.length; j++) {\n                const teile = [];\n                for (let x = 0; x < peers[i].bereiche.length; x++) {\n                    for (let y = 0; y < peers[j].bereiche.length; y++) {\n                        const a = peers[i].bereiche[x];\n                        const b = peers[j].bereiche[y];\n                        if (a.basis <= b.ende &#038;&#038; b.basis <= a.ende) {\n                            const enger = a.praefix >= b.praefix ? a : b;\n                            const t = cidrText(enger);\n                            if (teile.indexOf(t) === -1) { teile.push(t); }\n                        }\n                    }\n                }\n                if (teile.length > 0) {\n                    paare.push({ a: peers[i].name, b: peers[j].name, teile: teile });\n                }\n            }\n        }\n        return paare;\n    }<\/p>\n<p>    function mtuBerechnung(physisch, version) {\n        const roh = String(physisch === undefined || physisch === null ? '' : physisch).trim();\n        if (!\/^\\d{1,5}$\/.test(roh)) { return { fehler: { typ: 'mtu' } }; }\n        const n = parseInt(roh, 10);\n        if (n < 576 || n > 9200) { return { fehler: { typ: 'mtu' } }; }\n        const ipKopf = version === 'ipv6' ? 40 : 20;\n        const overhead = ipKopf + 8 + 32;\n        return {\n            fehler: null,\n            physisch: n,\n            ipKopf: ipKopf,\n            overhead: overhead,\n            tunnel: n - overhead,\n            version: version === 'ipv6' ? 'IPv6' : 'IPv4'\n        };\n    }<\/p>\n<p>    \/\/ fuer die Pruefung von aussen erreichbar\n    if (typeof window !== 'undefined') {\n        window.LW_TEST = window.LW_TEST || {};\n        window.LW_TEST.wg = {\n            berechne: berechne,\n            formatiere: formatiere,\n            ipZuZahl: ipZuZahl,\n            zahlZuIp: zahlZuIp,\n            parseCidr: parseCidr,\n            subtrahiere: subtrahiere,\n            fasseZusammen: fasseZusammen,\n            lesePeers: lesePeers,\n            ueberschneidungen: ueberschneidungen,\n            mtuBerechnung: mtuBerechnung\n        };\n    }<\/p>\n<p>    \/\/ --- DOM-Anbindung, sauber abgesichert ---<\/p>\n<p>    function esc(t) {\n        return String(t)\n            .replace(\/&\/g, '&amp;')\n            .replace(\/<\/g, '&lt;')\n            .replace(\/>\/g, '&gt;')\n            .replace(\/\"\/g, '&quot;');\n    }<\/p>\n<p>    const KASTEN = 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 6px; padding: 15px; margin-bottom: 12px;';\n    const MONO = 'font-family: monospace; color: var(--text-primary, #e8e8ee); word-break: break-all; line-height: 1.7;';\n    const KLEIN = 'color: var(--text-secondary, #9aa0aa); font-size: 0.8rem; margin: 0 0 6px 0; text-transform: uppercase; letter-spacing: 0.05em;';<\/p>\n<p>    function meldung(text) {\n        return '<\/p>\n<div style=\"' + KASTEN + ' border-color: var(--accent, #4ade80);\">' +\n            '<\/p>\n<p style=\"margin: 0; color: var(--text-primary, #e8e8ee);\">' + esc(text) + '<\/p>\n<\/div>\n<p>';\n    }<\/p>\n<p>    function fehlerText(f) {\n        if (f.typ === 'leer') { return 'Please enter a total range, for example 0.0.0.0\/0.'; }\n        if (f.typ === 'cidr') { return 'Not a valid IPv4 CIDR block:' + ' ' + f.wert; }\n        if (f.typ === 'praefix') { return 'The prefix has to be a number between 0 and 32:' + ' ' + f.wert; }\n        if (f.typ === 'zuviel') { return 'That produces too many blocks to calculate. Use fewer or larger exclusions.'; }\n        if (f.typ === 'name') { return 'A line needs a peer name followed by a colon, for example office: 10.0.0.0\/8. This line has none:' + ' ' + f.wert; }\n        if (f.typ === 'ohnebereich') { return 'This peer has no networks:' + ' ' + f.wert; }\n        if (f.typ === 'wenigpeers') { return 'Please enter at least two peers, one per line.'; }\n        if (f.typ === 'mtu') { return 'Please enter the physical MTU as a whole number between 576 and 9200.'; }\n        return 'Not a valid IPv4 CIDR block:';\n    }<\/p>\n<p>    const btn = document.getElementById('wg-btn1');\n    const out = document.getElementById('wg-ergebnis1');\n    if (!btn || !out) { return; }\n    const btn2 = document.getElementById('wg-btn2');\n    const out2 = document.getElementById('wg-ergebnis2');\n    if (!btn2 || !out2) { return; }\n    const btn3 = document.getElementById('wg-btn3');\n    const out3 = document.getElementById('wg-ergebnis3');\n    if (!btn3 || !out3) { return; }<\/p>\n<p>    const feldGesamt = document.getElementById('wg-gesamt');\n    const feldAusnahmen = document.getElementById('wg-ausnahmen');\n    const feldPeers = document.getElementById('wg-peers');\n    const feldMtu = document.getElementById('wg-mtu');\n    const feldVersion = document.getElementById('wg-version');\n    if (!feldGesamt || !feldAusnahmen || !feldPeers || !feldMtu || !feldVersion) { return; }<\/p>\n<p>    btn.addEventListener('click', function () {\n        const r = berechne(feldGesamt.value, feldAusnahmen.value);\n        if (r.fehler) { out.innerHTML = meldung(fehlerText(r.fehler)); return; }\n        if (r.anzahl === 0) { out.innerHTML = meldung('The exclusions cover the whole range. Nothing is left for AllowedIPs.'); return; }<\/p>\n<p>        let html = '<\/p>\n<div style=\"' + KASTEN + '\">' +\n            '<\/p>\n<p style=\"' + KLEIN + '\">AllowedIPs<\/p>\n<p>' +\n            '<\/p>\n<div style=\"' + MONO + '\">' + esc(r.allowedIPs) + '<\/div>\n<\/div>\n<p>';<\/p>\n<p>        const anteil = (r.abgedeckt \/ r.gesamtGroesse) * 100;\n        html += '<\/p>\n<div style=\"display: flex; gap: 12px; flex-wrap: wrap;\">' +\n            '<\/p>\n<div style=\"' + KASTEN + ' flex: 1; min-width: 150px;\">' +\n            '<\/p>\n<p style=\"' + KLEIN + '\">Blocks<\/p>\n<p>' +\n            '<\/p>\n<div style=\"' + MONO + ' font-size: 1.4rem;\">' + r.anzahl + '<\/div>\n<\/div>\n<p>' +\n            '<\/p>\n<div style=\"' + KASTEN + ' flex: 2; min-width: 220px;\">' +\n            '<\/p>\n<p style=\"' + KLEIN + '\">Addresses covered<\/p>\n<p>' +\n            '<\/p>\n<div style=\"' + MONO + '\">' + formatZahl(r.abgedeckt) + ' \/ ' + formatZahl(r.gesamtGroesse) +\n            ' (' + anteil.toFixed(2) + ' %)<\/div>\n<\/div>\n<\/div>\n<p>';<\/p>\n<p>        if (r.normalisiert.length > 0) {\n            let zeilen = '';\n            for (let i = 0; i < r.normalisiert.length; i++) {\n                zeilen += '\n\n<div style=\"' + MONO + '\">' + esc(r.normalisiert[i].von) + ' &rarr; ' +\n                    esc(r.normalisiert[i].nach) + '<\/div>\n<p>';\n            }\n            html += '<\/p>\n<div style=\"' + KASTEN + '\">\n<p style=\"' + KLEIN + '\">Host bits set to zero<\/p>\n<p>' + zeilen + '<\/p><\/div>\n<p>';\n        }<\/p>\n<p>        if (r.ignoriert.length > 0) {\n            html += '<\/p>\n<div style=\"' + KASTEN + '\">\n<p style=\"' + KLEIN + '\">Ignored, outside the total range<\/p>\n<p>' +\n                '<\/p>\n<div style=\"' + MONO + '\">' + esc(r.ignoriert.join(', ')) + '<\/div>\n<\/div>\n<p>';\n        }<\/p>\n<p>        out.innerHTML = html;\n    });<\/p>\n<p>    btn2.addEventListener('click', function () {\n        const gelesen = lesePeers(feldPeers.value);\n        if (gelesen.fehler) { out2.innerHTML = meldung(fehlerText(gelesen.fehler)); return; }<\/p>\n<p>        const paare = ueberschneidungen(gelesen.peers);\n        if (paare.length === 0) { out2.innerHTML = meldung('No overlaps. Every address matches at most one peer.'); return; }<\/p>\n<p>        let html = '<\/p>\n<div style=\"' + KASTEN + '\">\n<p style=\"' + KLEIN + '\">Overlapping peers<\/p>\n<p>';\n        html += '<\/p>\n<div style=\"display: flex; gap: 12px; ' + KLEIN + ' margin-top: 12px;\">' +\n            '<\/p>\n<div style=\"flex: 1;\">Peer pair<\/div>\n<div style=\"flex: 1;\">Shared networks<\/div>\n<\/div>\n<p>';\n        for (let i = 0; i < paare.length; i++) {\n            html += '\n\n<div style=\"display: flex; gap: 12px; flex-wrap: wrap; padding: 8px 0; border-top: 1px solid var(--border, #2a2a35);\">' +\n                '<\/p>\n<div style=\"flex: 1; min-width: 160px; ' + MONO + '\">' + esc(paare[i].a) + ' &harr; ' + esc(paare[i].b) + '<\/div>\n<p>' +\n                '<\/p>\n<div style=\"flex: 1; min-width: 160px; ' + MONO + ' color: var(--accent, #4ade80);\">' +\n                esc(paare[i].teile.join(', ')) + '<\/div>\n<\/div>\n<p>';\n        }\n        html += '<\/p><\/div>\n<p>';\n        html += '<\/p>\n<div style=\"' + KASTEN + ' border-color: var(--accent, #4ade80);\">' +\n            '<\/p>\n<p style=\"' + KLEIN + '\">Why this matters<\/p>\n<p>' +\n            '<\/p>\n<p style=\"margin: 0; color: var(--text-secondary, #9aa0aa);\">WireGuard picks the peer whose AllowedIPs match last, not the one that matches most specifically. Where two peers claim the same addresses, the traffic quietly takes the wrong tunnel and nothing reports an error. Make the ranges disjoint.<\/p>\n<\/div>\n<p>';<\/p>\n<p>        out2.innerHTML = html;\n    });<\/p>\n<p>    btn3.addEventListener('click', function () {\n        const r = mtuBerechnung(feldMtu.value, feldVersion.value);\n        if (r.fehler) { out3.innerHTML = meldung(fehlerText(r.fehler)); return; }<\/p>\n<p>        const kopf = r.version === 'IPv6' ? '40 (IPv6) + 8 (UDP) + 32 (WireGuard)' : '20 (IPv4) + 8 (UDP) + 32 (WireGuard)';\n        let html = '<\/p>\n<div style=\"display: flex; gap: 12px; flex-wrap: wrap;\">' +\n            '<\/p>\n<div style=\"' + KASTEN + ' flex: 1; min-width: 180px;\">' +\n            '<\/p>\n<p style=\"' + KLEIN + '\">Recommended tunnel MTU<\/p>\n<p>' +\n            '<\/p>\n<div style=\"' + MONO + ' font-size: 1.4rem; color: var(--accent, #4ade80);\">' + r.tunnel + ' Byte<\/div>\n<\/div>\n<p>' +\n            '<\/p>\n<div style=\"' + KASTEN + ' flex: 2; min-width: 240px;\">' +\n            '<\/p>\n<p style=\"' + KLEIN + '\">How it adds up<\/p>\n<p>' +\n            '<\/p>\n<div style=\"' + MONO + '\">' + kopf + ' = ' + r.overhead + ' Byte<\/div>\n<p>' +\n            '<\/p>\n<div style=\"' + MONO + '\">' + r.physisch + ' &minus; ' + r.overhead + ' = ' + r.tunnel + ' Byte<\/div>\n<p>' +\n            '<\/p><\/div>\n<\/div>\n<p>' +\n            '<\/p>\n<div style=\"' + KASTEN + '\">\n<p style=\"' + KLEIN + '\">Line for the interface section<\/p>\n<p>' +\n            '<\/p>\n<div style=\"' + MONO + '\">[Interface]<br \/>MTU = ' + r.tunnel + '<\/div>\n<\/div>\n<p>';<\/p>\n<p>        out3.innerHTML = html;\n    });\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Every WireGuard peer answers one question: which destination addresses belong in the tunnel. Get AllowedIPs wrong and traffic either leaks around the tunnel or vanishes into the wrong one, usually without a single error message. This calculator handles the three parts of that job. 1. Split tunnel: cut networks out of a range Enter the [&hellip;]<\/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":""},"class_list":["post-9420","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>WireGuard AllowedIPs &amp; Split-Tunnel Calculator - 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\/wireguard-allowedips-split-tunnel-calculator\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"WireGuard AllowedIPs &amp; Split-Tunnel Calculator - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Every WireGuard peer answers one question: which destination addresses belong in the tunnel. Get AllowedIPs wrong and traffic either leaks around the tunnel or vanishes into the wrong one, usually without a single error message. This calculator handles the three parts of that job. 1. Split tunnel: cut networks out of a range Enter the [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/wireguard-allowedips-split-tunnel-calculator\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-10T11:45:04+00:00\" \/>\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<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\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\\\/wireguard-allowedips-split-tunnel-calculator\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/wireguard-allowedips-split-tunnel-calculator\\\/\",\"name\":\"WireGuard AllowedIPs & Split-Tunnel Calculator - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-08-10T11:37:13+00:00\",\"dateModified\":\"2026-08-10T11:45:04+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/wireguard-allowedips-split-tunnel-calculator\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/wireguard-allowedips-split-tunnel-calculator\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/wireguard-allowedips-split-tunnel-calculator\\\/#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\":\"WireGuard AllowedIPs &#038; Split-Tunnel Calculator\"}]},{\"@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":"WireGuard AllowedIPs & Split-Tunnel Calculator - 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\/wireguard-allowedips-split-tunnel-calculator\/","og_locale":"en_US","og_type":"article","og_title":"WireGuard AllowedIPs & Split-Tunnel Calculator - Lukas Wojcik - Blog","og_description":"Every WireGuard peer answers one question: which destination addresses belong in the tunnel. Get AllowedIPs wrong and traffic either leaks around the tunnel or vanishes into the wrong one, usually without a single error message. This calculator handles the three parts of that job. 1. Split tunnel: cut networks out of a range Enter the [&hellip;]","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/wireguard-allowedips-split-tunnel-calculator\/","og_site_name":"Lukas Wojcik - Blog","article_modified_time":"2026-08-10T11:45:04+00:00","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","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/wireguard-allowedips-split-tunnel-calculator\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/wireguard-allowedips-split-tunnel-calculator\/","name":"WireGuard AllowedIPs & Split-Tunnel Calculator - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-08-10T11:37:13+00:00","dateModified":"2026-08-10T11:45:04+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/wireguard-allowedips-split-tunnel-calculator\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/wireguard-allowedips-split-tunnel-calculator\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/wireguard-allowedips-split-tunnel-calculator\/#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":"WireGuard AllowedIPs &#038; Split-Tunnel Calculator"}]},{"@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\/9420","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=9420"}],"version-history":[{"count":1,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9420\/revisions"}],"predecessor-version":[{"id":9438,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9420\/revisions\/9438"}],"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=9420"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}