MHOSTMHOST

Cloudflare перед VPS

Cloudflare — это обратный прокси и CDN, который встаёт между посетителями и вашим VPS: запросы сначала попадают в сеть Cloudflare, и только она обращается к вашему серверу (origin). В этой статье вы добавите домен в Cloudflare, разберётесь с proxied- и DNS only-записями, включите режим SSL/TLS Full (strict) с настоящим сертификатом, настроите базовое кэширование, научитесь получать настоящий IP посетителя вместо IP Cloudflare и узнаете, что учитывать при выпуске сертификата certbot, когда домен уже проксируется. На выходе — два практических эффекта: статика отдаётся быстрее за счёт CDN, а реальный IP VPS скрыт от посетителей и большинства сканеров, что снимает часть базовых DDoS-атак ещё на edge Cloudflare.

Что понадобится

  • Домен, зарегистрированный у любого регистратора, и доступ к его панели управления — там, где меняются NS-серверы.
  • Работающий сайт на VPS mHost, уже доступный по HTTP(S) — например, настроенный по статьям «Nginx: установка и первый сайт» или «Apache: установка и виртуальные хосты». Если домен ещё не привязан к серверу напрямую — см. «Как привязать домен к серверу»; если нужно освежить, что такое A/AAAA/CNAME и NS-записи, — «DNS-записи: A, AAAA, CNAME и другие».
  • Желательно — уже выпущенный сертификат Let's Encrypt, см. «Бесплатный SSL с Let's Encrypt (certbot)». Дальше в статье будет понятно, как он взаимодействует с Cloudflare, но начать можно и без него.
  • Бесплатный аккаунт Cloudflare — всё, что описано в этой статье (проксирование, SSL/TLS, базовое кэширование, скрытие IP), доступно на тарифе Free.
  • Доступ по SSH под root или пользователем с sudo — для настройки realip-модуля и, по желанию, файрвола.
Совет: даже если домен уже привязан к VPS напрямую через панель регистратора (по статье про подключение домена), это не пропадёт зря — подключение к Cloudflare просто переносит управление DNS-зоной на серверы имён Cloudflare, и ту же A-запись вы заведёте заново, но уже в панели Cloudflare.

Шаг 1. Добавьте домен в Cloudflare и смените NS-серверы

  1. Зарегистрируйтесь или войдите на dash.cloudflare.com.
  2. Нажмите Add a domain (в некоторых версиях панели — Onboard a domain) и введите домен без www и протокола, например example.com.
  3. Выберите тариф — для всего, что описано в этой статье, достаточно Free.
  4. Cloudflare сам просканирует текущие DNS-записи домена и предложит их импортировать. Внимательно проверьте список — особенно почтовые записи (MX, TXT с SPF/DKIM/DMARC): если сканер что-то пропустит или исказит, после переключения NS почта перестанет работать. Недостающие записи добавьте вручную до переключения NS.
  5. Cloudflare выдаст два NS-адреса вида xxxx.ns.cloudflare.com — зайдите в панель регистратора домена и замените там текущие NS-записи на эти два.
Важно: если у домена включён DNSSEC у прежнего регистратора или DNS-провайдера, отключите его до смены NS — иначе на время рассинхронизации домен может стать недоступен целиком. Включить DNSSEC заново, уже средствами Cloudflare, можно после того, как домен активируется.

Смена NS вступает в силу не сразу — обычно от нескольких минут до 24 часов, пока обновление разойдётся по резолверам. Проверить, какие NS-серверы сейчас видны снаружи, можно командой:

bash
dig NS example.com +short

Когда в ответе появятся два адреса вида *.ns.cloudflare.com, а в панели Cloudflare статус домена сменится на Active, — можно переходить к настройке записей.

Готово: статус домена в панели Cloudflare — Active.

Шаг 2. Proxied и DNS only: оранжевое и серое облако

