Μετάβαση στο περιεχόμενο
Server & .htaccess

Μικτό περιεχόμενο στο WordPress μετά τη μετάβαση σε HTTPS

Καθάρισε τις προειδοποιήσεις μικτού περιεχομένου στο WordPress μετά τη μετάβαση σε HTTPS: βρες τα http:// URL που έμειναν στη βάση, αντικατάστησέ τα με ασφάλεια και επίβαλε HTTPS στο .htaccess.

Δημοσιεύτηκε

Το πιστοποιητικό σου είναι εγκατεστημένο, το site φορτώνει μέσω HTTPS, και το λουκέτο επιμένει να μην εμφανίζεται. Αυτό είναι μικτό περιεχόμενο: η σελίδα ήρθε μέσω HTTPS, αλλά κάτι μέσα της — μια εικόνα, ένα stylesheet, ένα script — ζητείται ακόμη μέσω απλού http://. Εδώ βλέπουμε πώς να βρεις αυτά τα URL, πώς να τα αντικαταστήσεις χωρίς να καταστρέψεις σειριοποιημένα δεδομένα, πώς να διορθώσεις τα siteurl και home, και πώς να επιβάλεις HTTPS στον server ώστε το πρόβλημα να μην ξαναγυρίσει.

Τι είναι στην πραγματικότητα το μικτό περιεχόμενο

Η εγκατάσταση ενός πιστοποιητικού αλλάζει το πώς κρυπτογραφείται η σύνδεση. Δεν αλλάζει το τι ζητούν οι σελίδες σου. Κάθε http://yoursite.com/wp-content/uploads/logo.png που γράφτηκε σε ένα άρθρο, ένα widget ή μια ρύθμιση θέματος πριν τη μετάβαση κάθεται ακόμη στη βάση, και ο browser το ζητά υπάκουα μέσω μη ασφαλούς σύνδεσης.

Οι browsers αντιμετωπίζουν δύο κατηγορίες διαφορετικά, και η διάκριση μετράει όταν αποφασίζεις πόσο επείγον είναι το ζήτημα:

  • Ενεργό μικτό περιεχόμενο — scripts, stylesheets, iframes, XHR. Μπλοκάρεται εντελώς. Γι’ αυτό ένα site μπορεί να φαίνεται χαλασμένο μετά από μετάβαση σε HTTPS χωρίς κανένα μήνυμα σφάλματος πουθενά: ένα stylesheet απορρίφθηκε σιωπηλά.
  • Παθητικό μικτό περιεχόμενο — εικόνες, ήχος, βίντεο. Συνήθως φορτώνει, αλλά το λουκέτο υποβαθμίζεται και κάποιοι browsers δείχνουν ένδειξη «μη ασφαλές».

Και τα δύο αξίζει να διορθωθούν. Μόνο το πρώτο όμως σπάει πράγματα.

Βήμα 1: βρες τι είναι ακόμη μη ασφαλές

Ξεκίνα από τον browser. Άνοιξε τη σελίδα, άνοιξε τα εργαλεία προγραμματιστή, και διάβασε την κονσόλα. Κάθε μπλοκαρισμένο ή υποβαθμισμένο αίτημα ονομάζεται εκεί με το πλήρες URL του. Αυτό σου λέει τι είναι μη ασφαλές· δεν σου λέει πού αποθηκεύεται.

Για αυτό, κοίτα κατευθείαν στη βάση. Κάνε εξαγωγή και ψάξε στο dump:

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. Οτιδήποτε με domain που δεν αναγνωρίζεις είναι εξωτερικό ενσωματωμένο στοιχείο, που θέλει άλλη λύση (παρακάτω).

Βήμα 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');

Αυτά τα δύο είναι απλά αλφαριθμητικά, όχι σειριοποιημένοι πίνακες, οπότε μια απευθείας ενημέρωση 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: η αντικατάσταση πρέπει να σέβεται τη σειριοποίηση

