Sari la conținut
Server și .htaccess

Conținut mixt în WordPress după trecerea la https

Elimină avertismentele de conținut mixt în WordPress după trecerea la https: găsește URL-urile http:// rămase în baza de date, înlocuiește-le în siguranță și forțează https din .htaccess.

Publicat

Certificatul este instalat, site-ul se încarcă prin https, iar lacătul tot refuză să apară. Acesta este conținutul mixt: pagina în sine a venit prin https, dar ceva din interiorul ei — o imagine, o foaie de stil, un script — este cerut în continuare prin http:// simplu. Articolul de față îți arată cum găsești acele URL-uri, cum le înlocuiești fără să corupi datele serializate, cum corectezi siteurl și home și cum forțezi https la nivel de server, ca problema să nu se mai poată întoarce.

Ce este de fapt conținutul mixt

Instalarea unui certificat schimbă modul în care este criptată conexiunea. Nu schimbă ce anume cer paginile tale. Fiecare http://yoursite.com/wp-content/uploads/logo.png scris într-un articol, într-un widget sau într-o setare a temei înainte de mutare stă în continuare în baza de date, iar browserul îl cere ascultător printr-o conexiune nesecurizată.

Browserele tratează diferit două categorii, iar distincția contează atunci când decizi cât de urgent este:

  • Conținut mixt activ — scripturi, foi de stil, cadre iframe, XHR. Blocat complet. De aceea un site poate arăta stricat după trecerea la https fără niciun mesaj de eroare nicăieri: o foaie de stil a fost refuzată în tăcere.
  • Conținut mixt pasiv — imagini, audio, video. De obicei încă se încarcă, dar lacătul este retrogradat, iar unele browsere afișează un indicator „nesecurizat”.

Merită rezolvate amândouă. Doar primul strică efectiv lucruri.

Pasul 1: află ce mai este nesecurizat

Începe în browser. Deschide pagina, deschide uneltele pentru dezvoltatori și citește consola. Fiecare cerere blocată sau retrogradată este numită acolo cu URL-ul complet. Asta îți spune ce este nesecurizat; nu îți spune unde este stocat.

Pentru asta te uiți direct în baza de date. Exportă-o și caută în fișierul rezultat:

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

Dacă WP-CLI nu este disponibil, mysqldump produce același fișier:

mysqldump -u USER -p DBNAME > dump.sql

Citește URL-urile unice care apar. Căile din uploads trimit spre conținutul articolelor și metadate. Căile către resursele temei trimit de obicei spre opțiuni. Orice domeniu pe care nu îl recunoști este o încorporare externă, iar aceea cere o altă soluție (tratată mai jos).

Pasul 2: corectează întâi siteurl și home

siteurl și home sunt cele două opțiuni din care WordPress construiește aproape fiecare URL intern pe care îl generează. Dacă vreuna dintre ele încă spune http://, WordPress va continua să emită URL-uri nesecurizate, oricât de curat ar fi restul bazei de date.

Verifică-le:

wp option get siteurl
wp option get home

Sau în sql:

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

Acestea două sunt șiruri simple, nu tablouri serializate, așa că o actualizare sql directă este cu adevărat sigură aici:

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

O capcană înainte să pierzi timp cu asta. Dacă wp-config.php definește WP_HOME sau WP_SITEURL, acele constante suprascriu complet baza de date, iar actualizarea ta va părea că nu face nimic:

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

Actualizează-le acolo sau elimină-le și lasă baza de date să decidă. Verifică asta prima dată — explică multe dintre cazurile de „am schimbat și nu s-a întâmplat nimic”.

Pasul 3: înlocuirea trebuie să respecte serializarea

Tot restul bazei de date este locul unde stă riscul real, și nu este riscul la care se așteaptă majoritatea. Pericolul nu este că înlocuirea ratează URL-uri. Este că reușește la nivel de text și distruge datele din jur.

WordPress stochează tablouri și obiecte ca șiruri serializate de php, iar acel format înregistrează lungimea în octeți a fiecărui șir pe care îl conține:

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

