secaudit.eu
Protezione

WordPress HTTPS: risolvere mixed content e redirect

WordPress HTTPS fatto bene: certificato gratuito, sostituzione sicura degli URL con WP-CLI, un solo redirect 301, loop dietro proxy, mixed content e scadenza.

Di , AngelForce OÜ 8 min di lettura

Anche in: English Deutsch Français Español Polski Português Türkçe

La maggior parte dei problemi di HTTPS su WordPress nasce da una migrazione lasciata a metà, non dalla mancanza del certificato. Il certificato è installato e la home mostra il lucchetto. Gli articoli del blog, però, hanno un'icona di avviso, nei contenuti restano vecchi link http:// e chi digita example.com attraversa due o tre redirect prima di arrivare alla pagina vera. Questa guida porta il sito fuori da quella situazione, seguendo un ordine sicuro.

WordPress HTTPS: il certificato è solo il primo passo

Si parte dalla misurazione. Questi due comandi mostrano che cosa attraversa il browser di un visitatore:

curl -sI http://example.com
curl -sIL http://example.com | grep -iE '^(HTTP|location)'

Il primo comando mostra il codice di stato e l'intestazione Location della prima risposta. Il secondo segue ogni redirect. Se prima del 200 finale compare più di un 301, c'è una catena di redirect. Si apra poi il sito nel browser, si prema F12 e si guardi la Console: gli avvisi di mixed content (contenuto misto) compaiono lì.

Conviene anche chiarire che cosa fa HTTPS. Cifra il traffico tra browser e server e lo protegge da modifiche durante il transito. È una delle misure citate dal GDPR all'art. 32, par. 1, lett. a), la cifratura, per password di accesso, moduli di contatto e dati di checkout. Non dice nulla sulla sicurezza del sito in sé. Un sito con certificato valido può comunque usare un plugin vulnerabile, ospitare malware o avere un account amministratore protetto da password123. Come la cifratura si inserisce tra le altre misure di sicurezza è spiegato in GDPR articolo 32 per WordPress.

Schema: tre punti di ingresso HTTP e www reindirizzano ciascuno con un solo 301 verso un unico URL HTTPS canonico
Ogni variante dei vecchi URL dovrebbe raggiungere l'indirizzo HTTPS canonico in un solo passaggio

Certificato gratuito dall'hosting o tramite ACME

La maggior parte dei pannelli di controllo degli hosting europei offre un certificato gratuito Let's Encrypt con un semplice interruttore, spesso alla voce «SSL/TLS» o «Sicurezza». Lo si attivi e ci si assicuri che copra sia example.com sia www.example.com. Un certificato valido per un solo nome provoca errori nel browser sull'altro, anche se quel nome si limita a reindirizzare.

Su un server proprio si usa un client ACME. Certbot è la scelta più comune e configura il rinnovo automatico:

sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run

Con Apache si usa --apache al posto di --nginx. La prova a vuoto (dry run) verifica che il rinnovo funzioni davvero: se fallisce adesso, fallirà anche al prossimo rinnovo reale.

Per ottenere il lucchetto non serve pagare un certificato a validazione del dominio. I certificati a pagamento hanno senso per contratti di assistenza o per esigenze di validazione specifiche. Per la cifratura, un certificato gratuito è altrettanto robusto.

Backup, poi cambio di indirizzo WordPress e indirizzo del sito

Prima di modificare qualsiasi cosa si faccia un backup del database. I passaggi successivi riscrivono migliaia di righe e il backup è il tasto «annulla». Backup di WordPress che si ripristinano davvero spiega come realizzare backup di cui ci si può fidare. Con WP-CLI basta una riga:

wp db export before-https.sql

Poi si vada su Impostazioni > Generale e si cambino sia Indirizzo WordPress (URL) sia Indirizzo del sito (URL) da http:// a https://. Se WordPress è installato in una sottocartella, si mantenga il percorso. Lo stesso si può fare da riga di comando:

wp option update home 'https://example.com'
wp option update siteurl 'https://example.com'

Se i due campi sono in grigio, WP_HOME e WP_SITEURL sono definiti in wp-config.php. In tal caso si modifichino i valori lì: le impostazioni della bacheca non possono sovrascriverli.

Se si viene disconnessi e non è più possibile accedere, per esempio perché il certificato non funziona ancora, si definiscano temporaneamente WP_HOME e WP_SITEURL in wp-config.php con il vecchio indirizzo http:// (o li si riportino ai valori precedenti, se già presenti), oppure si eseguano i due comandi wp option update con i vecchi valori. Si sistemi prima il certificato, poi si riprovi.

