Lumaktaw sa nilalaman
Server at .htaccess

Mixed Content sa WordPress Matapos Lumipat sa https

Alisin ang mga babala sa mixed content sa WordPress matapos lumipat sa https: hanapin ang mga natitirang http:// URL sa database, palitan ang mga ito nang ligtas, at pilitin ang https sa htaccess.

Nailathala

Naka-install na ang iyong certificate, naglo-load na ang site sa https, at ayaw pa ring lumitaw ng padlock. Iyan ang mixed content: dumating ang mismong pahina sa https, pero may laman ito — isang image, isang stylesheet, isang script — na hinihingi pa rin sa plain http://. Dadaanan nito ang paghahanap sa mga URL na iyon, ang pagpapalit sa kanila nang hindi nasisira ang serialized data, ang pagtatama sa siteurl at home, at ang pagpilit ng https sa server para hindi na muling bumalik ang problema.

Ano ba talaga ang mixed content

Binabago ng pag-install ng certificate kung paano ini-encrypt ang koneksyon. Hindi nito binabago kung ano ang hinihingi ng iyong mga pahina. Ang bawat http://yoursite.com/wp-content/uploads/logo.png na naisulat sa isang post, isang widget, o isang setting ng theme bago ang paglipat ay nakaupo pa rin sa database, at masunuring hinihingi ito ng browser sa isang hindi ligtas na koneksyon.

Magkaiba ang trato ng mga browser sa dalawang kategorya, at mahalaga ang pagkakaibang ito kapag tinitimbang mo kung gaano ito kaapurahan:

  • Active mixed content — mga script, stylesheet, iframe, XHR. Tuluyang hinaharangan. Kaya nga maaaring magmukhang sira ang isang site matapos lumipat sa https nang walang kahit anong error message: tahimik na tinanggihan ang isang stylesheet.
  • Passive mixed content — mga image, audio, video. Karaniwang naglo-load pa rin, pero bumababa ang antas ng padlock at may ilang browser na nagpapakita ng indicator na “not secure”.

Parehong sulit ayusin. Ang una lang ang talagang nakakasira.

Hakbang 1: alamin kung ano ang hindi pa rin ligtas

Magsimula sa browser. Buksan ang pahina, buksan ang developer tools, at basahin ang console. Bawat hinarangan o ibinabang request ay pinapangalanan doon kasama ang buong URL nito. Sinasabi niyan sa iyo kung ano ang hindi ligtas; hindi nito sinasabi kung saan ito naka-imbak.

Para doon, tingnan mismo ang database. I-export ito at hanapin sa dump:

wp db export dump.sql
grep -o "http://example\.com[^\"']*" dump.sql | sort -u | head -50

Kung wala kang WP-CLI, ang mysqldump ay gumagawa ng parehong file:

mysqldump -u USER -p DBNAME > dump.sql

Basahin ang mga natatanging URL na bumalik. Ang mga uploads path ay tumutukoy sa nilalaman ng post at sa meta. Ang mga path ng asset ng theme ay karaniwang tumutukoy sa mga option. Ang anumang may domain na hindi mo kilala ay isang panlabas na embed, na kailangan ng ibang ayos (nasa ibaba).

Hakbang 2: ayusin muna ang siteurl at home

Ang siteurl at home ang dalawang option na ginagamit ng WordPress para buuin ang halos bawat panloob na URL na nililikha nito. Kung http:// pa rin ang laman ng alinman sa kanila, patuloy na maglalabas ang WordPress ng hindi ligtas na URL gaano man kalinis ang natitirang bahagi ng database.

Suriin ang mga ito:

wp option get siteurl
wp option get home

O sa sql:

SELECT option_name, option_value
FROM wp_options
WHERE option_name IN ('siteurl', 'home');

Ang dalawang ito ay payak na string, hindi serialized array, kaya tunay na ligtas dito ang diretsong update sa sql:

UPDATE wp_options
SET option_value = REPLACE(option_value, 'http://example.com', 'https://example.com')
WHERE option_name IN ('siteurl', 'home');

Isang bitag bago ka maglaan ng oras dito. Kung may itinatakdang WP_HOME o WP_SITEURL ang wp-config.php, tuluyang nadadaig ng mga constant na iyon ang database at magmumukhang walang epekto ang iyong update:

define( 'WP_HOME', 'https://example.com' );
define( 'WP_SITEURL', 'https://example.com' );

I-update ang mga iyon doon, o alisin ang mga ito at hayaang ang database ang magpatakbo. Suriin ito muna — marami itong naipapaliwanag na kaso ng “binago ko naman pero walang nangyari.”

Hakbang 3: kailangang serialization-safe ang replace

Ang lahat ng iba pa sa database ang kinaroroonan ng tunay na panganib, at hindi ito ang panganib na inaasahan ng karamihan. Ang panganib ay hindi na makakalampas ng URL ang replace. Ang panganib ay magtagumpay ito sa antas ng teksto at sirain ang datos sa paligid nito.

Iniimbak ng WordPress ang mga array at object bilang php-serialized na string, at itinatala ng format na iyon ang haba sa byte ng bawat string na nakapaloob dito:

