Браузер показывает SSL_ERROR_RX_RECORD_TOO_LONG при заходе на сайт по HTTPS: на порт 443 отвечает обычный HTTP

Что было. Сайт на Apache за домашним маршрутизатором. По HTTPS браузер выдаёт SSL_ERROR_RX_RECORD_TOO_LONG, хотя сертификат получен и виртуальный хост для 443 «настроен».

Что означает ошибка. Клиент начал TLS-рукопожатие, а в ответ получил не TLS-запись, а текст (обычно HTML или заголовки HTTP). Браузер честно сообщает: запись слишком длинная для TLS. Почти всегда это значит, что на порту 443 отвечает сервер без включённого TLS: либо не тот виртуальный хост, либо конфигурация не загрузилась, либо запрос пришёл не туда (проброс портов на другой сервис).

Что нашли у автора (подтверждено): сразу три проблемы, каждая давала тот же симптом:

  1. В конфигурации виртуального хоста использовалась директива Header, а модуль mod_headers не был включён; Apache падал на проверке синтаксиса, и в работе оставалась старая конфигурация без TLS.
  2. В apache2.conf была закомментирована строка IncludeOptional sites-enabled/*.conf, поэтому виртуальные хосты вообще не загружались; на 443 отвечал хост по умолчанию без SSL.
  3. На маршрутизаторе не был проброшен порт 80, из-за чего certbot не мог пройти проверку, и на сервере не было валидного сертификата.
    После включения модуля, восстановления IncludeOptional, проброса 80/443 и правил ufw сайт заработал.

Как разобрать за пять минут.

apachectl configtest                      # синтаксис и модули
apachectl -S                              # какие vhost на каких портах, какой по умолчанию
ss -ltnp | grep -E ':443|:80'             # кто слушает
openssl s_client -connect сервер:443 -servername домен </dev/null | head   # TLS или текст
curl -vk https://домен/ 2>&1 | head -20

Если openssl s_client показывает wrong version number или HTML вместо сертификата, на 443 отвечает HTTP. Проверить, что у хоста с <VirtualHost *:443> есть SSLEngine on и пути к сертификатам, что включены mod_ssl (a2enmod ssl) и сам сайт (a2ensite), и что Listen 443 присутствует (ports.conf).

Другие источники той же ошибки.

  • Обратный прокси проксирует на бэкенд по https://, а бэкенд слушает обычный HTTP (или наоборот).
  • Проброс порта на маршрутизаторе или в панели ведёт на 80-й порт сервера или на другой сервис.
  • Приложение (Node, Python) запущено без TLS, а перед ним нет терминатора.
  • Нестандартный порт в адресе (https://домен:8080, где 8080 отдаёт HTTP).

TLS в Apache описан в документации Apache.