स्लग में ñ और 404 त्रुटि: PHP में समाधान
लेख प्रकाशित हुआ, शीर्षक सही था… लेकिन सार्वजनिक लिंक पर 404 त्रुटि मिली। PHP त्रुटि लॉग में कुछ नहीं, डेटाबेस में भी कोई समस्या नहीं: लेख मौजूद था। दोषी केवल एक अक्षर था, «La Niña» का ñ। नवंबर 2026 की शुरुआत में हमारे ब्लॉग पर ऐसा हुआ और इसकी वजह विस्तार से समझने लायक है।
सिद्धांत सरल है: लेख का वेब पता उसके शीर्षक से बनता है, जिसे slug (या alias) कहते हैं। यदि इसे बनाने वाला फ़ंक्शन ऐसा अक्षर रहने दे जिसे वेब सर्वर रूट नहीं कर सकता, तो सब कुछ सही दिखने के बावजूद लेख नहीं खुलता।
समस्या: स्लग में ñ रह गया
हमारा alias बनाने वाला फ़ंक्शन हाथ से बनाई गई तालिका से उच्चारण चिह्न एक-एक करके बदलता था: é से e, à से a, ç से c आदि। यह फ़्रेंच के लिए पर्याप्त था, लेकिन स्पैनिश या पुर्तगाली के लिए नहीं: ñ, í, ó, ú, ò, ö और ã वैसे ही रहते थे। इसलिए «La Niña : le risque économique caché derrière El Niño» शीर्षक के alias में ñ बचा रहा।
सर्वर पर .htaccess फ़ाइल लेखों को लगभग इस नियम से रूट करती है:
RewriteRule ^article/([a-zA-Z0-9-_]+)$ /index.php?page_theme=article&alias=$1 [L][a-zA-Z0-9-_] वर्ग केवल ASCII अक्षर, अंक, हाइफ़न और अंडरस्कोर स्वीकार करता है। ब्राउज़र ñ को %C3%B1 के रूप में भेजता है, Apache मिलान से पहले इसे डीकोड करता है और प्राप्त दो UTF-8 बाइट इस वर्ग में नहीं आते। नियम लागू नहीं होता, दूसरा नियम भी काम नहीं आता और 404 पृष्ठ खुलता है।
समस्या बढ़ाने वाली बात: अंत का एंकर
कुछ दिन पहले हमने पैटर्न के अंत में $ जोड़ा था। इसके बिना नियम पते की शुरुआत ही जाँचता था: /article/la-niña-… के लिए वह la-ni पकड़कर बाकी भाग चुपचाप काट देता था। CMS आंशिक alias कैसे हल करता है, उसके अनुसार गलत पृष्ठ, Google की नज़र में डुप्लिकेट पृष्ठ या समझ न आने वाली 404 त्रुटि मिल सकती थी। एंकर त्रुटि स्पष्ट करता है, लेकिन मूल कारण नहीं हटाता: स्लग साफ़ होना चाहिए।
strtolower क्यों पर्याप्त नहीं है
हमारे कुछ प्रतिस्थापन केवल छोटे अक्षरों के लिए थे, क्योंकि पहले सभी अक्षर छोटे करने की उम्मीद थी। लेकिन strtolower() सिर्फ ASCII संभालता है: PHP 8.2 से यह locale पर भी निर्भर नहीं है और Ñ या É को नहीं बदलता। इसलिए «EL NIÑO» जैसे बड़े अक्षरों वाले शीर्षक में छोटे अक्षरों के लिए सुधार करने के बाद भी चिह्न बचता था। बड़े अक्षरों को अलग से संभालें या mb_strtolower($titre, 'UTF-8') इस्तेमाल करें।
UTF-8, latin1 और असफल प्रतिस्थापन
हाथ से बनाई प्रतिस्थापन तालिकाओं में दो और जोखिम हैं।
- डबल एन्कोडिंग। यदि शीर्षक latin1 वाली MySQL कनेक्शन से गुज़रा, जबकि डेटाबेस UTF-8 में था (या उलटा), तो ñ बदलकर
ñबन जाता है। आपकाstr_replace('ñ', 'n', …)कुछ नहीं ढूँढ़ पाता और स्लग में बिगड़े अक्षर आ जाते हैं। फ़ंक्शन को दोष देने से पहले कनेक्शन का कैरेक्टर सेट जाँचें (हर जगहutf8mb4)। uमॉडिफ़ायर के बिना रेगुलर एक्सप्रेशन।preg_replace('/[àâä]/', 'a', $s)बाइट के आधार पर काम करता है: वर्ग में छह बाइट हैं और दो बाइट वाला «à» बदलकर «aa» बन जाता है।/[àâä]/uके साथ PCRE अक्षरों के आधार पर काम करता है और परिणाम सही आता है।
PHP में सही स्लग कैसे बनाएँ
तालिका को लगातार बढ़ाने के बजाय Unicode लिप्यंतरण इस्तेमाल करें और फिर सख़्त अनुमत-अक्षर सूची से फ़िल्टर करें।
भरोसेमंद तरीका: intl एक्सटेंशन
function slugifier(string $titre): string
{
// हर लिपि को लैटिन, फिर ASCII में बदलें और छोटे अक्षर करें
$s = transliterator_transliterate('Any-Latin; Latin-ASCII; Lower()', $titre);
// अनुमत सूची: a-z या 0-9 के अलावा सब कुछ हाइफ़न बनेगा
$s = preg_replace('/[^a-z0-9]+/', '-', $s);
return trim($s, '-');
}यह तरीका ñ, ß (जो ss बनता है), œ, ø, ł और गैर-लैटिन लिपियों को भी संभालता है, जिन्हें Any-Latin रोमन लिपि में बदलता है। यह सर्वर के locale पर निर्भर नहीं है।
विकल्प और उनकी सीमाएँ
iconv('UTF-8', 'ASCII//TRANSLIT', $s)सक्रिय locale पर निर्भर है:setlocale(LC_CTYPE, 'fr_FR.UTF-8')के बिना glibc «Ni?a» देता है और Alpine (musl) पर परिणाम और कम भरोसेमंद है।Normalizer::normalize($s, Normalizer::FORM_D)के बादpreg_replace('/p{Mn}+/u', '', $s)लगाने से उच्चारण चिह्न हटते हैं, लेकिन œ, æ, ß और ø बचते हैं, क्योंकि Unicode में ये उच्चारण चिह्न वाले अक्षर नहीं हैं।- intl उपलब्ध न हो तो हाथ से बनाई तालिका भी चल सकती है, बशर्ते अंत में वही अनुमत-अक्षर सूची लगाई जाए: परिणाम की गारंटी वही देती है।
मुख्य नियम: स्लग और रीराइट नियम में एक जैसे अक्षर हों
मूल समस्या यह थी कि «अनुमत अक्षर» की दो अलग परिभाषाएँ थीं: PHP फ़ंक्शन की अप्रत्यक्ष परिभाषा और .htaccess की स्पष्ट परिभाषा। नियम सरल है: स्लग जनरेटर की अनुमत-अक्षर सूची रीराइट नियम के कैरेक्टर वर्ग जैसी ही होनी चाहिए। यदि .htaccess [a-z0-9-] स्वीकार करता है, तो फ़ंक्शन का अंत बिना अपवाद preg_replace('/[^a-z0-9]+/', '-', …) से होना चाहिए।
एक सुझाव: कैरेक्टर वर्ग में हाइफ़न सबसे अंत में रखें, [a-zA-Z0-9_-]। बीच में लिखने पर इंजन की सहनशीलता से काम हो सकता है, लेकिन यह रेंज जैसा दिखता है और किसी को भ्रमित कर सकता है।
इलाज से बेहतर रोकथाम
- मुश्किल शीर्षकों से परीक्षण: «La Niña», «EL NIÑO», «Ça coûte 5 €», «Œuvre complète», «Straße» और जापानी शीर्षक। हर स्लग Apache नियम के पैटर्न से मेल खाना चाहिए।
- खाली स्लग का विकल्प: केवल प्रतीकों वाला शीर्षक खाली स्ट्रिंग देता है। तब लेख की ID इस्तेमाल करें।
- मौजूदा alias की जाँच:
SELECT alias FROM articles WHERE alias NOT REGEXP '^[a-z0-9-]+$'जैसी क्वेरी खराब पतों को तुरंत सामने लाती है। - प्रकाशन के बाद जाँच: सार्वजनिक URL पर साधारण HTTP अनुरोध, जिसका उत्तर 200 होना चाहिए, समस्या तुरंत बता देता।
SEO पर क्या असर है?
Google Unicode अक्षरों वाले URL इंडेक्स कर सकता है, इसलिए पते में ñ होना अपने आप में गलत नहीं है। लेकिन संदेश या सोशल मीडिया में कॉपी करने पर पता अक्सर एन्कोडेड रूप में दिखता है, जिसमें %C3%B1 जैसे कम पठनीय हिस्से होते हैं। छोटा और पढ़ने योग्य ASCII स्लग सबसे सुरक्षित विकल्प है, बशर्ते फ़ंक्शन अनचाहे अक्षर न छोड़े।
हमारे मामले में तीन सुधार काफ़ी थे: छोटे और बड़े अक्षरों का लिप्यंतरण पूरा करना, डेटाबेस में संबंधित लेख का alias सुधारना और .htaccess में अंतिम एंकर बनाए रखना, ताकि भविष्य की त्रुटि चुपचाप कटने के बजाय दिखाई दे।
French
German
English
Spanish
Hindi
Italian
Japanese
Korean
Norwegian
Chinese