Όλα τα υπόλοιπα στη βάση είναι εκεί που κρύβεται ο πραγματικός κίνδυνος, και δεν είναι ο κίνδυνος που περιμένουν οι περισσότεροι. Το πρόβλημα δεν είναι ότι η αντικατάσταση θα χάσει URL. Είναι ότι θα πετύχει σε επίπεδο κειμένου και θα καταστρέψει τα γύρω δεδομένα.

Το WordPress αποθηκεύει πίνακες και αντικείμενα ως σειριοποιημένα αλφαριθμητικά της PHP, και αυτή η μορφή καταγράφει το μήκος σε bytes κάθε αλφαριθμητικού που περιέχει:

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

Άλλαξε το http:// σε https:// με ωμό REPLACE σε SQL και το κείμενο γίνεται ένα byte μεγαλύτερο ενώ το δηλωμένο μήκος μένει στην παλιά τιμή. Η PHP διαβάζει το μήκος, προχωράει τόσα bytes, δεν βρίσκει τον τερματιστή εκεί που τον περιμένει, και αρνείται να αποσειριοποιήσει όλο τον πίνακα. Τότε το WordPress δίνει στο θέμα ή στο πρόσθετο μια τιμή που συμπεριφέρεται σαν να μην αποθηκεύτηκε ποτέ η ρύθμιση.

Το σύμπτωμα δεν είναι σφάλμα. Είναι widgets που εξαφανίζονται, ρυθμίσεις του customizer που μηδενίζονται, και διατάξεις page builder που εμφανίζονται κενές — χωρίς τίποτα στη διαχείριση να δείχνει ότι κάτι πήγε στραβά. Η ανάκτηση από αυτό χωρίς αντίγραφο ασφαλείας είναι πραγματικά επώδυνη, οπότε πάρε ένα:

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

Μετά χρησιμοποίησε ένα εργαλείο που αποσειριοποιεί, αντικαθιστά μέσα στην αποκωδικοποιημένη δομή, και ξανασειριοποιεί με διορθωμένα μήκη. Πρώτα δοκιμαστικά:

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 και ποιες απαιτούν χειρισμό με επίγνωση της σειριοποίησης πριν τρέξεις οτιδήποτε, φτιάξε τις εντολές με το εργαλείο αναζήτησης και αντικατάστασης SQL για WordPress.

Αν δεν μπορείς να χρησιμοποιήσεις τη γραμμή εντολών, ένα πρόσθετο μετάβασης που δηλώνει ρητά ότι χειρίζεται σειριοποιημένα δεδομένα κάνει την ίδια δουλειά αποκωδικοποίησης μέσα από τη διαχείριση. Αν ένα εργαλείο δεν το δηλώνει, υπόθεσε ότι δεν το κάνει.

Βήμα 4: επίβαλε HTTPS στον server

Το καθάρισμα της βάσης σταματά τις σελίδες σου από το να ζητούν μη ασφαλείς πόρους. Δεν εμποδίζει έναν επισκέπτη να φτάσει σε http:// εξαρχής. Αυτό είναι ανακατεύθυνση σε επίπεδο server, και στον 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 — ό,τι είναι μέσα τους πετιέται κάθε φορά που κάποιος αποθηκεύει τους μόνιμους συνδέσμους. Και αυτό δουλεύει μόνο αν το mod_rewrite είναι ενεργό και ο virtual host επιτρέπει overrides (AllowOverride All, ή τουλάχιστον FileInfo). Στον nginx το .htaccess αγνοείται εντελώς και αυτό πρέπει να μπει σε μπλοκ server.

Ένα RedirectMatch δεν κάνει αυτή τη δουλειά. Ταιριάζει μόνο τη διαδρομή του αιτήματος και δεν έχει τρόπο να ελέγξει αν το τρέχον αίτημα είναι ήδη ασφαλές, οπότε ανακατευθύνει τα HTTPS αιτήματα στον εαυτό τους. Το RewriteRule με συνθήκη παραπάνω είναι το σωστό εργαλείο.

Ο βρόχος ανακατευθύνσεων, και γιατί συμβαίνει

