Akzente und ñ in Slugs: Warum Artikel einen 404-Fehler liefern
Ein Artikel geht online, der Titel sieht gut aus … doch der öffentliche Link liefert 404. Keine Hinweise in den PHP-Fehlerprotokollen, kein Problem in der Datenbank: Der Artikel existiert tatsächlich. Schuld ist ein einziger Buchstabe, das ñ in „La Niña“. Genau das passierte Anfang November 2026 in unserem Blog. Die Ursache lohnt eine genaue Analyse.
Das Prinzip ist einfach: Die Adresse eines Artikels wird aus seinem Titel erzeugt. Das Ergebnis heißt Slug (oder Alias). Lässt die erzeugende Funktion ein Zeichen durch, das der Webserver nicht zuordnen kann, ist der Artikel unerreichbar, obwohl alles korrekt aussieht.
Das Szenario: Ein Slug behält sein ñ
Unsere Funktion zur Alias-Erzeugung ersetzte Akzente einzeln anhand einer handgeschriebenen Tabelle: é wird e, à wird a, ç wird c und so weiter. Französisch war gut abgedeckt, Spanisch und Portugiesisch jedoch nicht: ñ, í, ó, ú, ò, ö und ã blieben unverändert. Der Titel „La Niña: das verborgene wirtschaftliche Risiko hinter El Niño“ erzeugte deshalb einen Alias mit ñ.
Auf dem Server leitet die Datei .htaccess Artikel mit einer Regel dieser Art weiter:
RewriteRule ^article/([a-zA-Z0-9-_]+)$ /index.php?page_theme=article&alias=$1 [L]Die Zeichenklasse [a-zA-Z0-9-_] akzeptiert nur ASCII-Buchstaben, Ziffern, Bindestriche und Unterstriche. Der Browser sendet ñ als %C3%B1; Apache dekodiert es vor dem Vergleich, und die entstehenden UTF-8-Bytes gehören eindeutig nicht zur Klasse. Die Regel greift nicht, keine andere übernimmt, und die Antwort ist eine 404-Seite.
Was es verschlimmert: der Anker am Ende
Einige Tage zuvor hatten wir am Ende des Musters $ ergänzt. Ohne diesen Anker genügte der Regel der Anfang einer Adresse: Bei /article/la-niña-… erfasste sie la-ni und schnitt den Rest stillschweigend ab. Je nachdem, wie das CMS einen Teilalias auflöst, erscheint dann eine falsche Seite, aus Googles Sicht ein Duplikat oder ein rätselhafter 404-Fehler. Der Anker macht den Fehler sichtbar, was besser ist. Die Ursache behebt er nicht: Der Slug muss sauber sein.
Warum strtolower nicht hilft
Ein Teil unserer Ersetzungen betraf nur Kleinbuchstaben, weil wir vorherige Kleinschreibung voraussetzten. Doch strtolower() verarbeitet nur ASCII: Seit PHP 8.2 ist die Funktion nicht einmal mehr von der Locale abhängig und lässt Ñ und É unverändert. Ein Titel in Großbuchstaben wie „EL NIÑO“ behielt seinen Akzent daher auch nach der Korrektur für Kleinbuchstaben. Man muss entweder Großbuchstaben ausdrücklich behandeln oder mb_strtolower($titre, 'UTF-8') verwenden.
UTF-8, latin1 und wirkungslose Ersetzungen
Zwei weitere Fallen lauern bei selbst erstellten Ersetzungstabellen.
- Doppelte Kodierung. Lief ein Titel über eine MySQL-Verbindung mit latin1, obwohl die Datenbank UTF-8 verwendet (oder umgekehrt), wird aus ñ die Zeichenfolge
ñ. Dann findetstr_replace('ñ', 'n', …)nichts mehr und der Slug enthält fehlerhafte Reste. Prüfen Sie zuerst den Zeichensatz der Verbindung (utf8mb4überall), bevor Sie die Funktion verdächtigen. - Reguläre Ausdrücke ohne Modifikator
u.preg_replace('/[àâä]/', 'a', $s)arbeitet byteweise: Die Klasse enthält sechs Bytes und „à“ (zwei Bytes) wird zu „aa“. Mit/[àâä]/uverarbeitet PCRE Zeichen, und das Ergebnis stimmt.
Saubere Slugs mit PHP erzeugen
Statt eine Tabelle endlos zu erweitern, sollte man Unicode-Transliteration verwenden und anschließend mit einer strengen Positivliste filtern.
Die robuste Lösung: die Erweiterung intl
function slugifier(string $titre): string
{
// Alle Schriften nach Latein, dann ASCII transliterieren und kleinschreiben
$s = transliterator_transliterate('Any-Latin; Latin-ASCII; Lower()', $titre);
// Positivliste: Alles außer a-z und 0-9 durch Bindestriche ersetzen
$s = preg_replace('/[^a-z0-9]+/', '-', $s);
return trim($s, '-');
}Diese Variante verarbeitet ñ, ß (wird zu ss), œ, ø und ł sowie nichtlateinische Schriften, die Any-Latin romanisiert. Sie hängt von keiner Server-Locale ab.
Alternativen und ihre Grenzen
iconv('UTF-8', 'ASCII//TRANSLIT', $s)hängt von der aktiven Locale ab: Ohnesetlocale(LC_CTYPE, 'fr_FR.UTF-8')liefert glibc „Ni?a“, unter Alpine (musl) ist das Ergebnis noch weniger zuverlässig.Normalizer::normalize($s, Normalizer::FORM_D)gefolgt vonpreg_replace('/p{Mn}+/u', '', $s)entfernt Akzente, lässt aber œ, æ, ß und ø stehen, da diese im Unicode-Sinn keine akzentuierten Buchstaben sind.- Eine eigene Tabelle ist ohne intl-Erweiterung weiterhin möglich, sofern am Ende dieselbe Positivliste angewendet wird: Sie garantiert das Ergebnis.
Die eigentliche Regel: Slug und Rewrite müssen übereinstimmen
Das Grundproblem waren zwei unterschiedliche Definitionen eines „erlaubten Zeichens“: eine implizite in der PHP-Funktion und eine explizite in .htaccess. Die Regel lautet: Die Positivliste des Slug-Generators muss exakt zur Zeichenklasse der Rewrite-Regel passen. Akzeptiert .htaccess die Klasse [a-z0-9-], muss die Funktion mit preg_replace('/[^a-z0-9]+/', '-', …) enden, ohne Ausnahme.
Noch ein Tipp: Setzen Sie den Bindestrich in einer Zeichenklasse ans Ende, also [a-zA-Z0-9_-]. In der Mitte funktioniert er möglicherweise dank der Toleranz der Engine, sieht aber wie ein Bereich aus und führt irgendwann zu Fehlern.
Vorbeugen statt reparieren
- Mit schwierigen Titeln testen: „La Niña“, „EL NIÑO“, „Ça coûte 5 €“, „Œuvre complète“, „Straße“ und ein japanischer Titel. Jeder Slug muss zum Muster der Apache-Regel passen.
- Ersatzwert für leere Slugs vorsehen: Ein Titel nur aus Symbolen ergibt eine leere Zeichenfolge. Verwenden Sie dann die Artikel-ID.
- Bestehende Aliase prüfen: Eine Abfrage wie
SELECT alias FROM articles WHERE alias NOT REGEXP '^[a-z0-9-]+$'zeigt bereits defekte Adressen in Sekunden. - Nach der Veröffentlichung kontrollieren: Ein einfacher HTTP-Aufruf der öffentlichen URL, der Status 200 liefern sollte, hätte den Fehler sofort aufgedeckt.
Und die Suchmaschinenoptimierung?
Google kann URLs mit Unicode-Zeichen indexieren; ein ñ in einer Adresse ist also nicht grundsätzlich verboten. Beim Kopieren in Nachrichten oder soziale Netzwerke erscheint die Adresse jedoch oft kodiert, mit wenig ansprechenden Folgen wie %C3%B1. Ein kurzer, lesbarer ASCII-Slug bleibt die sicherste Wahl, sofern die erzeugende Funktion zuverlässig filtert.
In unserem Fall waren drei Schritte nötig: die Transliteration für Klein- und Großbuchstaben vervollständigen, den Alias des betroffenen Artikels in der Datenbank korrigieren und den Endanker in .htaccess beibehalten, damit künftige Fehler sichtbar werden, statt unbemerkt abgeschnitten zu werden.
German
French
English
Spanish
Hindi
Italian
Japanese
Korean
Norwegian
Chinese


