https पर जाने के बाद WordPress मिक्स्ड कंटेंट
https पर जाने के बाद WordPress की मिक्स्ड कंटेंट चेतावनियाँ हटाइए: डेटाबेस में बचे http:// URL ढूँढ़िए, उन्हें सुरक्षित तरीके से बदलिए, और .htaccess में https लागू कीजिए।
प्रकाशित
आपका सर्टिफ़िकेट लग चुका है, साइट https पर लोड होती है, और ताला फिर भी दिखने से इनकार कर रहा है। यही मिक्स्ड कंटेंट है: पेज खुद तो https पर आया, पर उसके भीतर कुछ — कोई इमेज, कोई स्टाइलशीट, कोई स्क्रिप्ट — अब भी सादे http:// पर माँगा जा रहा है। यह लेख उन URL को ढूँढ़ने, serialized डेटा बिगाड़े बिना उन्हें बदलने, siteurl और home सुधारने, और सर्वर पर https लागू करने तक ले जाता है ताकि समस्या दोबारा लौट ही न सके।
मिक्स्ड कंटेंट असल में है क्या
सर्टिफ़िकेट लगाने से यह बदलता है कि कनेक्शन कैसे एन्क्रिप्ट होता है। इससे यह नहीं बदलता कि आपके पेज क्या माँगते हैं। हर वह http://yoursite.com/wp-content/uploads/logo.png जो माइग्रेशन से पहले किसी पोस्ट, विजेट या थीम सेटिंग में लिखा गया था, अब भी डेटाबेस में बैठा है, और ब्राउज़र उसे आज्ञाकारी ढंग से असुरक्षित कनेक्शन पर माँगता है।
ब्राउज़र दो श्रेणियों के साथ अलग-अलग व्यवहार करते हैं, और यह भेद तब मायने रखता है जब आप तय कर रहे हों कि मामला कितना ज़रूरी है:
- सक्रिय मिक्स्ड कंटेंट — स्क्रिप्ट, स्टाइलशीट, iframe, XHR। सीधे रोक दिए जाते हैं। इसीलिए https पर जाने के बाद कोई साइट कहीं कोई एरर दिखाए बिना टूटी हुई लग सकती है: कोई स्टाइलशीट चुपचाप ठुकरा दी गई थी।
- निष्क्रिय मिक्स्ड कंटेंट — इमेज, ऑडियो, वीडियो। आम तौर पर लोड तो हो जाता है, पर ताला घट जाता है और कुछ ब्राउज़र असुरक्षित होने का संकेत दिखाते हैं।
दोनों ठीक करने लायक हैं। तोड़ता सिर्फ़ पहला है।
चरण 1: पता कीजिए कि अब भी असुरक्षित क्या है
शुरुआत ब्राउज़र से कीजिए। पेज खोलिए, डेवलपर टूल खोलिए, और कंसोल पढ़िए। हर रोका गया या घटाया गया अनुरोध वहाँ अपने पूरे URL के साथ दर्ज होता है। इससे पता चलता है कि क्या असुरक्षित है; यह नहीं बताता कि वह कहाँ संग्रहीत है।
उसके लिए सीधे डेटाबेस देखिए। उसे एक्सपोर्ट कीजिए और डंप में खोजिए:
wp db export dump.sql
grep -o "http://example\.com[^\"']*" dump.sql | sort -u | head -50
अगर WP-CLI उपलब्ध नहीं है, तो mysqldump वही फ़ाइल बनाता है:
mysqldump -u USER -p DBNAME > dump.sql
जो अनोखे URL लौटें उन्हें पढ़िए। uploads वाले पाथ पोस्ट कंटेंट और meta की ओर इशारा करते हैं। थीम एसेट वाले पाथ आम तौर पर options की ओर। जिस डोमेन को आप पहचानते ही नहीं, वह कोई बाहरी embed है, जिसके लिए अलग इलाज चाहिए (नीचे बताया गया है)।
चरण 2: पहले siteurl और home ठीक कीजिए
siteurl और home वे दो विकल्प हैं जिनसे WordPress अपने बनाए लगभग हर आंतरिक URL को गढ़ता है। अगर इनमें से कोई भी अब भी http:// कहता है, तो बाकी डेटाबेस चाहे कितना ही साफ़ हो, WordPress असुरक्षित URL निकालता रहेगा।
इन्हें जाँचिए:
wp option get siteurl
wp option get home
या sql में:
SELECT option_name, option_value
FROM wp_options
WHERE option_name IN ('siteurl', 'home');
ये दोनों सादी स्ट्रिंग हैं, serialized array नहीं, इसलिए यहाँ सीधा sql अपडेट सचमुच सुरक्षित है:
UPDATE wp_options
SET option_value = REPLACE(option_value, 'http://example.com', 'https://example.com')
WHERE option_name IN ('siteurl', 'home');
इस पर समय लगाने से पहले एक जाल जान लीजिए। अगर wp-config.php में WP_HOME या WP_SITEURL परिभाषित हैं, तो वे स्थिरांक डेटाबेस को पूरी तरह ओवरराइड करते हैं और आपका अपडेट कुछ करता हुआ नहीं दिखेगा:
define( 'WP_HOME', 'https://example.com' );
define( 'WP_SITEURL', 'https://example.com' );
उन्हें वहीं अपडेट कीजिए, या हटाकर डेटाबेस को कमान दे दीजिए। यह सबसे पहले जाँचिए — “मैंने बदला और कुछ नहीं हुआ” वाले बहुत से मामले इसी से समझ आते हैं।
चरण 3: replace का serialization-safe होना ज़रूरी है
डेटाबेस की बाकी हर चीज़ में असली जोखिम बैठा है, और वह वैसा जोखिम नहीं है जैसी ज़्यादातर लोग उम्मीद करते हैं। खतरा यह नहीं है कि replace कुछ URL छोड़ देगा। खतरा यह है कि वह टेक्स्ट के स्तर पर सफल हो जाएगा और आसपास का डेटा तबाह कर देगा।
WordPress arrays और objects को php-serialized स्ट्रिंग के रूप में रखता है, और वह फ़ॉर्मैट अपने भीतर की हर स्ट्रिंग की byte लंबाई दर्ज करता है:
a:1:{s:4:"logo";s:38:"http://example.com/img/logo.png";}
कच्चे sql REPLACE से http:// को https:// कीजिए और टेक्स्ट एक byte लंबा हो जाता है जबकि घोषित लंबाई पुरानी ही रह जाती है। PHP लंबाई पढ़ता है, उतने byte आगे चलता है, अपेक्षित जगह पर terminator नहीं पाता, और पूरे array को unserialize करने से इनकार कर देता है। WordPress फिर थीम या प्लगइन को ऐसा मान थमा देता है जो ऐसे बर्ताव करता है मानो वह सेटिंग कभी सेव ही न हुई हो।
लक्षण कोई एरर नहीं होता। लक्षण होता है विजेट का गायब होना, customizer सेटिंग्स का रीसेट होना, और page builder लेआउट का खाली रेंडर होना — और एडमिन में कहीं कोई संकेत नहीं कि कुछ गड़बड़ हुआ। बिना बैकअप के इससे उबरना सचमुच तकलीफ़देह है, इसलिए एक बैकअप ले लीजिए:
wp db export backup-before-https-replace.sql
फिर ऐसा औज़ार इस्तेमाल कीजिए जो unserialize करे, decode की गई संरचना के भीतर बदलाव करे, और सही लंबाइयों के साथ दोबारा serialize करे। पहले dry run:
wp search-replace 'http://example.com' 'https://example.com' --dry-run --all-tables --skip-columns=guid
रिपोर्ट पढ़िए। अगर कोई ऐसी टेबल जिसे आप पहचानते नहीं, हज़ारों मिलान दिखाए, तो रुककर देखिए और तभी आगे बढ़िए। फिर असल में चलाइए:
wp search-replace 'http://example.com' 'https://example.com' --all-tables --skip-columns=guid --report-changed-only
--skip-columns=guid मायने रखता है: guid फ़ीड रीडर के लिए एक स्थायी पहचानकर्ता है, कोई जीवित URL नहीं, और उसे दोबारा लिखने से सब्सक्राइबर को आपका पूरा आर्काइव नई पोस्ट की तरह दिख सकता है। कुछ भी चलाने से पहले अगर आपको यह देखना है कि कौन से कॉलम सादे sql के लिए सुरक्षित हैं और किन्हें serialization का ध्यान रखने वाला इलाज चाहिए, तो statements WordPress search and replace sql टूल से बनाइए।
अगर आप कमांड लाइन इस्तेमाल नहीं कर सकते, तो ऐसा माइग्रेशन प्लगइन जो साफ़ कहता हो कि वह serialized डेटा संभालता है, एडमिन स्क्रीन से वही decoding काम कर देता है। अगर कोई औज़ार यह नहीं कहता, तो मान लीजिए कि वह करता नहीं।
चरण 4: सर्वर पर https लागू कीजिए
डेटाबेस साफ़ करने से आपके पेज असुरक्षित संसाधन माँगना बंद कर देते हैं। इससे यह नहीं रुकता कि कोई विज़िटर पहले ही http:// पर आ पहुँचे। वह सर्वर स्तर का रीडायरेक्ट है, और Apache पर उसकी जगह .htaccess है:
# BEGIN Force HTTPS
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
# END Force HTTPS
जगह के बारे में दो बातें। इसे # BEGIN WordPress मार्करों के ऊपर रखिए — उनके भीतर की हर चीज़ हर बार permalinks सेव होने पर फेंक दी जाती है। और यह तभी काम करता है जब mod_rewrite चालू हो और virtual host ओवरराइड की इजाज़त देता हो (AllowOverride All, या कम से कम FileInfo)। nginx पर .htaccess पूरी तरह अनदेखी की जाती है और इसे server block में डालना पड़ता है।
RedirectMatch यह काम नहीं करेगा। वह सिर्फ़ अनुरोध के पाथ से मेल खाता है और उसके पास यह जाँचने का कोई तरीका नहीं कि मौजूदा अनुरोध पहले से सुरक्षित है या नहीं, इसलिए वह https अनुरोधों को उन्हीं पर वापस भेजता रहेगा। ऊपर वाला शर्त-आधारित RewriteRule ही सही औज़ार है।
रीडायरेक्ट लूप, और वह होता क्यों है
अगर वह नियम जोड़ते ही साइट हमेशा के लिए रीडायरेक्ट करने लगे, तो आपका TLS कहीं ऊपर की तरफ़ खत्म हो रहा है — किसी लोड बैलेंसर, रिवर्स प्रॉक्सी या CDN पर — जो फिर Apache को सादा http आगे भेजता है। Apache को असुरक्षित अनुरोध दिखता है, वह https पर भेजता है, प्रॉक्सी फिर http आगे बढ़ा देता है, और चक्र चलता रहता है।
उसकी जगह forwarded हेडर जाँचिए:
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
उस सेटअप में खुद WordPress की भी यही अंधी जगह है — is_ssl() $_SERVER['HTTPS'] पढ़ता है, जिसे प्रॉक्सी कभी सेट ही नहीं करता, इसलिए एडमिन URL http:// के रूप में निकलते हैं। इसे wp-config.php में, संपादन रोकने की सूचना देने वाली लाइन के ऊपर जोड़िए:
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
$_SERVER['HTTPS'] = 'on';
}
जोखिम पर सीधी बात: X-Forwarded-Proto एक क्लाइंट का भेजा हेडर है। उस पर भरोसा तभी सही है जब आपके नियंत्रण वाला कोई प्रॉक्सी उसे हमेशा दोबारा लिखता हो। इंटरनेट से सीधे पहुँचे जा सकने वाले सर्वर पर उसे नकली बनाया जा सकता है।
डेटाबेस replace क्या नहीं सुधारेगा
- फ़ाइलों में hardcoded URL।
functions.php, किसी child theme टेम्पलेट, या किसी inline एसेट पाथ में लिखी कोई भी चीज़ डेटाबेस में नहीं है। थीम डायरेक्टरी को अलग से grep कीजिए। - Escaped JSON। कुछ बिल्डर URL को
https:\/\/example.comके रूप में रखते हैं। सामान्य रूप की खोज इन्हें पूरी तरह छोड़ देती है, इसलिए escaped स्ट्रिंग पर एक दूसरा दौर लग सकता है। - बाहरी संसाधन। कोई तीसरे पक्ष की स्क्रिप्ट या embed जो सिर्फ़ http पर उपलब्ध हो, आपकी तरफ़ से ठीक नहीं हो सकती। उसका https वाला रूप ढूँढ़िए या उसे हटा दीजिए।
- Cache। पेज cache, object cache और CDN की कॉपियाँ सही replace के बाद भी पुराना markup परोसती रहती हैं। यह नतीजा निकालने से पहले कि replace फ़ेल हुआ, तीनों को flush कीजिए।
एक चीज़ छोड़ देने लायक है: upgrade-insecure-requests वाला Content Security Policy हेडर ब्राउज़र में असुरक्षित अनुरोधों को दोबारा लिखकर चेतावनियाँ चुप करा देगा। वह लक्षण का इलाज करता है और गलत URL आपके डेटाबेस में ही छोड़ देता है, जहाँ से अगला एक्सपोर्ट, माइग्रेशन या फ़ीड उन्हें आगे ढो ले जाएगा। पहले डेटा ठीक कीजिए, फिर चाहें तो उसे सुरक्षा जाल की तरह इस्तेमाल कीजिए।
अब भी अटके हैं?
हर कदम के बाद siteurl और home दोबारा जाँचिए — प्लगइन और माइग्रेशन टूल कभी-कभी आपकी पीठ पीछे उन्हें बदल देते हैं। अगर कंसोल अब भी कोई ऐसा असुरक्षित URL बता रहा है जो आपको डंप में नहीं मिल रहा, तो पेज का सोर्स देखिए और उसे वहाँ खोजिए: अगर वह जनरेट हुए markup में दिखे पर डेटाबेस में नहीं, तो वह php में बनाया जा रहा है, और जो थीम या प्लगइन उसे बना रहा है, ठीक उसी को सुधारना है।
FAQ
सवाल
SSL सर्टिफ़िकेट लगाने के बाद भी मेरी WordPress साइट मिक्स्ड कंटेंट चेतावनी क्यों दिखाती है?
सर्टिफ़िकेट सिर्फ़ यह बदलता है कि कनेक्शन कैसे एन्क्रिप्ट होता है, यह नहीं कि आपके पेज क्या माँगते हैं। पुराने http:// URL डेटाबेस में ही पड़े रहते हैं — पोस्ट कंटेंट, विजेट विकल्पों और थीम सेटिंग्स के भीतर। ब्राउज़र पेज को https पर लोड करता है, फिर देखता है कि कोई इमेज या स्क्रिप्ट सादे http पर माँगी जा रही है, और एक वरना सुरक्षित पेज पर मिक्स्ड कंटेंट की रिपोर्ट कर देता है।
अपने WordPress डेटाबेस में बचे http:// URL कैसे ढूँढ़ूँ?
किसी पेज को ब्राउज़र में खोलिए और डेवलपर कंसोल पढ़िए, जो हर असुरक्षित अनुरोध का URL सहित नाम बताता है। फिर डेटाबेस एक्सपोर्ट कीजिए और डंप में अपने डोमेन को http उपसर्ग के साथ खोजिए। प्रति टेबल मिलान गिनने से पता चल जाता है कि समस्या पोस्ट कंटेंट में है, wp_options में है, या ऐसी प्लगइन टेबल में जिसकी आपने उम्मीद ही नहीं की थी।
क्या मैं सादे sql search and replace से मिक्स्ड कंटेंट ठीक कर सकता हूँ?
सिर्फ़ siteurl और home जैसे साधारण मानों के लिए। WordPress जो कुछ भी serialized array के रूप में रखता है — और इसमें ज़्यादातर option तथा meta मान आते हैं — वह अपने भीतर की हर स्ट्रिंग की byte लंबाई भी दर्ज करता है। कच्चा replace टेक्स्ट तो बदल देता है पर पुरानी लंबाई छोड़ देता है, फिर मान unserialize होने में फ़ेल होता है, और सेटिंग चुपचाप अपने डिफ़ॉल्ट पर लौट जाती है।
WordPress में https लागू करने वाला .htaccess नियम कौन सा है?
https वेरिएबल पर शर्त लगाकर बनाया गया एक RewriteRule, जिसे BEGIN WordPress मार्करों के ऊपर रखा जाए ताकि permalinks सेव करने पर वह मिट न जाए। प्रॉक्सी या CDN के पीछे https वेरिएबल सुरक्षित अनुरोधों पर भी off पढ़ा जाता है, इसलिए उसकी जगह X-Forwarded-Proto हेडर जाँचिए वरना नियम हमेशा के लिए रीडायरेक्ट करता रहेगा।
https लागू करने के बाद मेरी साइट रीडायरेक्ट लूप में क्यों चली गई?
लगभग तय है कि आपका TLS किसी प्रॉक्सी या CDN पर खत्म हो रहा है जो फिर Apache को सादा http आगे भेजता है। rewrite को असुरक्षित अनुरोध दिखता है, वह https पर भेजता है, प्रॉक्सी फिर http आगे बढ़ा देता है, और लूप दोहराता रहता है। शर्त को X-Forwarded-Proto हेडर पर ले जाइए और wp-config.php में https सर्वर वेरिएबल सेट कीजिए।
क्या मिक्स्ड कंटेंट पूरा पेज तोड़ता है या सिर्फ़ ताला हटाता है?
यह संसाधन पर निर्भर है। ब्राउज़र सक्रिय मिक्स्ड कंटेंट — जैसे स्क्रिप्ट, स्टाइलशीट और iframe — को सीधे रोक देते हैं, जिससे बिना किसी दिखने वाली वजह के लेआउट या कार्यक्षमता टूट सकती है। निष्क्रिय मिक्स्ड कंटेंट, जैसे इमेज और वीडियो, आम तौर पर लोड हो ही जाता है, पर ताला घट जाता है और विज़िटर को असुरक्षित का संकेत दिख सकता है। दोनों ठीक करने लायक हैं।