UptimeChecker

Порт 80 закрыт: что делать

Обложка: Порт 80 закрыт: что делать

Что означает «порт 80 закрыт»

Порт 80 — стандартный TCP-порт протокола HTTP, за ним закреплён весь незашифрованный веб-трафик. Когда вы видите в адресной строке http://example.com, браузер по умолчанию идёт именно на 80-й порт. Поэтому формулировка «порт 80 закрыт» на практике описывает три разных ситуации, и лечатся они по-разному:

Что происходит Что на самом деле Как отличить
Никто не слушает порт 80 Веб-сервер не имеет секции listen 80 — порт формально свободен, ядро отвечает RST ss -lntp не показывает строку с :80
Слушает, но трафик режется nginx/Apache запущены, но фаервол хоста или security group облака отбрасывает пакеты Локально curl работает, снаружи — таймаут
Слушает и отвечает, но не тем Порт открыт, однако отдаёт ошибку или чужой сайт (например, дефолтный vhost) Снаружи приходит код 4xx/5xx или не тот сертификат

Путать эти случаи дорого: вы можете полдня править конфиг nginx, тогда как пакеты дропает внешний фаервол, — или наоборот, открывать порт в облаке, когда процесс вообще не запущен.

Важно и другое: «закрыт» — не всегда авария. Часть администраторов сознательно закрывают 80, оставляя только HTTPS, считая HTTP лишним. Ниже разберём, почему это решение почти всегда создаёт проблемы — даже если сайт продолжает открываться по https://.

Почему закрытый 80 ломает редирект HTTP → HTTPS

Редирект с HTTP на HTTPS — это обычный HTTP-ответ. Чтобы браузер получил 301 Moved Permanently и адрес Location: https://…, должен существовать кто-то, кто этот ответ отдаст. Отдаёт его веб-сервер по 80-му порту. Если порта нет, ответить некому.

Получается логический тупик:

  1. Пользователь открывает http://example.com (старая закладка, ссылка из письма, бэклинк, QR-код на визитке, документация интеграции).
  2. Браузер пытается установить TCP-соединение на порт 80.
  3. Соединение не устанавливается — если порт закрыт «в отказ», мгновенно приходит ERR_CONNECTION_REFUSED; если пакеты дропаются, браузер висит и через десятки секунд показывает ERR_CONNECTION_TIMED_OUT.
  4. Никакого 301 не приходит, на HTTPS браузер не переходит. Пользователь видит «Не удаётся получить доступ к сайту».

То есть закрытый 80 не «отключает лишний протокол», а отключает точку входа, через которую браузер узнавал бы, что нужно идти на HTTPS. Зашифрованный порт 443 при этом полностью работоспособен — и это сбивает с толку: сайт жив, SSL-сертификат валиден, а половина старых ссылок и часть посетителей упираются в ошибку. Получается парадоксальная ситуация: сайт доступен по HTTPS, но недоступен по HTTP, и никакого промежуточного состояния не остаётся.

Кто страдает от этого в первую очередь:

  • Ссылки в письмах и рассылках. Письма живут годами; шаблоны часто содержат http:// без редиректа на стороне отправителя.
  • Бэклинки и чужой контент. Вы не контролируете, как вас процитировали.
  • Платёжные и партнёрские сервисы. Часть интеграций проверяет http://-адрес «на живость» до отправки пользователя.
  • Внутренние сети и старые клиенты. Корпоративные прокси, тонкие клиенты, скрипты на Python/curl без явного https://.
  • Мониторинг и внешние проверки. Проверка доступности, настроенная на http://, начинает показывать падение, хотя с сайтом всё в порядке.

Проверить, как сейчас ведёт себя связка «HTTP → HTTPS», можно двумя способами: глазами через проверку редиректов и вручную из терминала — команды ниже.

Ошибка ERR_CONNECTION_REFUSED

Почему закрытый 80 ломает продление Let's Encrypt

Второе последствие — отложенное и поэтому более болезненное. Большинство сертификатов Let's Encrypt выдаются и продлеваются через ACME-проверку типа HTTP-01.