В разделе DNS → Records у каждой записи есть переключатель-«облако»:

  • Proxied (оранжевое облако) — запрос идёт через сеть Cloudflare: посетителям виден IP-адрес Cloudflare, а не настоящий IP VPS; на такую запись действуют кэширование, WAF, базовая защита от DDoS и остальные функции Cloudflare.
  • DNS only (серое облако) — Cloudflare отдаёт настоящий IP из записи как обычный DNS-резолвер, без прокси; функции Cloudflare (кэш, WAF, скрытие IP) на такую запись не действуют.

Проксировать можно только записи A, AAAA и CNAME — у остальных типов (MX, TXT, NS, SRV и т. д.) переключателя вообще нет, они всегда DNS only — это ограничение самого Cloudflare.

  • Основную запись сайта (example.com, www.example.com) включите Proxied — чтобы получить CDN и скрытие IP.
  • Почтовые записи (MX) и записи для почтовых сервисов (SPF/DKIM/DMARC-подобные TXT, CNAME для autodiscover/autoconfig) оставьте DNS only: Cloudflare не проксирует почтовый трафик, а проксированный CNAME может сломать проверку у почтового провайдера.
  • Служебные поддомены, к которым вы обращаетесь напрямую (например, отдельный не-HTTP сервис), тоже логично оставить DNS only.
Совет: после включения Proxied обычные dig/nslookup по домену больше не покажут реальный IP вашего VPS — полезный побочный эффект, но не замена настройке безопасности самого сервера. Файрвол и SSH всё равно стоит настроить отдельно — см. «Файрвол ufw» и «Чек-лист безопасности сервера».

Шаг 3. Режим SSL/TLS: включите Full (strict)

В разделе SSL/TLS → Overview выберите режим шифрования между Cloudflare и вашим origin-сервером:

  • Off — вообще без шифрования, весь трафик, включая Cloudflare → origin, идёт открытым текстом. Не используйте.
  • Flexible — посетитель ↔ Cloudflare может быть по HTTPS, но Cloudflare ↔ origin всегда идёт по обычному HTTP.
  • Full — Cloudflare обращается к origin по тому же протоколу, что и посетитель, включая HTTPS, но не проверяет сертификат origin — подойдёт даже самоподписанный.
  • Full (strict) — то же самое, но Cloudflare ещё и проверяет, что сертификат origin настоящий: выпущен публичным CA (например, Let's Encrypt) или Cloudflare Origin CA.

Cloudflare рекомендует режим Full или Full (strict), чтобы исключить незашифрованные и неаутентифицированные подключения к origin. Если на VPS уже настроен Let's Encrypt (см. «Бесплатный SSL с Let's Encrypt (certbot)»), сразу ставьте Full (strict) — это самый защищённый вариант и он ничего не стоит сверх обычного сертификата.