Sostituire in sicurezza i vecchi URL http:// con wp search-replace

Articoli, menu, widget e opzioni del tema contengono ancora centinaia di link http://example.com. Non si correggano con un REPLACE SQL diretto. WordPress e molti plugin salvano le impostazioni come PHP serializzato, che registra la lunghezza di ogni stringa, per esempio s:22:"http://example.com/img". Aggiungere la «s» rende la stringa più lunga di un carattere. La lunghezza memorizzata non corrisponde più, PHP non riesce a leggere il valore e widget, layout del page builder o impostazioni del tema spariscono senza alcun avviso.

[wp search-replace](https://developer.wordpress.org/cli/commands/search-replace/) deserializza i dati, sostituisce il testo e li serializza di nuovo con le lunghezze corrette. Si esegua sempre prima una prova a vuoto:

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

Si leggano i conteggi per tabella. Se sembrano plausibili, si lanci lo stesso comando senza --dry-run. Alcune precisazioni:

  • --skip-columns=guid lascia stare i GUID degli articoli. Sono identificatori, non link, e per convenzione non si modificano.
  • --all-tables interviene su ogni tabella del database. Se il database è condiviso con un altro sito o un'altra applicazione, si usi invece --all-tables-with-prefix.
  • Se l'host canonico è senza www, si esegua una seconda sostituzione da http://www.example.com a https://example.com.
  • Alcuni page builder salvano JSON con slash con carattere di escape (http:\/\/example.com), che una ricerca semplice non trova. Si faccia una prova a vuoto anche per quella forma, oppure si usi lo strumento di sostituzione URL del page builder.

Poi si svuotino tutte le cache: wp cache flush, il plugin di page cache, l'eventuale cache del server e la CDN. Se il page builder lo permette, si rigenerino i file CSS. Senza accesso SSH, un plugin di search-replace che gestisca i dati serializzati, come Better Search Replace, svolge lo stesso compito. Va eliminato al termine.

Un solo redirect 301 verso l'host HTTPS canonico

Si scelga un solo indirizzo canonico, con o senza www, e lo si mantenga. Ogni altra variante deve arrivare direttamente a quell'indirizzo con un unico redirect 301. Una catena come http → https → www → finale rallenta ogni prima visita, spreca scansioni dei motori di ricerca e rende difficile individuare gli errori.

Apache, da inserire sopra il blocco # BEGIN WordPress in .htaccess:

RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^ https://example.com%{REQUEST_URI} [L,R=301]

nginx:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name www.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    return 301 https://example.com$request_uri;
}

Il redirect va tenuto in un unico punto. Se un plugin, .htaccess e la CDN reindirizzano tutti insieme, si ottengono catene o loop. Si scelga il server oppure la CDN e si rimuovano gli altri.

Loop di redirect dietro una CDN o un reverse proxy

Il loop classico è la modalità SSL «Flexible» di Cloudflare. Il browser parla in HTTPS con Cloudflare, ma Cloudflare parla in semplice HTTP con il server. Il server vede HTTP, reindirizza a HTTPS e il ciclo ricomincia. Si installi un certificato sul server e si imposti la modalità Full (strict) nelle impostazioni SSL/TLS di Cloudflare. Il traffico verso il server risulta così cifrato e il certificato del server viene validato.

Altri proxy e load balancer trasmettono lo schema originale nell'intestazione X-Forwarded-Proto. WordPress non la legge per impostazione predefinita. Si aggiunga questo codice sopra la riga «stop editing» in wp-config.php:

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

FORCE_SSL_ADMIN e la pagina di login

define( 'FORCE_SSL_ADMIN', true );

Con questa costante WordPress serve la pagina di login e la bacheca solo tramite HTTPS. Password e cookie dell'amministratore non viaggiano mai in chiaro, anche se qualcuno apre un vecchio segnalibro http://. È una rete di sicurezza, non la soluzione per i redirect: per le pagine pubbliche non fa nulla. Altre impostazioni di wp-config.php sono trattate in Hardening di wp-config.php: 10 impostazioni da verificare.

Trovare il mixed content residuo negli strumenti per sviluppatori