Механика HTTP-01:

  1. Клиент (certbot и его аналоги) запрашивает у центра сертификации задачу.
  2. ЦС генерирует случайный токен и сохраняет его у себя.
  3. Клиент кладёт файл с этим токеном на сервер — обычно в /.well-known/acme-challenge/<token>.
  4. ЦС снаружи обращается по адресу http://<ваш-домен>/.well-known/acme-challenge/<token> — то есть строго на порт 80.
  5. Если ЦС получила корректное содержимое файла, владение доменом подтверждено и сертификат выдаётся.

Порт 80 здесь не «рекомендация», а жёсткое требование протокола: HTTP-01 выполняется только по HTTP на порту 80. Если порт закрыт, шаг 4 не проходит, и в логе появляется характерная ошибка:

Challenge failed for domain example.com
http-01 challenge for example.com
Fetching http://example.com/.well-known/acme-challenge/xxxxxxxx: Connection refused
Some challenges have failed.

Сертификат не продлевается. Срок жизни сертификата Let's Encrypt — 90 дней, а продление запускается за 30 дней до истечения. Если порт 80 был закрыт «в рамках оптимизации», сайт продолжит работать примерно два месяца — а потом пользователи получат предупреждение браузера о недействительном сертификате. Это типичный сценарий «всё было хорошо, а сегодня внезапно упало».

Отдельно стоит проверить, не близок ли ваш сертификат к истечению прямо сейчас: проверка срока действия SSL и проверка SSL-сертификата. Развёрнутый разбор причин неудачного продления — в статье Let's Encrypt не продлился.

Что делать, если держать 80 открытым принципиально не хочется:

  • DNS-01 — подтверждение через TXT-запись в DNS. Порт 80 не нужен вообще, но требуется API-доступ к DNS-провайдеру или ручное добавление записи.
  • TLS-ALPN-01 — подтверждение на порту 443 по специальному расширению ALPN. Требует настроенного веб-сервера и не работает через reverse-proxy, который сам терминирует TLS.

Оба варианта — компромисс: они усложняют автоматизацию и требуют, чтобы её кто-то поддерживал. Универсально дешёвое решение — оставить порт 80 открытым и отдавать с него только редирект и ACME-файлы.

Валидация HTTP-01 через порт 80

Как проверить, закрыт ли порт 80: пошагово

Начинайте с внешней проверки — именно её видит ЦС и посетитель. Полезно иметь две-три разных точки обзора: локальный терминал, внешний сервис, машина в другой сети.

Шаг 1. Проверка снаружи через curl

curl -I -m 10 http://example.com

Ключ -I запрашивает только заголовки, -m 10 ограничивает ожидание 10 секундами, чтобы не ждать таймаут по умолчанию.

Три возможных сценария и что они означают:

curl: (7) Failed to connect to example.com port 80: Connection refused

Connection refused — на порт прилетел TCP-RST. Значит, до сервера достучались, но слушателя на 80 нет, либо правило фаервола намеренно отвечает отказом (REJECT, а не DROP). Ищите причину в конфиге веб-сервера.

curl: (28) Operation timed out after 10001 milliseconds with 0 bytes received

Таймаут — пакеты уходят в никуда. Так ведёт себя правило DROP в фаерволе или deny в security group облака. То есть на сервере порт может быть открыт и процесс запущен, но снаружи его не видно. Локально такая проверка обычно проходит успешно — это и есть ловушка.

HTTP/1.1 301 Moved Permanently
Location: https://example.com/
Server: nginx

Это корректный рабочий сценарий: порт 80 отвечает, редирект стоит.

Шаг 2. Проверка порта без HTTP

nc -vz example.com 80

Утилита nc (netcat) открывает TCP-соединение и сразу закрывает его — без отправки HTTP-запроса. Полезно, когда нужно понять, работает ли порт как таковой, независимо от конфигурации vhost.

Connection to example.com 80 port [tcp/http] succeeded!

Порт открыт. Если после этого curl отдаёт 404 или чужую страницу — проблема в конфигурации сайта, а не в сети.

