Короткий ответ
Страница открылась по https://site.example, но HTML/CSS тянет http://site.example/... или чужой http-CDN. Современный браузер блокирует активный mixed content (скрипты) и предупреждает о картинках. Лечение: исправить URL в шаблоне и БД, не «разрешить небезопасный контент» пользователям. IP origin 10.0.20.40, webroot /var/www/site.example.
Параллельно нужен корректный 301 с HTTP: редирект. CSP upgrade-insecure-requests — вспомогательный костыль, не замена правки данных. Полные заголовки: headers.
Редирект 301 с http→https не убирает mixed: браузер уже на https, а HTML всё ещё содержит http-URL скриптов. Чините разметку и БД. Плагины кэша CSS часто хранят абсолютные http — сброс кэша обязателен после search-replace.
Симптомы и как отличить
- Консоль:
Mixed Content: The page at 'https://site.example/' was loaded over HTTPS, but requested an insecure .... - Стили «как без темы», скрипты админки WP мертвы.
curl -s https://site.example/ | grep -i 'http://'.
| Что | Не mixed |
|---|---|
| Сам HTTPS не открывается | cert/DNS |
404 на /style.css | 404 после переноса |
| Редирект-петля | HSTS/редирект |
Возможные причины
- WordPress
siteurl/homeосталисьhttp://. - Контент в
wp_postsс абсолютными http. - Хардкод в теме
http://site.example/wp-content/.... - Плагин CDN с http endpoint.
- CSP
default-srcбез https, или наоборотupgrade-insecure-requestsотсутствует, а URL не поправили. - Трекинг-пиксели на http.
- После миграции search-replace не тронул сериализованные опции.
Диагностика
1. Заголовки и HTML
curl -sI --resolve site.example:443:10.0.20.40 https://site.example/ | grep -iE 'content-security|strict-transport|location'
curl -s --resolve site.example:443:10.0.20.40 https://site.example/ | grep -oE 'http://[^"'\'' ]+' | sort -u | head2. WordPress опции
sudo -u www-data wp --path=/var/www/site.example option get siteurl
sudo -u www-data wp --path=/var/www/site.example option get homeДолжно быть https://site.example без хвоста-слэша или со — как принято у вас, но схема https.
3. Поиск по файлам темы (не по всему uploads вслепую)
sudo grep -R --include='*.php' --include='*.js' --include='*.css' 'http://site.example' /var/www/site.example/wp-content/themes | head4. CSP в nginx
sudo nginx -T 2>/dev/null | grep -i content-security-policyЖёсткий CSP без https: для script-src плюс http-URL = блокировка даже после upgrade, если источник другой.
Решение
Снимок БД до search-replace.
sudo -u www-data wp --path=/var/www/site.example db export /root/site.example-before-https.sqlСценарий A. siteurl
sudo -u www-data wp --path=/var/www/site.example option update siteurl 'https://site.example'
sudo -u www-data wp --path=/var/www/site.example option update home 'https://site.example'Либо константы в wp-config.php:
define('WP_HOME', 'https://site.example');
define('WP_SITEURL', 'https://site.example');Сценарий B. Контент и сериализация
sudo -u www-data wp --path=/var/www/site.example search-replace 'http://site.example' 'https://site.example' --all-tablesНе sed по sql-дампу: сломаете длину сериализованных строк → 500.
Сценарий C. Тема/хардкод
Замените на https:// или на относительные //site.example (всё ещё схема-относительные; лучше явный https или функции home_url()).
Сценарий D. Временный upgrade-insecure-requests
add_header Content-Security-Policy "upgrade-insecure-requests" always;Браузер перепишет http→https на том же хосте. Чужой http-домен без HTTPS это не спасёт — уберите или найдите https-зеркало.
После зачистки URL можно вписать полноценный CSP, не только upgrade. HSTS — после стабильного HTTPS: HSTS.
Сценарий E. Сторонние скрипты только http
Замените провайдера или выкиньте тег. Не проксируйте чужой http через свой origin без понимания ToS и кэша.
Сценарий F. Кэш, CDN и «в браузере ещё http»
После search-replace HTML origin уже https, а Cloudflare/Nginx fastcgi_cache/proxy_cache отдаёт старую страницу. Purge кэша CDN и локального fastcgi_cache_path. Плагины WP Super Cache / LiteSpeed хранят файлы в wp-content/cache — удаляйте кэш, не uploads.
srcset и JSON в data- атрибутах Elementor часто содержат http. Повторите wp search-replace с --all-tables и сброс CSS Elementor (Regenerate CSS в UI). Проверьте письма WooCommerce: шаблон email-header с http логотипом — это не mixed в браузере витрины, но та же болезнь данных.
CSP upgrade-insecure-requests не поможет, если скрипт грузится с http://third.example без HTTPS-версии: браузер запросит https, получит fail, скрипт умрёт. Тогда либо https-URL провайдера, либо удалить тег.
Для админки WP отдельно откройте DevTools на /wp-admin/: load-scripts.php и load-styles.php должны быть https. FORCE_SSL_ADMIN без починенного siteurl даёт петлю — сначала опции, потом константа.
Не используйте расширение «HTTP Everywhere наоборот» у пользователя как стандарт лечения.
Как проверить, что проблема устранена
- DevTools → Security: нет mixed.
- Повторный grep HTML без
http://site.example. curl -sIпоказывает HTTPS и при наличии CSP.- Админка WP загружает
load-scripts.phpбез блокировок.
Проверьте внутренние страницы и одну запись со старыми картинками в контенте.
Если не помогло
- Elementor/кэш CSS с абсолютными http — сброс кэша плагина, не
rm -rf wp-content/uploads. FORCE_SSL_ADMINбез siteurl — частично.- WebSocket
ws://— нуженwss://. - Письма и RSS с http — отдельные шаблоны.
- В HTML нет
http://site.example, но DevTools орёт наhttp://static.oldcdn.example— это не ваш search-replace, а хардкод темы/виджета. Замените виджет или зеркало https, иначе CSP upgrade не спасёт чужой хост без TLS. - Админка зелёная, витрина нет: кэш анонимов на edge. Purge по URL главной и одной записи со старой датой.
Профилактика
- Сразу выпускать HTTPS в чеклисте переноса.
- Запретить http-URL в code review тем.
- Staging с тем же CSP, что прод.
- После search-replace откройте DevTools на витрине и на
/wp-admin/с отключённым кэшем: active mixed (скрипты) должен быть ноль.
FAQ
//site.example/foo достаточно?
Схема-относительный URL возьмёт https со страницы. На email/PDF не сработает. Предпочтительнее https.
Нужно ли редиректить каждый ассет 301?
Редирект спасёт часть картинок, активные скрипты браузер не подхватит с http. Чините HTML.
CDN http://cdn.site.example
Включите HTTPS на CDN и поправьте URL. Иначе mixed.
Почему grep по uploads молчит, а браузер орёт?
URL внутри CSS в uploads/elementor/css. Ищите http:// в css-кэше.
Apache Header CSP vs nginx add_header
Один слой. Дубль разных политик путает. add_header в location без always может пропасть на 404.