Αν το site αρχίσει να ανακατευθύνει επ’ άπειρον τη στιγμή που προσθέτεις αυτόν τον κανόνα, το TLS σου τερματίζει κάπου πιο πάνω — σε load balancer, reverse proxy, ή CDN — που μετά προωθεί απλό HTTP στον Apache. Ο Apache βλέπει μη ασφαλές αίτημα, ανακατευθύνει σε HTTPS, το proxy προωθεί ξανά HTTP, και πάει λέγοντας.

Έλεγξε την προωθημένη κεφαλίδα αντ’ αυτού:

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

Το ίδιο το WordPress έχει το ίδιο τυφλό σημείο σε αυτή τη διάταξη — η is_ssl() διαβάζει το $_SERVER['HTTPS'], που το proxy δεν ορίζει ποτέ, οπότε τα URL της διαχείρισης βγαίνουν ως http://. Πρόσθεσε αυτό στο wp-config.php, πάνω από τη γραμμή «stop editing»:

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

Αξίζει να είμαστε ειλικρινείς για τον κίνδυνο: το X-Forwarded-Proto είναι κεφαλίδα που στέλνει ο πελάτης. Το να την εμπιστεύεσαι είναι σωστό μόνο όταν ένα proxy που ελέγχεις την ξαναγράφει πάντα. Σε server προσβάσιμο απευθείας από το internet, μπορεί να πλαστογραφηθεί.

Τι δεν θα διορθώσει μια αντικατάσταση στη βάση

  • URL γραμμένα σκληρά μέσα σε αρχεία. Οτιδήποτε έχει γραφτεί στο functions.php, σε πρότυπο θυγατρικού θέματος, ή σε ενσωματωμένη διαδρομή πόρου δεν βρίσκεται στη βάση. Ψάξε τον κατάλογο του θέματος ξεχωριστά με grep.
  • JSON με χαρακτήρες διαφυγής. Κάποιοι builders αποθηκεύουν URL ως https:\/\/example.com. Μια αναζήτηση της κανονικής μορφής τα χάνει εντελώς, οπότε μπορεί να χρειαστεί δεύτερο πέρασμα στη μορφή με διαφυγή.
  • Εξωτερικοί πόροι. Ένα script τρίτου ή ένα ενσωματωμένο στοιχείο διαθέσιμο μόνο μέσω HTTP δεν διορθώνεται από τη δική σου πλευρά. Βρες έκδοση HTTPS ή παράτησέ το.
  • Caches. Η cache σελίδων, η object cache και τα αντίγραφα στο CDN συνεχίζουν να σερβίρουν το παλιό markup μετά από μια σωστή αντικατάσταση. Καθάρισέ τα και τα τρία πριν συμπεράνεις ότι η αντικατάσταση απέτυχε.

Ένα πράγμα που αξίζει να παραλείψεις: η κεφαλίδα Content Security Policy upgrade-insecure-requests θα σωπάσει τις προειδοποιήσεις ξαναγράφοντας τα μη ασφαλή αιτήματα μέσα στον browser. Αντιμετωπίζει το σύμπτωμα και αφήνει τα λάθος URL στη βάση σου, από όπου η επόμενη εξαγωγή, μετάβαση ή ροή θα τα κουβαλήσει παρακάτω. Διόρθωσε πρώτα τα δεδομένα, και μετά χρησιμοποίησέ την ως δίχτυ ασφαλείας αν τη θέλεις.

Ακόμη κολλημένος;

Ξαναέλεγξε τα siteurl και home μετά από κάθε βήμα — πρόσθετα και εργαλεία μετάβασης καμιά φορά τα ξαναγράφουν πίσω από την πλάτη σου. Αν η κονσόλα εξακολουθεί να ονομάζει ένα μη ασφαλές URL που δεν βρίσκεις στο dump, δες τον πηγαίο κώδικα της σελίδας και ψάξ’ το εκεί: αν εμφανίζεται στο παραγόμενο markup αλλά όχι στη βάση, τότε χτίζεται σε PHP, και το θέμα ή το πρόσθετο που το παράγει είναι αυτό που πρέπει να διορθωθεί.

FAQ

Ερωτήσεις

Γιατί το site μου στο WordPress εξακολουθεί να δείχνει προειδοποιήσεις μικτού περιεχομένου μετά την εγκατάσταση πιστοποιητικού SSL;