Schimbă http:// în https:// cu un REPLACE brut în sql și textul devine cu un octet mai lung, în timp ce lungimea declarată rămâne la valoarea veche. php citește lungimea, avansează atâția octeți, nu găsește acolo terminatorul așteptat și refuză să deserializeze tot tabloul. WordPress predă apoi temei sau pluginului o valoare care se comportă ca și cum setarea nu ar fi fost salvată niciodată.

Simptomul nu este o eroare. Sunt widgeturi care dispar, setări din personalizator care revin la zero și machete de la constructoarele de pagini care se randează goale — fără nimic în administrare care să indice că ceva a mers prost. Recuperarea fără o copie de siguranță este cu adevărat dureroasă, deci fă una:

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

Apoi folosește o unealtă care deserializează, înlocuiește în interiorul structurii decodate și serializează din nou cu lungimile corectate. Întâi o rulare de probă:

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

Citește raportul. Dacă un tabel pe care nu îl recunoști arată mii de potriviri, oprește-te și uită-te înainte să confirmi. Apoi rulează pe bune:

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

--skip-columns=guid contează: guid este un identificator permanent pentru cititoarele de fluxuri, nu un URL viu, iar rescrierea lui îi poate face pe abonați să vadă întreaga ta arhivă ca articole noi. Dacă vrei să vezi ce coloane sunt sigure pentru sql simplu și care au nevoie de tratare conștientă de serializare înainte să rulezi ceva, construiește instrucțiunile cu unealta de căutare și înlocuire sql pentru WordPress.

Dacă nu poți folosi linia de comandă, un plugin de migrare care afirmă explicit că tratează datele serializate face aceeași muncă de decodare din panoul de administrare. Dacă o unealtă nu spune asta, presupune că nu o face.

Pasul 4: forțează https la nivel de server

Curățarea bazei de date oprește paginile tale din a cere resurse nesecurizate. Nu oprește un vizitator să ajungă pe http:// de la bun început. Pentru asta este nevoie de o redirecționare la nivel de server, iar pe Apache locul ei este în .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

Două lucruri despre poziție. Pune asta deasupra marcajelor # BEGIN WordPress — orice se află între ele este aruncat de fiecare dată când cineva salvează legăturile permanente. Și funcționează doar dacă mod_rewrite este activ și gazda virtuală permite suprascrieri (AllowOverride All sau măcar FileInfo). Pe nginx, .htaccess este ignorat complet, iar asta trebuie pusă într-un bloc de server.

Un RedirectMatch nu poate face treaba asta. El se potrivește doar pe calea cererii și nu are cum să testeze dacă cererea curentă este deja securizată, deci redirecționează cererile https către ele însele. RewriteRule condiționată de mai sus este unealta potrivită.

Bucla de redirecționare și de ce apare

Dacă site-ul începe să redirecționeze la nesfârșit în clipa în care adaugi acea regulă, TLS-ul tău se termină undeva mai sus — un echilibrator de trafic, un proxy invers sau un CDN — care apoi trimite http simplu către Apache. Apache vede o cerere nesecurizată, redirecționează spre https, proxy-ul trimite iar http și tot așa la infinit.

Testează în schimb antetul transmis mai departe:

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

WordPress însuși are același punct orb în configurația asta — is_ssl() citește $_SERVER['HTTPS'], pe care proxy-ul nu îl setează niciodată, deci URL-urile din administrare ies ca http://. Adaugă asta în wp-config.php, deasupra liniei „stop editing”:

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

Merită spus cinstit despre risc: X-Forwarded-Proto este un antet furnizat de client. Să te încrezi în el este corect doar când un proxy pe care îl controlezi îl suprascrie mereu. Pe un server accesibil direct din internet, poate fi falsificat.

Ce nu rezolvă o înlocuire în baza de date

  • URL-uri scrise fix în fișiere. Tot ce a fost scris în functions.php, într-un șablon de temă copil sau într-o cale de resursă din cod nu se află în baza de date. Caută separat în directorul temei.
  • JSON cu caractere de evadare. Unele constructoare stochează URL-uri ca https:\/\/example.com. O căutare după forma normală le ratează complet, deci poate fi nevoie de o a doua trecere pe varianta cu caractere de evadare.
  • Resurse externe. Un script sau o încorporare de la terți, disponibilă doar prin http, nu poate fi reparată din partea ta. Găsește o variantă pe https sau renunță la ea.
  • Memoriile cache. Cache-ul de pagini, cache-ul de obiecte și copiile din CDN continuă să servească vechiul cod chiar și după o înlocuire corectă. Golește-le pe toate trei înainte să concluzionezi că înlocuirea a eșuat.

