{"id":13551,"date":"2026-09-02T13:16:55","date_gmt":"2026-09-02T11:16:55","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/werkzeuge\/redirect-rule-generator-htaccess-nginx\/"},"modified":"2026-09-02T13:16:55","modified_gmt":"2026-09-02T11:16:55","slug":"redirect-rule-generator-htaccess-nginx","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/redirect-rule-generator-htaccess-nginx\/","title":{"rendered":"301-Redirect-Regel-Generator (.htaccess, nginx, WordPress)"},"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;\">Alte und neue Adressen einf\u00fcgen, ein Paar je Zeile, getrennt durch Pfeil, Tabulator oder Komma. Alles l\u00e4uft im Browser. Paare mit demselben Ordnerwechsel fallen zu einer Pr\u00e4fixregel mit Platzhalter zusammen, so wie es eine umbenannte Rubrik braucht. Eine neue Adresse, die selbst eine alte ist, ergibt eine Kette; der Generator l\u00e4sst die erste auf die letzte zeigen, damit kein Besucher die Kette abl\u00e4uft. Schleifen und widerspr\u00fcchliche Ziele werden gemeldet und weggelassen.<\/p>\n<div style=\"margin-bottom: 14px;\">\n        <label for=\"rg-paare\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Alte Adresse -&gt; neue Adresse, ein Paar je Zeile<\/label><br \/>\n        <textarea id=\"rg-paare\" style=\"width: 100%; height: 190px; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; box-sizing: border-box; font-family: monospace; font-size: 0.85rem; resize: vertical;\">\/blog\/en\/wordpress-hacks\/ -> \/blog\/en\/wordpress-plugins-tricks\/<br \/>\n\/blog\/en\/wordpress-hacks\/speed-up\/ -> \/blog\/en\/wordpress-plugins-tricks\/speed-up\/<br \/>\n\/alte-seite.html -> \/neue-seite\/<br \/>\n\/produkt\/alt -> \/produkt\/neu<br \/>\n\/produkt\/neu -> \/shop\/produkt-neu\/<br \/>\nhttps:\/\/old.example\/archive\/2019\/ -> https:\/\/www.example.com\/blog\/<\/textarea>\n    <\/div>\n<div style=\"display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px 18px; margin-bottom: 14px;\">\n<div>\n            <label for=\"rg-format\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Ausgabe<\/label><br \/>\n            <select id=\"rg-format\" class=\"form-control\" style=\"width: 100%; padding: 9px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px;\"><option value=\"htaccess\">Apache .htaccess (RewriteRule)<\/option><option value=\"redirect\">Apache .htaccess (Redirect \/ RedirectMatch)<\/option><option value=\"nginx\">nginx (location \/ rewrite)<\/option><option value=\"nginx_map\">nginx (map + return)<\/option><option value=\"wordpress\">WordPress (functions.php, template_redirect)<\/option><\/select>\n        <\/div>\n<div>\n            <label for=\"rg-code\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Statuscode<\/label><br \/>\n            <select id=\"rg-code\" class=\"form-control\" style=\"width: 100%; padding: 9px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px;\"><option value=\"301\">301<\/option><option value=\"308\">308<\/option><option value=\"302\">302<\/option><option value=\"307\">307<\/option><\/select>\n        <\/div>\n<div style=\"display: flex; flex-direction: column; gap: 6px; justify-content: flex-end;\">\n            <label style=\"color: var(--text-secondary, #a0a0b0); font-size: 0.85rem;\"><input id=\"rg-qsa\" type=\"checkbox\" checked style=\"margin-right: 6px;\">Abfrageteil durchreichen<\/label><br \/>\n            <label style=\"color: var(--text-secondary, #a0a0b0); font-size: 0.85rem;\"><input id=\"rg-praefix\" type=\"checkbox\" checked style=\"margin-right: 6px;\">Ordnerwechsel zu Pr\u00e4fixregeln zusammenfassen<\/label><br \/>\n            <label style=\"color: var(--text-secondary, #a0a0b0); font-size: 0.85rem;\"><input id=\"rg-glaetten\" type=\"checkbox\" checked style=\"margin-right: 6px;\">Ketten auf das Endziel gl\u00e4tten<\/label>\n        <\/div>\n<\/p><\/div>\n<p style=\"color: var(--text-secondary, #a0a0b0); font-size: 0.82rem; margin-bottom: 16px;\">Nichts, was hier eingegeben wird, verl\u00e4sst den Browser. Die Regeln entstehen lokal.<\/p>\n<p>    <button id=\"rg-btn\" class=\"button\" style=\"background: var(--accent, #7ee787); color: var(--on-accent, #0b1114); border: none; padding: 12px 24px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Regeln erzeugen<\/button><\/p>\n<div id=\"rg-ausgabe\" style=\"margin-top: 22px;\" data-mp-mask><\/div>\n<p style=\"color: var(--text-secondary, #a0a0b0); font-size: 0.8rem; margin: 24px 0 0;\">Grenzen, die dazugeh\u00f6ren: Pr\u00e4fixregeln treffen alles unterhalb des alten Ordners, auch nie aufgelistete Adressen; ein Paar mit Abfrageteil wird bei Apache zu einer RewriteCond und bei nginx zu einem if, was gewollt, aber nicht elegant ist; und eine Regel, die auf einen Host leitet, auf dem dieselben Regeln wieder greifen, l\u00e4uft in eine Schleife, die nur ein Lauf gegen die echte Kette zeigt.<\/p>\n<\/div>\n<p><script>\n(function () {\n    'use strict';<\/p>\n<p>    const T = {\"einleitung\":\"Alte und neue Adressen einf\u00fcgen, ein Paar je Zeile, getrennt durch Pfeil, Tabulator oder Komma. Alles l\u00e4uft im Browser. Paare mit demselben Ordnerwechsel fallen zu einer Pr\u00e4fixregel mit Platzhalter zusammen, so wie es eine umbenannte Rubrik braucht. Eine neue Adresse, die selbst eine alte ist, ergibt eine Kette; der Generator l\u00e4sst die erste auf die letzte zeigen, damit kein Besucher die Kette abl\u00e4uft. Schleifen und widerspr\u00fcchliche Ziele werden gemeldet und weggelassen.\",\"label_paare\":\"Alte Adresse -\\u003E neue Adresse, ein Paar je Zeile\",\"label_format\":\"Ausgabe\",\"label_code\":\"Statuscode\",\"label_qsa\":\"Abfrageteil durchreichen\",\"label_praefix\":\"Ordnerwechsel zu Pr\u00e4fixregeln zusammenfassen\",\"label_glaetten\":\"Ketten auf das Endziel gl\u00e4tten\",\"datenschutz\":\"Nichts, was hier eingegeben wird, verl\u00e4sst den Browser. Die Regeln entstehen lokal.\",\"knopf\":\"Regeln erzeugen\",\"grenze\":\"Grenzen, die dazugeh\u00f6ren: Pr\u00e4fixregeln treffen alles unterhalb des alten Ordners, auch nie aufgelistete Adressen; ein Paar mit Abfrageteil wird bei Apache zu einer RewriteCond und bei nginx zu einem if, was gewollt, aber nicht elegant ist; und eine Regel, die auf einen Host leitet, auf dem dieselben Regeln wieder greifen, l\u00e4uft in eine Schleife, die nur ein Lauf gegen die echte Kette zeigt.\",\"zusammenfassung\":\"{gelesen} Paar(e) gelesen, {paare} brauchbar, {praefixe} Pr\u00e4fixregel(n), {einzeln} Einzelregel(n).\",\"h_fehler\":\"Nicht lesbare Zeilen\",\"h_hinweise\":\"Pr\u00fcfungen\",\"h_praefixe\":\"Pr\u00e4fixregeln\",\"h_ausgabe\":\"Regeln\",\"h_ausgabe_htaccess\":\"Apache .htaccess (mod_rewrite)\",\"h_ausgabe_redirect\":\"Apache .htaccess (mod_alias)\",\"h_ausgabe_nginx\":\"nginx-Serverblock\",\"h_ausgabe_nginx_map\":\"nginx map\",\"h_ausgabe_wordpress\":\"WordPress functions.php\",\"kopieren\":\"Kopieren\",\"kopiert\":\"Kopiert\",\"kommentar_kopf\":\"Erzeugt vom 301-Redirect-Regel-Generator, lukaswojcik.com\",\"kommentar_query\":\"Abfrageteil ist mit mod_alias nicht pr\u00fcfbar, daf\u00fcr das RewriteRule-Format verwenden:\",\"fehler_form\":\"Zeile {zeile}: kein Adresspaar ({roh}).\",\"fehler_host\":\"Zeile {zeile}: der Hostteil ist unbrauchbar ({roh}).\",\"hinweis_doppelt\":\"Zeile {zeile}: {alt} steht zweimal mit demselben Ziel, die Wiederholung wurde weggelassen.\",\"hinweis_widerspruch\":\"Zeile {zeile}: {alt} zeigt schon auf {a}, diese Zeile will {b}. Das zweite Ziel wurde weggelassen.\",\"hinweis_selbst\":\"Zeile {zeile}: {alt} zeigt auf sich selbst und wurde weggelassen.\",\"hinweis_kette\":\"Zeile {zeile}: Kette {kette}. Gl\u00e4tten einschalten oder die erste Adresse auf die letzte zeigen lassen.\",\"hinweis_geglaettet\":\"Zeile {zeile}: Kette {kette} gegl\u00e4ttet, {alt} zeigt jetzt auf {ziel}.\",\"hinweis_schleife\":\"Zeile {zeile}: Schleife {kette}. Alle Adressen der Schleife wurden weggelassen; der Server w\u00fcrde mit ERR_TOO_MANY_REDIRECTS antworten.\",\"hinweis_format_htaccess\":\"Geh\u00f6rt in der .htaccess \u00fcber den WordPress-Block (alles zwischen BEGIN und END WordPress wird beim Speichern der Permalinks \u00fcberschrieben). Muster gelten ohne f\u00fchrenden Schr\u00e4gstrich und relativ zum Ordner der .htaccess; QSA reicht den Abfrageteil durch, NE bewahrt kodierte Zeichen. Vor dem Verlassen mit curl -I pr\u00fcfen.\",\"hinweis_format_redirect\":\"mod_alias l\u00e4uft vor mod_rewrite und kann den Abfrageteil nicht sehen. Redirect trifft den Pfad und alles darunter, deshalb stehen Einzelseiten mit dem exakten Pfad; RedirectMatch nimmt einen regul\u00e4ren Ausdruck.\",\"hinweis_format_nginx\":\"Geh\u00f6rt in den Serverblock. location = trifft genau einen Pfad, location ^~ ein Pr\u00e4fix. permanent ist 301, redirect 302; andere Codes gehen \u00fcber return. Nach nginx -t die Konfiguration neu laden.\",\"hinweis_format_nginx_map\":\"Die map geh\u00f6rt in den http-Block, das if in den Serverblock. Ein Nachschlagen je Anfrage, keine Kette aus location-Bl\u00f6cken; regul\u00e4re Ausdr\u00fccke in der map beginnen mit ~.\",\"hinweis_format_wordpress\":\"L\u00e4uft bei template_redirect, also nur f\u00fcr Anfragen, die WordPress erreichen; ein Seitencache liefert gecachte Seiten aus, bevor dieser Code l\u00e4uft. Exakte Treffer zuerst, Pr\u00e4fixe danach. wp_redirect braucht das exit.\"};<\/p>\n<p>    \/\/ =========================================================================\n    \/\/ Reine Logik: parsen, pruefen, glaetten, Praefixe, erzeugen\n    \/\/ =========================================================================<\/p>\n<p>    function trennen(zeile) {\n        var z = zeile.trim();\n        if (!z || z.charAt(0) === '#') { return null; }\n        var m = z.match(\/^(\\S+)\\s*(?:->|=>|\u2192|\\t|,|;|\\s+)\\s*(\\S+)$\/);\n        if (!m) { return { fehler: 'form', roh: z }; }\n        return { alt: m[1], neu: m[2] };\n    }\n    function zerlegen(adresse) {\n        var m = adresse.match(\/^(https?:\\\/\\\/[^\\\/?#]+)?([^?#]*)(\\?[^#]*)?(#.*)?$\/i);\n        if (!m) { return null; }\n        var pfad = m[2] || '\/';\n        if (pfad.charAt(0) !== '\/') { pfad = '\/' + pfad; }\n        return { host: m[1] ? m[1].toLowerCase() : '', pfad: pfad, query: m[3] || '', voll: (m[1] ? m[1].toLowerCase() : '') + pfad + (m[3] || '') };\n    }\n    function parsen(text) {\n        var paare = [], fehler = [];\n        String(text || '').split(\/\\r?\\n\/).forEach(function (zeile, i) {\n            var t = trennen(zeile);\n            if (t === null) { return; }\n            if (t.fehler) { fehler.push({ zeile: i + 1, art: 'form', roh: t.roh }); return; }\n            var a = zerlegen(t.alt), n = zerlegen(t.neu);\n            if (!a || !n) { fehler.push({ zeile: i + 1, art: 'form', roh: zeile.trim() }); return; }\n            if (a.host && !\/^https?:\\\/\\\/[a-z0-9.-]+(:\\d+)?$\/i.test(a.host)) { fehler.push({ zeile: i + 1, art: 'host', roh: t.alt }); return; }\n            paare.push({ zeile: i + 1, alt: a, neu: n, altRoh: t.alt, neuRoh: t.neu });\n        });\n        return { paare: paare, fehler: fehler };\n    }<\/p>\n<p>    \/** Doppelte, Selbstverweise, Ketten und Schleifen finden; Ketten optional glaetten. *\/\n    function pruefen(paare, glaetten) {\n        var hinweise = [];\n        var nachAlt = {};\n        var raus = [];\n        paare.forEach(function (p) {\n            var k = p.alt.voll;\n            if (nachAlt[k]) {\n                if (nachAlt[k].neu.voll === p.neu.voll) { hinweise.push({ art: 'doppelt', zeile: p.zeile, alt: p.altRoh }); }\n                else { hinweise.push({ art: 'widerspruch', zeile: p.zeile, alt: p.altRoh, a: nachAlt[k].neuRoh, b: p.neuRoh }); }\n                return;\n            }\n            if (p.alt.voll === p.neu.voll) { hinweise.push({ art: 'selbst', zeile: p.zeile, alt: p.altRoh }); return; }\n            nachAlt[k] = p;\n            raus.push(p);\n        });\n        \/\/ Ketten: neu ist wieder ein alt\n        raus.forEach(function (p) {\n            var ziel = p.neu, schritte = 0, gesehen = {};\n            gesehen[p.alt.voll] = true;\n            var kette = [p.altRoh];\n            while (nachAlt[ziel.voll] && schritte < 20) {\n                if (gesehen[ziel.voll]) { hinweise.push({ art: 'schleife', zeile: p.zeile, alt: p.altRoh, kette: kette.concat([ziel.voll]).join(' -> ') }); p.schleife = true; return; }\n                gesehen[ziel.voll] = true;\n                kette.push(ziel.voll);\n                ziel = nachAlt[ziel.voll].neu;\n                schritte++;\n            }\n            if (schritte > 0) {\n                kette.push(ziel.voll);\n                if (glaetten) { hinweise.push({ art: 'geglaettet', zeile: p.zeile, alt: p.altRoh, kette: kette.join(' -> '), ziel: ziel.voll }); p.neu = ziel; p.neuRoh = ziel.voll; }\n                else { hinweise.push({ art: 'kette', zeile: p.zeile, alt: p.altRoh, kette: kette.join(' -> ') }); }\n            }\n        });\n        return { paare: raus.filter(function (p) { return !p.schleife; }), hinweise: hinweise };\n    }<\/p>\n<p>    \/** Paare, die denselben Ordnerwechsel teilen, zu einer Praefixregel zusammenfassen. *\/\n    function praefixe(paare) {\n        var gruppen = {}, einzeln = [];\n        paare.forEach(function (p) {\n            if (p.alt.host !== p.neu.host && p.neu.host && p.alt.host) { einzeln.push(p); return; }\n            var a = p.alt.pfad.split('\/'), n = p.neu.pfad.split('\/');\n            \/\/ gemeinsamer Rest von hinten\n            var rest = 0;\n            while (rest < a.length - 1 &#038;&#038; rest < n.length - 1 &#038;&#038; a[a.length - 1 - rest] === n[n.length - 1 - rest]) { rest++; }\n            if (rest === 0 || p.alt.query || p.neu.query) { einzeln.push(p); return; }\n            var altPraefix = a.slice(0, a.length - rest).join('\/') + '\/';\n            var neuPraefix = n.slice(0, n.length - rest).join('\/') + '\/';\n            if (altPraefix === '\/' || altPraefix === neuPraefix) { einzeln.push(p); return; }\n            var k = (p.alt.host || '') + altPraefix + ' => ' + (p.neu.host || '') + neuPraefix;\n            if (!gruppen[k]) { gruppen[k] = { altHost: p.alt.host, alt: altPraefix, neuHost: p.neu.host, neu: neuPraefix, paare: [] }; }\n            gruppen[k].paare.push(p);\n        });\n        var regeln = [];\n        Object.keys(gruppen).forEach(function (k) {\n            var g = gruppen[k];\n            if (g.paare.length >= 2) { regeln.push(g); } else { einzeln.push(g.paare[0]); }\n        });\n        einzeln.sort(function (x, y) { return x.zeile - y.zeile; });\n        return { praefixe: regeln, einzeln: einzeln };\n    }<\/p>\n<p>    function reEsc(s) { return s.replace(\/[.*+?^${}()|[\\]\\\\]\/g, '\\\\$&'); }\n    function ohneSlash(p) { return p.replace(\/^\\\/\/, ''); }<\/p>\n<p>    function erzeugen(paare, praefixRegeln, format, code, qsa) {\n        var zeilen = [];\n        var flags = function (extra) { var f = ['L', 'R=' + code, 'NE']; if (qsa) { f.push('QSA'); } if (extra) { f = f.concat(extra); } return '[' + f.join(',') + ']'; };\n        var nginxCode = function () { return String(code); };\n        if (format === 'htaccess') {\n            zeilen.push('# ' + T.kommentar_kopf);\n            zeilen.push('RewriteEngine On');\n            praefixRegeln.forEach(function (g) {\n                var ziel = (g.neuHost || '') + g.neu + '$1';\n                zeilen.push('RewriteRule ^' + reEsc(ohneSlash(g.alt)) + '(.*)$ ' + ziel + ' ' + flags());\n            });\n            paare.forEach(function (p) {\n                var ziel = (p.neu.host || '') + p.neu.pfad + (p.neu.query || '');\n                if (p.alt.query) {\n                    zeilen.push('RewriteCond %{QUERY_STRING} ^' + reEsc(p.alt.query.slice(1)) + '$');\n                    zeilen.push('RewriteRule ^' + reEsc(ohneSlash(p.alt.pfad)) + '\/?$ ' + ziel + (p.neu.query ? '' : '?') + ' ' + flags().replace(',QSA', ''));\n                } else {\n                    zeilen.push('RewriteRule ^' + reEsc(ohneSlash(p.alt.pfad)) + '\/?$ ' + ziel + ' ' + flags());\n                }\n            });\n        } else if (format === 'redirect') {\n            zeilen.push('# ' + T.kommentar_kopf);\n            praefixRegeln.forEach(function (g) {\n                zeilen.push('RedirectMatch ' + code + ' ^' + reEsc(g.alt) + '(.*)$ ' + (g.neuHost || '') + g.neu + '$1');\n            });\n            paare.forEach(function (p) {\n                if (p.alt.query) { zeilen.push('# ' + T.kommentar_query + ' ' + p.altRoh); return; }\n                zeilen.push('Redirect ' + code + ' ' + p.alt.pfad + ' ' + (p.neu.host || '') + p.neu.pfad + (p.neu.query || ''));\n            });\n        } else if (format === 'nginx') {\n            zeilen.push('# ' + T.kommentar_kopf);\n            praefixRegeln.forEach(function (g) {\n                zeilen.push('location ^~ ' + g.alt + ' {');\n                zeilen.push('    rewrite ^' + reEsc(g.alt) + '(.*)$ ' + (g.neuHost || '$scheme:\/\/$host') + g.neu + '$1 ' + (code === '301' ? 'permanent' : 'redirect') + ';');\n                zeilen.push('}');\n            });\n            paare.forEach(function (p) {\n                var ziel = (p.neu.host || '$scheme:\/\/$host') + p.neu.pfad + (p.neu.query ? p.neu.query : (qsa ? '$is_args$args' : ''));\n                if (p.alt.query) {\n                    zeilen.push('if ($request_uri = \"' + p.alt.pfad + p.alt.query + '\") { return ' + nginxCode() + ' ' + (p.neu.host || '$scheme:\/\/$host') + p.neu.pfad + (p.neu.query || '') + '; }');\n                } else {\n                    zeilen.push('location = ' + p.alt.pfad + ' { return ' + nginxCode() + ' ' + ziel + '; }');\n                }\n            });\n        } else if (format === 'nginx_map') {\n            zeilen.push('# ' + T.kommentar_kopf);\n            zeilen.push('# http { ... }');\n            zeilen.push('map $request_uri $lw_redirect {');\n            zeilen.push('    default \"\";');\n            praefixRegeln.forEach(function (g) {\n                zeilen.push('    ~^' + reEsc(g.alt) + '(?<rest>.*)$ ' + (g.neuHost || '') + g.neu + '$rest;');\n            });\n            paare.forEach(function (p) {\n                zeilen.push('    ' + (p.alt.query ? '' : '') + p.alt.pfad + (p.alt.query || '') + ' ' + (p.neu.host || '') + p.neu.pfad + (p.neu.query || '') + ';');\n            });\n            zeilen.push('}');\n            zeilen.push('# server { ... }');\n            zeilen.push('if ($lw_redirect) { return ' + nginxCode() + ' $lw_redirect; }');\n        } else if (format === 'wordpress') {\n            zeilen.push('<?php');\n            zeilen.push('\/\/ ' + T.kommentar_kopf);\n            zeilen.push(\"add_action( 'template_redirect', function () {\");\n            zeilen.push(\"    $pfad = rtrim( (string) parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ), '\/' );\");\n            zeilen.push(\"    $query = (string) parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY );\");\n            zeilen.push('    $praefixe = array(');\n            praefixRegeln.forEach(function (g) { zeilen.push(\"        '\" + g.alt + \"' => '\" + (g.neuHost || '') + g.neu + \"',\"); });\n            zeilen.push('    );');\n            zeilen.push('    $einzeln = array(');\n            paare.forEach(function (p) { zeilen.push(\"        '\" + p.alt.pfad.replace(\/\\\/$\/, '') + (p.alt.query || '') + \"' => '\" + (p.neu.host || '') + p.neu.pfad + (p.neu.query || '') + \"',\"); });\n            zeilen.push('    );');\n            zeilen.push(\"    $schluessel = $pfad === '' ? '\/' : $pfad;\");\n            zeilen.push(\"    if ( isset( $einzeln[ $schluessel . ( $query !== '' ? '?' . $query : '' ) ] ) ) { $ziel = $einzeln[ $schluessel . ( $query !== '' ? '?' . $query : '' ) ]; }\");\n            zeilen.push(\"    elseif ( isset( $einzeln[ $schluessel ] ) ) { $ziel = $einzeln[ $schluessel ]\" + (qsa ? \" . ( $query !== '' ? '?' . $query : '' )\" : '') + '; }');\n            zeilen.push('    else {');\n            zeilen.push('        foreach ( $praefixe as $alt => $neu ) {');\n            zeilen.push(\"            if ( strpos( $pfad . '\/', $alt ) === 0 ) { $ziel = $neu . substr( $pfad . '\/', strlen( $alt ) )\" + (qsa ? \" . ( $query !== '' ? '?' . $query : '' )\" : '') + '; break; }');\n            zeilen.push('        }');\n            zeilen.push('    }');\n            zeilen.push('    if ( ! empty( $ziel ) ) { wp_redirect( $ziel, ' + code + \" ); exit; }\");\n            zeilen.push('} );');\n        }\n        return zeilen.join('\\n');\n    }<\/p>\n<p>    function bauen(text, optionen) {\n        var o = optionen || {};\n        var p = parsen(text);\n        var g = pruefen(p.paare, o.glaetten !== false);\n        var pr = o.praefix !== false ? praefixe(g.paare) : { praefixe: [], einzeln: g.paare.slice() };\n        var ausgabe = erzeugen(pr.einzeln, pr.praefixe, o.format || 'htaccess', String(o.code || '301'), o.qsa !== false);\n        return { fehler: p.fehler, hinweise: g.hinweise, paare: g.paare.length, praefixe: pr.praefixe, einzeln: pr.einzeln.length, ausgabe: ausgabe, format: o.format || 'htaccess', gelesen: p.paare.length };\n    }<\/p>\n<p>    window.LW_TEST = window.LW_TEST || {};\n    window.LW_TEST.RG = { parsen: parsen, pruefen: pruefen, praefixe: praefixe, erzeugen: erzeugen, bauen: bauen, darstellen: null, T: T };<\/p>\n<p>    \/\/ =========================================================================\n    \/\/ Oberflaeche\n    \/\/ =========================================================================<\/p>\n<p>    var btn = document.getElementById('rg-btn');\n    var out = document.getElementById('rg-ausgabe');\n    if (!btn || !out) { return; }\n    function el(tag, stil, txt) { var e = document.createElement(tag); if (stil) { e.setAttribute('style', stil); } if (txt !== undefined && txt !== null) { e.textContent = txt; } return e; }\n    function wert(id) { var e = document.getElementById(id); return e ? e.value : ''; }\n    function haken(id) { var e = document.getElementById(id); return e ? e.checked : true; }\n    var UEBERSCHRIFT = 'font-family: \"Nunito Sans\", sans-serif; font-weight: 700; color: var(--text-primary, #e8e8ee); font-size: 0.95rem; margin: 22px 0 8px;';\n    var ZELLE = 'padding: 5px 12px 5px 0; color: var(--text-secondary, #a0a0b0); font-size: 0.87rem;';\n    var VORFORM = 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 6px; padding: 12px; color: var(--text-primary, #e8e8ee); font-family: monospace; font-size: 0.8rem; white-space: pre; overflow-x: auto; margin: 0;';\n    var GUT = ' color: #7ee787;', WARN = ' color: #ffa94d;', ROT = ' color: #ff7b72;', GRAU = ' color: #8a8a99;';\n    function text(k, d) { var t = T[k]; if (typeof t !== 'string') { return k; } return t.replace(\/\\{([a-z_]+)\\}\/g, function (m, x) { return d && d[x] !== undefined ? String(d[x]) : m; }); }<\/p>\n<p>    function darstellen(b, ziel) {\n        ziel.innerHTML = '';\n        ziel.appendChild(el('div', ZELLE + ' margin: 0 0 6px;' + (b.paare ? GRAU : WARN), text('zusammenfassung', { gelesen: b.gelesen, paare: b.paare, praefixe: b.praefixe.length, einzeln: b.einzeln })));\n        if (b.fehler.length) {\n            ziel.appendChild(el('div', UEBERSCHRIFT, T.h_fehler));\n            var lf = el('ul', 'margin: 0 0 0 18px; padding: 0; color: var(--text-secondary, #a0a0b0); font-size: 0.87rem; line-height: 1.6;');\n            b.fehler.forEach(function (f) { lf.appendChild(el('li', ROT, text('fehler_' + f.art, { zeile: f.zeile, roh: f.roh }))); });\n            ziel.appendChild(lf);\n        }\n        if (b.hinweise.length) {\n            ziel.appendChild(el('div', UEBERSCHRIFT, T.h_hinweise));\n            var lh = el('ul', 'margin: 0 0 0 18px; padding: 0; color: var(--text-secondary, #a0a0b0); font-size: 0.87rem; line-height: 1.6;');\n            b.hinweise.forEach(function (h) {\n                var stil = (h.art === 'schleife' || h.art === 'widerspruch') ? ROT : (h.art === 'geglaettet' ? GUT : WARN);\n                lh.appendChild(el('li', stil, text('hinweis_' + h.art, h)));\n            });\n            ziel.appendChild(lh);\n        }\n        if (b.praefixe.length) {\n            ziel.appendChild(el('div', UEBERSCHRIFT, T.h_praefixe));\n            var lp = el('ul', 'margin: 0 0 0 18px; padding: 0; color: var(--text-secondary, #a0a0b0); font-size: 0.87rem; font-family: monospace; line-height: 1.6;');\n            b.praefixe.forEach(function (g) { lp.appendChild(el('li', '', (g.altHost || '') + g.alt + ' -> ' + (g.neuHost || '') + g.neu + '  (' + g.paare.length + ')')); });\n            ziel.appendChild(lp);\n        }\n        ziel.appendChild(el('div', UEBERSCHRIFT, T['h_ausgabe_' + b.format] || T.h_ausgabe));\n        var pre = el('pre', VORFORM, b.ausgabe);\n        ziel.appendChild(pre);\n        var kopieren = el('button', 'margin-top: 8px; background: transparent; color: var(--accent, #7ee787); border: 1px solid var(--accent, #7ee787); padding: 7px 14px; border-radius: 6px; font-weight: 700; cursor: pointer; font-size: 0.82rem;', T.kopieren);\n        kopieren.className = 'button rg-kopieren';\n        kopieren.addEventListener('click', function () {\n            if (navigator.clipboard) { navigator.clipboard.writeText(b.ausgabe).then(function () { kopieren.textContent = T.kopiert; setTimeout(function () { kopieren.textContent = T.kopieren; }, 1500); }); }\n        });\n        ziel.appendChild(kopieren);\n        ziel.appendChild(el('p', ZELLE + GRAU + ' margin: 12px 0 0; line-height: 1.55;', T['hinweis_format_' + b.format] || ''));\n    }\n    window.LW_TEST.RG.darstellen = darstellen;<\/p>\n<p>    btn.addEventListener('click', function () {\n        darstellen(bauen(wert('rg-paare'), { format: wert('rg-format'), code: wert('rg-code'), qsa: haken('rg-qsa'), praefix: haken('rg-praefix'), glaetten: haken('rg-glaetten') }), out);\n    });\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Macht aus einer Liste alter und neuer Adressen Weiterleitungsregeln f\u00fcr Apache .htaccess, nginx oder einen WordPress-Hook. Fasst Paare mit demselben Ordnerwechsel zu einer Pr\u00e4fixregel zusammen, gl\u00e4ttet Ketten, sodass jede alte Adresse auf ihr Endziel zeigt, und weist Schleifen und Widerspr\u00fcche ab, bevor sie den Server erreichen.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":9283,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[92214,92289,92340,92277],"class_list":["post-13551","page","type-page","status-publish","hentry","tag-devops-de","tag-seo-de","tag-server-administration-de","tag-wordpress-de"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>301-Redirect-Regel-Generator (.htaccess, nginx, WordPress) - 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\/de\/werkzeuge\/redirect-rule-generator-htaccess-nginx\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"301-Redirect-Regel-Generator (.htaccess, nginx, WordPress) - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Macht aus einer Liste alter und neuer Adressen Weiterleitungsregeln f\u00fcr Apache .htaccess, nginx oder einen WordPress-Hook. Fasst Paare mit demselben Ordnerwechsel zu einer Pr\u00e4fixregel zusammen, gl\u00e4ttet Ketten, sodass jede alte Adresse auf ihr Endziel zeigt, und weist Schleifen und Widerspr\u00fcche ab, bevor sie den Server erreichen.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/redirect-rule-generator-htaccess-nginx\/\" \/>\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<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\\\/de\\\/werkzeuge\\\/redirect-rule-generator-htaccess-nginx\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/redirect-rule-generator-htaccess-nginx\\\/\",\"name\":\"301-Redirect-Regel-Generator (.htaccess, nginx, WordPress) - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-09-02T11:16:55+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/redirect-rule-generator-htaccess-nginx\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/redirect-rule-generator-htaccess-nginx\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/redirect-rule-generator-htaccess-nginx\\\/#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\\\/toolbox-2\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"301-Redirect-Regel-Generator (.htaccess, nginx, WordPress)\"}]},{\"@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":"301-Redirect-Regel-Generator (.htaccess, nginx, WordPress) - 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\/de\/werkzeuge\/redirect-rule-generator-htaccess-nginx\/","og_locale":"en_US","og_type":"article","og_title":"301-Redirect-Regel-Generator (.htaccess, nginx, WordPress) - Lukas Wojcik - Blog","og_description":"Macht aus einer Liste alter und neuer Adressen Weiterleitungsregeln f\u00fcr Apache .htaccess, nginx oder einen WordPress-Hook. Fasst Paare mit demselben Ordnerwechsel zu einer Pr\u00e4fixregel zusammen, gl\u00e4ttet Ketten, sodass jede alte Adresse auf ihr Endziel zeigt, und weist Schleifen und Widerspr\u00fcche ab, bevor sie den Server erreichen.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/redirect-rule-generator-htaccess-nginx\/","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","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/redirect-rule-generator-htaccess-nginx\/","url":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/redirect-rule-generator-htaccess-nginx\/","name":"301-Redirect-Regel-Generator (.htaccess, nginx, WordPress) - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-09-02T11:16:55+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/redirect-rule-generator-htaccess-nginx\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/redirect-rule-generator-htaccess-nginx\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/redirect-rule-generator-htaccess-nginx\/#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\/toolbox-2\/"},{"@type":"ListItem","position":3,"name":"301-Redirect-Regel-Generator (.htaccess, nginx, WordPress)"}]},{"@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\/13551","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=13551"}],"version-history":[{"count":0,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/13551\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9283"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=13551"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=13551"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}