a:1:{s:4:"logo";s:38:"http://example.com/img/logo.png";}

Palitan ang http:// ng https:// gamit ang hilaw na REPLACE sa sql at ang teksto ay humahaba nang isang byte samantalang ang idineklarang haba ay nananatili sa lumang halaga. Binabasa ng php ang haba, lumalakad ito nang ganoong dami ng byte pasulong, hindi nito natatagpuan ang terminator kung saan ito inaasahan, at tumatanggi itong i-unserialize ang buong array. Iaabot naman ng WordPress sa theme o plugin ang isang value na kumikilos na parang hindi kailanman na-save ang setting.

Ang sintomas ay hindi isang error. Ito ay mga widget na naglalaho, mga setting sa customizer na nagre-reset, at mga layout ng page builder na blangkong nagre-render — nang walang anuman sa admin na nagsasabing may namali. Tunay na masakit ang pagbawi mula riyan kung walang backup, kaya kumuha ka nga:

wp db export backup-before-https-replace.sql

Pagkatapos ay gumamit ng kasangkapang nag-a-unserialize, nagpapalit sa loob ng na-decode na estruktura, at nagse-serialize muli nang may naitamang haba. Mag-dry run muna:

wp search-replace 'http://example.com' 'https://example.com' --dry-run --all-tables --skip-columns=guid

Basahin ang ulat. Kung may table na hindi mo kilala na nagpapakita ng libu-libong tama, huminto at tingnan bago ka magpatuloy. Saka mo ito patakbuhin nang totohanan:

wp search-replace 'http://example.com' 'https://example.com' --all-tables --skip-columns=guid --report-changed-only

Mahalaga ang --skip-columns=guid: ang guid ay isang permanenteng identifier para sa mga feed reader, hindi isang buhay na URL, at ang muling pagsulat dito ay maaaring magdulot na makita ng mga subscriber ang buo mong archive bilang bagong mga post. Kung kailangan mong makita kung aling mga column ang ligtas para sa payak na sql at alin ang nangangailangan ng serialization-aware na paghawak bago ka magpatakbo ng kahit ano, buuin ang mga statement gamit ang WordPress search and replace sql tool.

Kung hindi mo magamit ang command line, ang isang migration plugin na tahasang nagsasabing hinahawakan nito ang serialized data ay gumagawa ng parehong pag-decode sa pamamagitan ng admin screen. Kung hindi ito sinasabi ng isang kasangkapan, ipagpalagay mong hindi nito ginagawa.

Hakbang 4: pilitin ang https sa server

Ang paglilinis ng database ay pumipigil sa iyong mga pahina na humingi ng hindi ligtas na resource. Hindi nito napipigilan ang isang bisita na dumating sa http:// sa simula pa lang. Trabaho iyon ng redirect sa antas ng server, at sa Apache ay nabibilang iyon sa .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

Dalawang bagay tungkol sa paglalagay. Ilagay ito sa itaas ng mga marker na # BEGIN WordPress — itinatapon ang anumang nasa loob nila tuwing may nagse-save ng permalinks. At gumagana lang ito kung naka-enable ang mod_rewrite at pinapayagan ng virtual host ang mga override (AllowOverride All, o hindi bababa sa FileInfo). Sa nginx, ganap na binabalewala ang .htaccess at kailangan itong ilagay sa isang server block sa halip.

Hindi kayang gawin ng RedirectMatch ang trabahong ito. Tumutugma lamang ito sa request path at wala itong paraan para subukin kung ligtas na ang kasalukuyang request, kaya ire-redirect nito ang mga https request pabalik sa kanilang sarili. Ang kondisyonal na RewriteRule sa itaas ang tamang kasangkapan.

Ang redirect loop, at kung bakit ito nangyayari

Kung magsisimulang mag-redirect nang walang katapusan ang site sa sandaling idagdag mo ang rule na iyon, nagtatapos ang iyong TLS sa isang lugar na nasa itaas — isang load balancer, isang reverse proxy, o isang CDN — na saka nagpapasa ng plain http sa Apache. Nakikita ng Apache ang isang hindi ligtas na request, nagre-redirect ito sa https, nagpapasa muli ng http ang proxy, at paikot-ikot ito.

Subukin ang forwarded header sa halip:

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

May kaparehong bulag na sulok ang WordPress mismo sa ganoong setup — binabasa ng is_ssl() ang $_SERVER['HTTPS'], na hindi kailanman itinatakda ng proxy, kaya lumalabas na http:// ang mga admin URL. Idagdag ito sa wp-config.php, sa itaas ng linyang “stop editing”:

if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
	$_SERVER['HTTPS'] = 'on';
}

Sulit na maging tapat tungkol sa panganib: ang X-Forwarded-Proto ay isang header na galing sa client. Tama lang itong pagkatiwalaan kapag laging ino-overwrite ito ng isang proxy na kontrolado mo. Sa isang server na direktang naaabot mula sa internet, maaari itong pekein.