nc: connect to example.com port 80 (tcp) failed: Connection refused

Порт закрыт — переходите к разделу «Как открыть порт 80».

Шаг 3. Проверка изнутри сервера

sudo ss -lntp | grep ':80 '

Показывает, есть ли вообще слушающий процесс. Разбираем вывод:

LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1337,fd=6))
LISTEN 0 511    [::]:80    [::]:* users:(("nginx",pid=1337,fd=7))
  • LISTEN — сокет слушает входящие соединения.
  • 0.0.0.0:80 — приём на всех IPv4-интерфейсах. Это то, что нам нужно.
  • [::]:80 — то же для IPv6. Без этой строки IPv6-клиенты и часть внешних проверок получат отказ.
  • users:(("nginx",…)) — какой процесс держит порт.

Если вместо 0.0.0.0:80 вы видите 127.0.0.1:80, сервис слушает только loopback. Снаружи порт закрыт, даже если сам процесс работает: так часто ломается конфиг за reverse-proxy или в контейнере без публикации порта. Если строк с :80 нет вообще — веб-сервер не слушает порт, правьте его конфигурацию.

Шаг 4. Проверка ACME-пути

curl -I -m 10 http://example.com/.well-known/acme-challenge/test-token

Перед реальным продлением полезно убедиться, что путь /.well-known/acme-challenge/ отвечает снаружи. Правильный результат — HTTP/1.1 404 Not Found от вашего веб-сервера: файла нет, но порт открыт, редирект его не перехватывает и запрос доходит до нужного места. Если приходит 301 на HTTPS — редирект ловит ACME раньше обработчика, и продление через HTTP-01 упадёт. Если Connection refused — порт 80 закрыт.

Диагностика закрытого порта 80

Быстрая альтернатива без терминала — проверка порта UptimeChecker: она делает ту же проверку с внешней стороны и показывает, доступен ли порт извне.

Таблица: симптом → причина → решение

Симптом Причина Решение
curl: (7) Connection refused Нет слушателя на порту 80, либо правило REJECT Добавить listen 80 в конфиг веб-сервера, перезапустить его
curl: (28) timed out Правило DROP в фаерволе или deny в security group Разрешить входящий TCP 80 на уровне хоста и облака
Локально работает, снаружи — нет Разница в фаерволе хоста и security group, слушатель только на 127.0.0.1 Проверить оба уровня, привязать сервис к 0.0.0.0/[::]
Из браузера ошибка, nc — «succeeded» Слушает дефолтный vhost или другой сервис Сверить server_name и порядок виртуальных хостов
Порт открыт, но редиректа нет Нет секции return 301, или она перекрыта другим location Добавить редирект, проверить порядок правил
Редирект есть, но продление падает ACME-путь перехватывается редиректом на HTTPS Исключить /.well-known/acme-challenge/ из редиректа
Всё работало, через два месяца «сертификат истёк» Порт 80 закрыли после последнего успешного продления Открыть 80 либо перейти на DNS-01
Ошибка появляется периодически Балансировка между серверами, где 80 открыт не везде Привести конфигурацию всех узлов к общему виду
ERR_CONNECTION_REFUSED только у части пользователей IPv6-клиенты, а [::]:80 не слушает Добавить директиву для IPv6

Как открыть порт 80

Действовать нужно последовательно: сначала процесс, потом фаервол хоста, потом облако. Открывать всё сразу — верный способ запутаться, что именно помогло.

1. Веб-сервер должен слушать порт

Для nginx добавьте серверный блок, который отвечает на 80-й порт:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
        allow all;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

Проверить конфигурацию и применить без разрыва соединений:

sudo nginx -t && sudo systemctl reload nginx

Для Apache аналог выглядит так:

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/html
    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
</VirtualHost>

Условие RewriteCond исключает ACME-путь из редиректа — это то, что спасает автоматическое продление.

2. Фаервол хоста

Debian/Ubuntu с ufw:

sudo ufw status verbose
sudo ufw allow 80/tcp