Si parla di mixed content quando una pagina HTTPS carica una risorsa tramite HTTP. I browser bloccano script e frame non sicuri e segnalano o aggiornano le immagini: per questo il lucchetto scompare. Si apra DevTools su una pagina interessata. La Console elenca ogni richiesta non sicura con il relativo URL. Nei browser basati su Chromium, scrivendo scheme:http nel filtro della scheda Network si vedono tutte insieme.

Dopo un search-replace pulito, le cause rimaste di solito stanno fuori dal database. Le fonti tipiche sono URL scritti a mano nei file del tema, widget HTML personalizzati, CSS personalizzato (background: url(http://...)), file CSS dei page builder e vecchi embed di terze parti. Per cercarli nel tema:

grep -rn "http://" wp-content/themes/your-theme/
wp db search 'http://example.com'

Il problema va corretto alla fonte. I plugin che riscrivono gli URL a ogni caricamento di pagina nascondono il difetto senza risolverlo e smettono di funzionare una volta disattivati. La direttiva CSP upgrade-insecure-requests può fare da ponte temporaneo durante la pulizia ed è spiegata in Header di sicurezza per WordPress.

Non basta controllare la home. Si verifichino il checkout, il modulo di contatto, la pagina di login, un articolo del blog, l'archivio di una categoria e ogni landing page costruita con un template diverso.

Monitorare la scadenza del certificato, poi attivare HSTS

Il rinnovo automatico può fallire senza che nessuno se ne accorga. Le cause più comuni sono una modifica del DNS, il passaggio a una nuova CDN, oppure una regola di firewall o di redirect che blocca /.well-known/acme-challenge/. Si configuri un monitoraggio esterno che controlli il certificato e avvisi almeno due settimane prima della scadenza. Molti servizi di uptime includono questo controllo. Per leggere la data di scadenza da soli:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate

HSTS (RFC 6797) viene per ultimo. Indica ai browser di usare solo HTTPS: un certificato difettoso diventa quindi un errore bloccante per i visitatori e, con includeSubDomains, lo diventa anche ogni sottodominio dimenticato che funziona solo in HTTP. Lo si attivi solo quando tutti i sottodomini e i redirect funzionano, partendo da un max-age breve. Valori e avvertenza sul preload sono trattati in Header di sicurezza per WordPress.

Il plugin di audit di secaudit.eu controlla le impostazioni di hardening e di configurazione, insieme all'integrità dei file e ai plugin obsoleti. Dopo la migrazione si dispone così di una registrazione dello stato del sito, utile per la propria documentazione. → secaudit.eu

Prossimo passo: si esegua il controllo dei salti con curl -sIL su tutte e quattro le varianti (http, http://www, https://www, https). Poi si aprano le cinque pagine più importanti con la Console di DevTools visibile. Al massimo un 301 per variante, un 200 diretto sull'URL HTTPS canonico e nessun messaggio di mixed content indicano che la migrazione è completa.

FAQ

Un certificato gratuito basta per un negozio online?

Sì. Un certificato gratuito a validazione del dominio cifra il traffico con la stessa robustezza di uno a pagamento. La sicurezza del negozio dipende da plugin, aggiornamenti, accessi e configurazione dei pagamenti, non dal prezzo del certificato.

Perché il lucchetto scompare solo su alcune pagine?

Quelle pagine caricano almeno una risorsa tramite HTTP. Di solito è un'immagine, uno script o un embed proveniente da un widget, un template o una sezione del page builder che le altre pagine non usano. La Console di DevTools su quella pagina mostra l'URL esatto.

HTTPS rende il mio sito conforme al GDPR?

No. HTTPS è una misura tecnica prevista dall'art. 32, la cifratura in transito. Il GDPR richiede inoltre di mettere in sicurezza i sistemi stessi, di ripristinare i dati dopo gli incidenti, di verificare regolarmente le misure adottate e di documentare tutto questo.

Questo articolo è un'informazione generale e non costituisce consulenza legale.

Controlla il tuo sito

Security Audit & Compliance è il plugin WordPress di secaudit.eu: cerca malware, spam nascosto, file core modificati e codice vulnerabile, e collega i risultati all’art. 32 del GDPR, all’art. 21 della NIS2 e alla OWASP Top 10.

Scopri cosa controlla il plugin
Melike Görel

Melike Görel scrive le guide di secaudit.eu sulla sicurezza di WordPress e sulla conformità alle norme UE per AngelForce OÜ, l’azienda di software di Tallinn che sta dietro a secaudit.eu.

Realizzato con l’aiuto dell’IA e verificato prima della pubblicazione. Questo articolo è un’informazione generale e non una consulenza legale.