Важно: если на origin уже настроен редирект с HTTP на HTTPS (его добавляет certbot с плагином --nginx/--apache, см. статью про Let's Encrypt), не выбирайте Flexible. В этом режиме Cloudflare ходит на origin только по HTTP, origin в ответ редиректит обратно на HTTPS, Cloudflare снова подключается по HTTP — получается бесконечный редирект и ошибка вроде ERR_TOO_MANY_REDIRECTS в браузере. Решение — переключить режим на Full или Full (strict), а не убирать редирект на origin.

Если на origin ещё вообще нет сертификата, поставьте пока Full (strict проверять пока нечего) или временно переключите запись в DNS only (шаг 2) — и выпустите сертификат по шагу 4.

Шаг 4. Сертификат на origin и certbot за проксированием

Если Let's Encrypt на VPS был настроен ещё до подключения Cloudflare, на этом шаге ничего дополнительно делать не нужно — переходите к шагу 5. Ниже — что учитывать, если сертификат ещё предстоит получить или обновить для нового поддомена уже после того, как запись стала Proxied.

Certbot с HTTP-01-валидацией (--nginx/--apache/--webroot) обычно нормально работает и через проксированный домен: запрос Let's Encrypt на http://example.com/.well-known/acme-challenge/... попадает на edge Cloudflare, а тот перенаправляет его на origin по 80-му порту. Но два стандартных механизма Cloudflare могут это сломать:

  • Always Use HTTPS (SSL/TLS → Edge Certificates) — редиректит на HTTPS прямо на edge, до того как запрос дойдёт до origin. Если у origin ещё нет валидного сертификата, получить новый через HTTP-01 в этот момент не выйдет.
  • Cache Rule с «Cache Everything» на весь сайт — может закэшировать старый ответ по пути /.well-known/acme-challenge/..., из-за чего повторная валидация при продлении сертификата получит устаревший токен.

Два рабочих варианта:

  • Проще всего — на время выпуска или продления сертификата переключить запись обратно в DNS only (серое облако, шаг 2), выполнить certbot как обычно (см. статью «Бесплатный SSL с Let's Encrypt (certbot)») и включить Proxied обратно, когда сертификат готов.
  • Надёжнее для автопродления — переключить certbot на DNS-01-валидацию через официальный плагин certbot-dns-cloudflare. Он подтверждает владение доменом через TXT-запись, создаваемую по API Cloudflare, и не зависит от того, проксируется запись или нет.

Чтобы настроить DNS-01, сначала создайте API-токен в Cloudflare: My Profile → API Tokens → Create Token, шаблон Edit zone DNS, в Zone Resources укажите конкретный домен, а не «все зоны». Скопируйте токен — панель показывает его только один раз.

Дальше на VPS (certbot уже должен быть установлен через snap, как в статье про Let's Encrypt):

bash
sudo snap install certbot-dns-cloudflare
sudo snap set certbot trust-plugin-with-root=ok
sudo snap connect certbot:plugin certbot-dns-cloudflare

Проверьте, что плагин подключился:

bash
certbot plugins

В списке должен появиться dns-cloudflare. Сохраните токен в файл, доступный на чтение только root:

bash
sudo mkdir -p /root/.secrets/certbot
sudo tee /root/.secrets/certbot/cloudflare.ini > /dev/null <<'EOF'
dns_cloudflare_api_token = <CLOUDFLARE_API_TOKEN>
EOF
sudo chmod 600 /root/.secrets/certbot/cloudflare.ini

Замените <CLOUDFLARE_API_TOKEN> на настоящий токен из панели Cloudflare, затем запросите сертификат:

bash
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/certbot/cloudflare.ini \
  --dns-cloudflare-propagation-seconds 60 \
  -d example.com -d www.example.com
Совет: --dns-cloudflare-propagation-seconds 60 — запас времени на распространение TXT-записи по DNS Cloudflare перед проверкой; значение по умолчанию (10 секунд) обычно тоже работает, но 60 надёжнее. Если для этого же домена уже был сертификат, выпущенный через --nginx/--apache, certbot переиспользует тот же сертификат (тот же путь /etc/letsencrypt/live/example.com/) и обновит /etc/letsencrypt/renewal/example.com.conf — конфигурацию nginx/Apache менять не придётся, а systemd-таймер автопродления (см. статью про Let's Encrypt) дальше будет использовать уже DNS-01.

Режим certonly только получает сертификат, но не правит конфигурацию веб-сервера — если вы ещё не настраивали HTTPS в nginx/Apache вручную, сделайте это по статье «Бесплатный SSL с Let's Encrypt (certbot)», указав пути /etc/letsencrypt/live/example.com/fullchain.pem и privkey.pem.

Совет: вместо публичного сертификата для связки Cloudflare ↔ origin можно использовать Cloudflare Origin CA (SSL/TLS → Origin Server → Create Certificate) — бесплатный, выпускается на срок до 15 лет и не требует certbot вообще. Ограничение: браузеры ему не доверяют, он действителен только для проверки Cloudflare в режиме Full (strict) — если кто-то обратится к origin напрямую, в обход Cloudflare, увидит ошибку сертификата. Подходит, только если весь трафик идёт исключительно через проксированные записи.

Проверьте результат после любого из двух вариантов:

bash
curl -I https://example.com/
Готово: ответ HTTP/2 200 без предупреждений о сертификате — значит, режим Full (strict) из шага 3 работает.

Шаг 5. Базовое кэширование

По умолчанию, без какой-либо настройки, Cloudflare уже кэширует статику по расширению файла — изображения (jpg, png, gif, webp), видео (mp4, webm), CSS, JS, шрифты (woff, woff2), архивы. HTML и JSON по умолчанию не кэшируются — Cloudflare ориентируется на заголовки Cache-Control/Expires origin, а если их нет, применяет TTL по умолчанию (около 2 часов для успешных ответов).

Чтобы кэшировать больше — например, HTML статичных страниц или гарантировать TTL независимо от заголовков origin, — используйте Cache Rules (Rules → Overview, в некоторых версиях панели — Caching → Cache Rules; это текущая замена устаревших Page Rules):

  • Задайте условие, например «URI Path starts with /blog/» или «Hostname equals example.com».
  • В действии выберите Cache eligibility: Eligible for cache и задайте Edge TTL — например, 2 часа.
  • Для полностью статичного сайта без личных кабинетов и cookies можно применить Cache Everything на весь домен — но не делайте так для сайтов с авторизацией, корзиной или другим персональным контентом: можно случайно отдать одному посетителю закэшированную страницу другого.

Дополнительно пригодятся:

  • Browser Cache TTL (Caching → Configuration) — отдельная настройка: сколько ресурс хранится в кэше браузера посетителя, не путать с кэшем на edge Cloudflare.
  • Development Mode (там же, Caching → Configuration) — временно, на 3 часа, отключает кэш Cloudflare; удобно при проверке изменений на origin, чтобы не ждать протухания кэша.
  • Purge Cache — после деплоя обновлений на origin стоит очистить кэш («Purge Everything» или по конкретным URL), иначе часть посетителей ещё какое-то время будет видеть старую версию.
Совет: если создавали общее правило Cache Everything, не забудьте про шаг 4 — путь /.well-known/acme-challenge/... не должен попадать под кэширование, иначе продление сертификата через certbot может сломаться из-за устаревшего ответа.

Шаг 6. Настоящий IP посетителя: заголовок CF-Connecting-IP

Как только запись стала Proxied, в логах nginx/Apache и во всех приложениях на VPS вместо реального IP посетителя будет виден IP edge-сервера Cloudflare — это ломает логи, geo-IP, rate limiting и такие инструменты, как fail2ban, которые опираются на IP-адрес.

Cloudflare добавляет к каждому проксированному запросу заголовок `CF-Connecting-IP` — он содержит ровно один настоящий IP-адрес посетителя. Есть также True-Client-IP (то же самое, но доступен только на тарифе Enterprise) и стандартный X-Forwarded-For (список через запятую, менее удобен для этой задачи) — для восстановления реального IP используйте именно CF-Connecting-IP.

Nginx: модуль ngx_http_realip_module

Модуль ngx_http_realip_module уже встроен в стандартный пакет nginx из репозиториев Ubuntu/Debian/AlmaLinux/Rocky — ничего доустанавливать не нужно (проверить можно командой nginx -V 2>&1 | grep -o http_realip_module). Сгенерируйте конфиг с актуальными диапазонами IP Cloudflare — официальный список публикуется на cloudflare.com:

bash
{
  for ip in $(curl -s https://www.cloudflare.com/ips-v4); do
    echo "set_real_ip_from $ip;"
  done
  for ip in $(curl -s https://www.cloudflare.com/ips-v6); do
    echo "set_real_ip_from $ip;"
  done
  echo "real_ip_header CF-Connecting-IP;"
} | sudo tee /etc/nginx/conf.d/cloudflare-realip.conf > /dev/null

Проверьте и примените конфигурацию:

bash
sudo nginx -t && sudo systemctl reload nginx

После этого $remote_addr в конфигурации nginx — в том числе в стандартном log_format — автоматически станет настоящим IP посетителя вместо IP Cloudflare, отдельно менять log_format не требуется.

Apache: модуль mod_remoteip

На Ubuntu/Debian модуль уже входит в пакет apache2, его достаточно включить:

bash
{
  echo "RemoteIPHeader CF-Connecting-IP"
  for ip in $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6); do
    echo "RemoteIPTrustedProxy $ip"
  done
} | sudo tee /etc/apache2/conf-available/remoteip.conf > /dev/null
bash
sudo a2enmod remoteip
sudo a2enconf remoteip
sudo apache2ctl configtest
sudo systemctl restart apache2

По умолчанию Apache логирует IP через %h. Чтобы в логах тоже был настоящий IP, замените в LogFormat (файл /etc/apache2/apache2.conf) %h на %a.

Совет: на AlmaLinux/Rocky тот же модуль обычно уже входит в пакет httpd, но может быть не загружен — проверьте командой httpd -M | grep remoteip. Если модуля в списке нет, добавьте LoadModule remoteip_module modules/mod_remoteip.so файлом в /etc/httpd/conf.modules.d/, а те же директивы RemoteIPHeader/RemoteIPTrustedProxy положите в /etc/httpd/conf.d/remoteip.conf — как и остальные модули на этом семействе дистрибутивов, см. статью «Apache: установка и виртуальные хосты».
Важно: диапазоны IP Cloudflare изредка меняются — актуальный список всегда на https://www.cloudflare.com/ips-v4 и https://www.cloudflare.com/ips-v6. Команды генерации конфига стоит перезапускать время от времени, например по расписанию cron (см. статью «Cron и автоматизация бэкапов» про синтаксис cron), чтобы список не устарел.
Важно: доверяйте заголовку CF-Connecting-IP только тем запросам, которые действительно пришли через Cloudflare. Если origin по-прежнему доступен напрямую по своему настоящему IP в обход Cloudflare, кто угодно может подставить в этот заголовок произвольное значение. Следующий шаг закрывает эту дыру файрволом.

Шаг 7. Ограничьте 80/443 только сетями Cloudflare (опционально)

Proxied-запись прячет IP VPS от обычного DNS-лукапа, но если реальный IP всё же где-то «утёк» (старый DNS-кэш, случайно оставленная DNS only запись, сканирование диапазонов хостера), защита Cloudflare — включая базовое сглаживание DDoS — просто не сработает: атакующий обратится к серверу напрямую, в обход Cloudflare. Чтобы закрыть эту дыру, разрешите на 80/443 только сети Cloudflare.

Ниже — вариант для ufw (Ubuntu/Debian). Если ufw ещё не настроен — сначала пройдите статью «Файрвол ufw»; как и она сама, этот шаг не рассматривает firewalld на AlmaLinux/Rocky — там тот же результат достигается через rich rules (firewall-cmd --add-rich-rule), но это отдельная тема со своим синтаксисом. Дальше добавьте разрешающие правила для диапазонов Cloudflare:

bash
for ip in $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6); do
  sudo ufw allow from "$ip" to any port 80,443 proto tcp
done

Теперь удалите старые широкие правила, открывавшие 80/443 для всех (80/tcp, 443/tcp, Nginx Full и подобные) — посмотрите их номера и удаляйте по одному, начиная с большего номера:

bash
sudo ufw status numbered
sudo ufw delete <RULE_NUMBER>
Важно: прежде чем удалять широкие правила для 80/443, убедитесь, что правило для SSH (обычно 22/tcp) точно есть и не тронуто — иначе рискуете потерять доступ не к сайту, а сразу к самому серверу. Подробно о работе с номерами правил — в статье «Файрвол ufw».

Этот шаг стоит повторять вместе с обновлением конфигурации realip из шага 6 — используются одни и те же диапазоны.

Что дальше

Домен работает через Cloudflare: CDN ускоряет отдачу статики по всему миру, IP VPS скрыт от обычных посетителей и сканеров, а базовые волюметрические DDoS-атаки гасятся на edge Cloudflare, не доходя до сервера. Из смежных тем: «DNS-записи: A, AAAA, CNAME и другие» и «Как привязать домен к серверу» — если нужно освежить, как работают записи без Cloudflare; «Файрвол ufw» и «Чек-лист безопасности сервера» — чтобы защитить сам VPS, а не только спрятать его за CDN.