Un lucru pe care merită să îl eviți: antetul Content Security Policy upgrade-insecure-requests va reduce la tăcere avertismentele rescriind cererile nesecurizate în browser. Tratează simptomul și lasă URL-urile greșite în baza ta de date, de unde următorul export, următoarea migrare sau următorul flux le va duce mai departe. Repară întâi datele, apoi folosește-l ca plasă de siguranță, dacă vrei.

Tot blocat?

Reverifică siteurl și home după fiecare pas — pluginurile și uneltele de migrare le rescriu uneori pe la spatele tău. Dacă în consolă apare în continuare un URL nesecurizat pe care nu îl găsești în export, deschide sursa paginii și caută-l acolo: dacă apare în codul generat, dar nu în baza de date, este construit în php, iar tema sau pluginul care îl produce este lucrul de reparat.

FAQ

Întrebări

De ce afișează site-ul meu WordPress în continuare avertismente de conținut mixt după instalarea unui certificat SSL?

Certificatul schimbă doar modul în care este criptată conexiunea, nu și ce anume cer paginile tale. URL-urile vechi cu http:// rămân stocate în baza de date, în conținutul articolelor, în opțiunile widgeturilor și în setările temei. Browserul încarcă pagina prin https, vede o imagine sau un script cerut prin http simplu și raportează conținut mixt pe o pagină altfel securizată.

Cum găsesc URL-urile http:// rămase în baza de date WordPress?

Deschide o pagină în browser și citește consola pentru dezvoltatori, care numește fiecare cerere nesecurizată cu URL cu tot. Apoi exportă baza de date și caută în export domeniul tău cu prefixul http. Numărul de rezultate pe fiecare tabel îți spune dacă problema stă în conținutul articolelor, în wp_options sau în tabele de plugin la care nu te așteptai.

Pot rezolva conținutul mixt cu o simplă căutare și înlocuire în sql?

Doar pentru valori scalare precum siteurl și home. Tot ce stochează WordPress ca tablou serializat, adică majoritatea valorilor de opțiuni și de metadate, înregistrează lungimea în octeți a fiecărui șir din interior. O înlocuire brută schimbă textul, dar lasă lungimea veche, valoarea nu mai poate fi deserializată, iar setarea revine în tăcere la valoarea implicită.

Ce regulă .htaccess forțează https în WordPress?

O RewriteRule protejată de o condiție pe variabila HTTPS, plasată deasupra marcajelor BEGIN WordPress, ca salvarea legăturilor permanente să nu o șteargă. În spatele unui proxy sau al unui CDN, variabila HTTPS apare ca off chiar și la cereri securizate, deci testează acolo antetul X-Forwarded-Proto, altfel regula va redirecționa la nesfârșit.

De ce a intrat site-ul într-o buclă de redirecționare după ce am forțat https?

Aproape sigur TLS-ul tău se termină la un proxy sau la un CDN care apoi trimite http simplu către Apache. Regula vede o cerere nesecurizată, redirecționează spre https, proxy-ul trimite iar http, iar bucla se repetă. Comută condiția pe antetul X-Forwarded-Proto și setează variabila de server HTTPS în wp-config.php.

Conținutul mixt strică toată pagina sau doar lacătul?

Depinde de resursă. Browserele blochează complet conținutul mixt activ, adică scripturi, foi de stil și cadre iframe, ceea ce poate strica aspectul sau funcționalitatea fără nicio explicație vizibilă. Conținutul mixt pasiv, cum sunt imaginile și clipurile video, de obicei se încarcă în continuare, dar lacătul este retrogradat și vizitatorii pot vedea un indicator de site nesecurizat. Merită rezolvate amândouă.