Το πιστοποιητικό αλλάζει μόνο το πώς κρυπτογραφείται η σύνδεση, όχι το τι ζητούν οι σελίδες σου. Τα παλιά http:// URL παραμένουν αποθηκευμένα στη βάση δεδομένων, μέσα στο περιεχόμενο άρθρων, στις επιλογές των widget και στις ρυθμίσεις του θέματος. Ο browser φορτώνει τη σελίδα μέσω HTTPS, βλέπει μια εικόνα ή ένα script να ζητείται μέσω απλού HTTP, και αναφέρει μικτό περιεχόμενο σε μια κατά τα άλλα ασφαλή σελίδα.

Πώς βρίσκω τα http:// URL που έμειναν στη βάση δεδομένων του WordPress;

Άνοιξε μια σελίδα σε browser και διάβασε την κονσόλα προγραμματιστή, που ονομάζει κάθε μη ασφαλές αίτημα με το URL του. Μετά κάνε εξαγωγή της βάσης και ψάξε στο dump το domain σου με το πρόθεμα http. Μετρώντας τα ευρήματα ανά πίνακα καταλαβαίνεις αν το πρόβλημα ζει στο περιεχόμενο των άρθρων, στο wp_options, ή σε πίνακες προσθέτων που δεν περίμενες.

Μπορώ να διορθώσω το μικτό περιεχόμενο με μια απλή αναζήτηση και αντικατάσταση σε SQL;

Μόνο για βαθμωτές τιμές όπως τα siteurl και home. Οτιδήποτε αποθηκεύει το WordPress ως σειριοποιημένο πίνακα, που καλύπτει τις περισσότερες τιμές επιλογών και meta, καταγράφει το μήκος σε bytes κάθε αλφαριθμητικού μέσα του. Μια ωμή αντικατάσταση αλλάζει το κείμενο αλλά αφήνει το παλιό μήκος, η τιμή τότε αποτυγχάνει να αποσειριοποιηθεί, και η ρύθμιση επιστρέφει σιωπηλά στην προεπιλογή της.

Ποιος κανόνας .htaccess επιβάλλει HTTPS στο WordPress;

Ένα RewriteRule με συνθήκη πάνω στη μεταβλητή HTTPS, τοποθετημένο πάνω από τους δείκτες BEGIN WordPress ώστε η αποθήκευση των μόνιμων συνδέσμων να μην το σβήνει. Πίσω από proxy ή CDN η μεταβλητή HTTPS διαβάζεται ως off ακόμη και σε ασφαλή αιτήματα, οπότε έλεγξε την κεφαλίδα X-Forwarded-Proto αντ' αυτού, αλλιώς ο κανόνας θα ανακατευθύνει επ' άπειρον.

Γιατί το site μου μπήκε σε βρόχο ανακατευθύνσεων μετά την επιβολή HTTPS;

Το TLS σου σχεδόν σίγουρα τερματίζει σε proxy ή CDN που μετά προωθεί απλό HTTP στον Apache. Η επανεγγραφή βλέπει ένα μη ασφαλές αίτημα, ανακατευθύνει σε HTTPS, το proxy προωθεί ξανά HTTP, και ο βρόχος επαναλαμβάνεται. Άλλαξε τη συνθήκη στην κεφαλίδα X-Forwarded-Proto και όρισε τη μεταβλητή HTTPS στο wp-config.php.

Το μικτό περιεχόμενο χαλάει όλη τη σελίδα ή μόνο το λουκέτο;

Εξαρτάται από τον πόρο. Οι browsers μπλοκάρουν εντελώς το ενεργό μικτό περιεχόμενο, όπως scripts, stylesheets και iframes, κάτι που μπορεί να σπάσει τη διάταξη ή τη λειτουργικότητα χωρίς καμία ορατή εξήγηση. Το παθητικό μικτό περιεχόμενο, όπως εικόνες και βίντεο, συνήθως φορτώνει, αλλά το λουκέτο υποβαθμίζεται και οι επισκέπτες μπορεί να δουν ένδειξη «μη ασφαλές». Και τα δύο αξίζει να διορθωθούν.