Первая команда показывает текущие правила и политику по умолчанию, вторая добавляет разрешение. Если в выводе есть правило deny 80/tcp выше разрешающего, правила применяются сверху вниз — уберите или переставьте.

iptables напрямую:

sudo iptables -L INPUT -n --line-numbers | grep -E 'Chain INPUT|dpt:80'
sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT

Первая команда показывает цепочку INPUT с номерами строк: правило, оказавшееся выше принимающего и имеющее цель DROP/REJECT, и блокирует порт. Вторая вставляет разрешение в начало цепочки. Помните, что правила iptables без iptables-persistent не переживут перезагрузку.

CentOS/RHEL/AlmaLinux с firewalld:

sudo firewall-cmd --list-all
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload

--list-all покажет, входит ли http в список разрешённых сервисов зоны; --permanent сохраняет правило после перезагрузки.

3. Security group облака

Если сервер в облаке, помимо фаервола хоста есть внешний уровень: security groups, сетевые ACL, «файрволы» облачного провайдера. Правило проверки простое — убедитесь, что во входящих (ingress) разрешён TCP-порт 80 с источника 0.0.0.0/0 и, если используете IPv6, ::/0. Exact набор интерфейсов отличается у каждого провайдера, но принцип одинаков: два независимых уровня защиты должны пропускать трафик, и блокировать может любой из них.

После правок обязательно повторите шаги проверки из предыдущего раздела снаружи: успешный локальный curl ничего не доказывает.

Особые случаи

Сайт за CDN или обратным прокси

Когда трафик терминируется на стороне CDN, порт 80 на вашем сервере вообще может быть закрыт — это допустимо, если CDN сам отвечает на 80 и делает редирект на HTTPS, а ACME-валидацию вы проходите через DNS-01 или через сам CDN. Ключевое условие: внешняя точка входа на 80 должна существовать и корректно редиректить.

HSTS и «мы всё равно переходим на HTTPS»

Заголовок Strict-Transport-Security заставляет браузер обращаться к домену сразу по HTTPS, и после первого визита HTTP-ссылки действительно будут переписаны на https://. Но HSTS не отменяет необходимость порта 80:

  • первый визит всё равно идёт по http://, пока браузер не увидел заголовок;
  • предзагрузка (preload) покрывает браузеры, но не скрипты, не curl, не серверные интеграции;
  • ACME-проверка HTTP-01 не подчиняется HSTS — сервер сертификации идёт строго на 80-й порт.

Поэтому «у нас HSTS» — не аргумент в пользу закрытия 80-го порта.

Порт 80 отвечает не тем сайтом

Иногда порт открыт, но на запрос приходит дефолтная страница веб-сервера или другой домен: server_name не покрывает нужное имя, либо блок с listen 80 default_server перехватывает запрос раньше. Проверяется командой:

curl -sI -m 10 http://example.com | grep -iE 'server|location|http/'

Если в поле Server или в содержимом виден чужой сайт — правьте порядок виртуальных хостов и наличие default_server у служебного блока.

Что держать под наблюдением

Закрытый 80-й порт — из тех сбоев, которые не дают мгновенной обратной связи. Сайт открывается по HTTPS, посетители приходят по новым ссылкам, поэтому день тишины не значит, что всё в порядке. Ошибка проявляется двумя волнами: сначала часть старых ссылок и проверок перестаёт работать, а через полтора-два месяца истекает сертификат, и проблема становится массовой.

Разумный минимум для профилактики — держать под регулярной внешней проверкой три вещи одновременно: доступность самого сайта (чекер доступности), состояние SSL-сертификата и его срок, и корректность редиректов HTTP → HTTPS. Дополнительно можно смотреть на конфигурацию соседнего порта — в статье Порт 443 закрыт разобраны похожие сценарии для HTTPS, и вместе эти две статьи закрывают весь входной периметр сайта.

Если нужен только разовый ответ на вопрос «доступен ли порт 80 извне и куда он ведёт» — это решается за пару секунд через проверку порта, без захода на сервер и без установки утилит.