Ano ang hindi maaayos ng isang database replace

  • Mga hardcoded na URL sa mga file. Ang anumang naisulat sa functions.php, sa isang child theme template, o sa isang inline na asset path ay wala sa database. I-grep nang hiwalay ang theme directory.
  • Escaped JSON. May ilang builder na nag-iimbak ng mga URL bilang https:\/\/example.com. Napapalampas sila nang tuluyan ng paghahanap sa normal na anyo, kaya maaaring kailanganin ng ikalawang pasada sa escaped na string.
  • Mga panlabas na resource. Ang isang third-party na script o embed na makukuha lang sa http ay hindi maaayos mula sa iyong panig. Humanap ng bersyon sa https o alisin ito.
  • Mga cache. Ang page cache, object cache, at mga kopya sa CDN ay patuloy na naghahatid ng lumang markup kahit tama na ang naging replace. I-flush ang tatlo bago mo ipagpalagay na nabigo ang replace.

Isang bagay na sulit laktawan: ang upgrade-insecure-requests na Content Security Policy header ay patatahimikin ang mga babala sa pamamagitan ng muling pagsulat ng mga hindi ligtas na request sa loob ng browser. Ginagamot nito ang sintomas at iniiwan ang maling mga URL sa iyong database, kung saan dadalhin sila pasulong ng susunod na export, migration, o feed. Ayusin ang datos, saka mo ito gamitin bilang safety net kung gusto mo.

Naiipit pa rin?

Suriin muli ang siteurl at home matapos ang bawat hakbang — kung minsan ay muling isinusulat ang mga ito ng mga plugin at migration tool nang hindi mo alam. Kung pinapangalanan pa rin ng console ang isang hindi ligtas na URL na hindi mo mahanap sa dump, tingnan ang source ng pahina at hanapin ito roon: kung lumilitaw ito sa nabuong markup pero wala sa database, binubuo ito sa php, at ang theme o plugin na gumagawa nito ang dapat ayusin.

FAQ

Mga Tanong

Bakit nagpapakita pa rin ng mga babala sa mixed content ang aking WordPress site matapos mag-install ng SSL certificate?

Binabago lamang ng certificate kung paano ini-encrypt ang koneksyon, hindi kung ano ang hinihingi ng iyong mga pahina. Nananatiling nakaimbak sa database ang mga lumang http:// URL, sa loob ng nilalaman ng post, sa mga opsyon ng widget at sa mga setting ng theme. Nilo-load ng browser ang pahina sa https, nakikita nitong may image o script na hinihingi sa plain http, at iniuulat nito ang mixed content sa isang pahinang ligtas naman sana.

Paano ko mahahanap ang mga natitirang http:// URL sa aking WordPress database?

Buksan ang isang pahina sa browser at basahin ang developer console, na nagpapangalan sa bawat hindi ligtas na request ayon sa URL. Pagkatapos ay i-export ang database at hanapin sa dump ang iyong domain na may http prefix. Ang pagbilang ng mga tama kada table ang magsasabi sa iyo kung nasa nilalaman ba ng post ang problema, nasa wp_options, o nasa mga plugin table na hindi mo inaasahan.

Maaayos ko ba ang mixed content gamit ang payak na search and replace sa sql?

Para lamang sa mga scalar value gaya ng siteurl at home. Ang anumang iniimbak ng WordPress bilang serialized array, na sumasaklaw sa halos lahat ng option at meta value, ay nagtatala ng haba sa byte ng bawat string sa loob nito. Binabago ng hilaw na replace ang teksto pero naiiwan ang lumang haba, kaya nabibigong ma-unserialize ang value, at tahimik na bumabalik sa default ang setting.

Anong rule sa htaccess ang pumipilit ng https sa WordPress?

Isang RewriteRule na binabantayan ng kondisyon sa variable na HTTPS, nakalagay sa itaas ng mga marker na BEGIN WordPress para hindi ito mabura kapag nag-save ng permalinks. Sa likod ng proxy o CDN, nagbabasa ng off ang variable na HTTPS kahit sa mga ligtas na request, kaya subukin ang X-Forwarded-Proto header sa halip o magre-redirect ang rule nang walang katapusan.

Bakit napunta sa redirect loop ang site ko matapos kong pilitin ang https?

Halos tiyak na nagtatapos ang iyong TLS sa isang proxy o CDN na saka nagpapasa ng plain http sa Apache. Nakikita ng rewrite ang isang hindi ligtas na request, nagre-redirect ito sa https, nagpapasa muli ng http ang proxy, at umuulit ang siklo. Palitan ang kondisyon papunta sa X-Forwarded-Proto header at itakda ang HTTPS server variable sa wp-config.php.

Sinisira ba ng mixed content ang buong pahina o ang padlock lamang?

Nakadepende ito sa resource. Tuluyang hinaharangan ng mga browser ang active mixed content gaya ng mga script, stylesheet at iframe, na maaaring sumira sa layout o sa paggana nang walang nakikitang paliwanag. Ang passive mixed content gaya ng mga image at video ay karaniwang naglo-load pa rin, pero bumababa ang antas ng padlock at maaaring makakita ang mga bisita ng indicator na hindi ligtas ang site. Parehong sulit ayusin.