Блог

HTTPS-редирект через Yandex Cloud Functions и API Gateway

Функция формирует Location, API Gateway принимает запрос по вашему домену, Certificate Manager даёт сертификат источника. Этот рецепт охватывает HTTPS; обработку HTTP на порту 80 он не устанавливает.

Сначала выберите исходное имя

Пример маршрута
Исходный URLhttps://go.example.com/docs/start?tag=a&tag=b
HTTP301
Назначениеhttps://www.example.com/docs/start?tag=a&tag=b

Собственный HTTPS-хост → API Gateway → функция → фиксированное назначение.

Нужны аккаунт и каталог Yandex Cloud с оплатой, права на создание функции, шлюза, сервисного аккаунта и сертификата, а также управление DNS источника. Пример использует отдельный поддомен go.example.com, не занятый живым сайтом.

При стороннем DNS официальная схема подключения поддерживает поддомен через CNAME. Для корневого example.com текущая инструкция требует делегирования в Yandex Cloud DNS и записи ANAME. Прежде чем выбирать apex, оцените перенос всей DNS-зоны: заранее сохраните MX, TXT и записи работающего назначения. Один CNAME не решает этот вопрос.

Все адреса example.com и example.org здесь демонстрационные. Замените их своими. Меняйте только исходный хост: рабочие DNS-записи назначения должны остаться на месте. Источник и назначение не должны перенаправлять друг на друга.

Для проверки нового правила можно сначала выбрать 302. После подтверждённого постоянного переезда переключите его на 301: браузер способен кешировать постоянный редирект, и откат серверной настройки не удаляет уже сохранённый ответ у посетителя.

Если нужны и HTTP-, и HTTPS-ссылки, для HTTP потребуется отдельно проверенная точка входа. В этом рецепте поведение порта 80 не подтверждается. Рассмотрите Caddy или два правила Cloudflare, где обе схемы заданы явно. Ресурс Yandex CDN здесь не нужен.

1. Создайте функцию Node.js

В Cloud Functions создайте функцию и её версию с поддерживаемой средой Node.js. Добавьте файл index.js, задайте точку входа index.handler. Зависимости для этого кода не требуются.

index.js · ответ функции
exports.handler = async (event) => {
    const path = event.rawPath || '/';
    const query = event.rawQueryString || '';
    return {
        statusCode: 301,
        headers: {
            Location: 'https://www.example.com' + path + (query ? '?' + query : '')
        },
        body: '',
        isBase64Encoded: false
    };
};

Назначение фиксировано. Функция использует rawPath и rawQueryString формата события 2.0, не пересобирая query из словаря с потерей повторов. Пустой путь получает /; при пустой query знак вопроса не добавляется. Код не проксирует тело запроса и не исполняет переход сам — это сделает клиент.

Сохраните идентификатор функции. Не открывайте её вызов всем пользователям отдельно: шлюз будет обращаться к ней через сервисный аккаунт.

2. Подключите два маршрута API Gateway

Создайте сервисный аккаунт шлюза и дайте ему роль functions.functionInvoker на эту функцию. Для вызова не нужна широкая роль editor на весь каталог. В API Gateway создайте шлюз со следующим полным документом OpenAPI; в обоих местах замените идентификаторы функции и аккаунта.

OpenAPI · корень и greedy-путь
openapi: 3.0.0
info:
  title: Domain redirect
  version: 1.0.0
paths:
  /:
    x-yc-apigateway-any-method:
      responses:
        '301':
          description: Permanent redirect
          headers:
            Location:
              schema:
                type: string
      x-yc-apigateway-integration:
        type: cloud_functions
        function_id: REPLACE_WITH_FUNCTION_ID
        service_account_id: REPLACE_WITH_SERVICE_ACCOUNT_ID
        payload_format_version: '2.0'
  /{path+}:
    x-yc-apigateway-any-method:
      parameters:
        - name: path
          in: path
          required: true
          schema:
            type: string
      responses:
        '301':
          description: Permanent redirect
          headers:
            Location:
              schema:
                type: string
      x-yc-apigateway-integration:
        type: cloud_functions
        function_id: REPLACE_WITH_FUNCTION_ID
        service_account_id: REPLACE_WITH_SERVICE_ACCOUNT_ID
        payload_format_version: '2.0'

Маршрут / обрабатывает корень отдельно, /{path+} — вложенные пути. Нельзя рассчитывать, что greedy-вариант сам закроет корень. Расширение x-yc-apigateway-any-method направляет методы в одну интеграцию; ниже проверяются веб-запросы GET и HEAD.

Этот пример для ссылок, не для повторной отправки произвольных API-тел. При 301/302 клиент может заменить POST на GET. Для API, где нужен исходный метод, сначала разберите соответствующую семантику, а не применяйте это правило вслепую.

3. Привяжите сертификат и DNS

  1. В Certificate Manager запросите управляемый сертификат для go.example.com. Выполните выбранную проверку владения доменом и дождитесь статуса Issued.
  2. В разделе Domains шлюза добавьте полное имя источника и выберите этот сертификат.
  3. Возьмите служебное имя созданного шлюза из его карточки и задайте для go требуемую CNAME-запись. Не угадывайте имя endpoint по идентификатору функции.
  4. Дождитесь применения DNS и проверьте HTTPS на собственном имени. Сохраните записи, необходимые для продления управляемого сертификата.

Для apex используйте предусмотренные провайдером Cloud DNS и ANAME после подготовки переноса зоны. Сертификат функции на её служебном URL не покрывает автоматически ваш домен. Сертификат www остаётся отдельной задачей целевого хостинга.

4. Проверьте HTTPS и данные события

Проверка корня и вложенного URL · GET
curl -sS -o /dev/null -D - 'https://go.example.com/'
curl -sS -o /dev/null -D - 'https://go.example.com/docs/start?tag=a&tag=b&utm_source=mail'

Ожидайте код 301. Для корня заголовок Location должен быть https://www.example.com/, для вложенного адреса — https://www.example.com/docs/start?tag=a&tag=b&utm_source=mail. Версия HTTP и регистр имени заголовка в выводе могут отличаться. Если временно выбрали 302, ожидаемый код — 302.

Дополнительно: HEAD и закодированные символы
curl -sS -I 'https://go.example.com/docs/start?tag=a&tag=b'
curl --path-as-is -sS -o /dev/null -D - 'https://go.example.com/files/report%20one.pdf?tag=a&tag=b&q=a%2Fb'

Не добавляйте -L к первой проверке: важно увидеть ответ источника, а не скрыть его за переходом. Затем отдельно откройте адрес назначения и убедитесь, что там есть нужная страница. DNS не создаёт HTTP-ответ, а сертификат назначения не заменяет сертификат источника.

Для encoded-примера ожидается https://www.example.com/files/report%20one.pdf?tag=a&tag=b&q=a%2Fb. Код сохраняет строки, которые пришли в событии. Это не доказательство побайтового сохранения на живом шлюзе: после развёртывания проверьте %20, %2F, кириллицу и повторяющиеся query на фактическом API Gateway.

Событие для локальной проверки обработчика
{
  "version": "2.0",
  "rawPath": "/files/report%20one.pdf",
  "rawQueryString": "tag=a&tag=b&q=a%2Fb"
}

При таком входе функция должна вернуть statusCode 301 и указанный Location. Вызов функции отдельно проверяет только обработчик; запрос к собственному HTTPS-домену нужен для проверки DNS, TLS, маршрутизации и прав вместе. Успех HTTPS не подтверждает работу HTTP.

Диагностика и откат

Симптом и причина
СимптомЧто проверить
На вложенном пути ответ есть, на / — 404Убедитесь, что в опубликованной спецификации есть отдельный маршрут корня.
Ошибка вызова функцииСверьте function_id, service_account_id, права functionInvoker именно на функцию и наличие рабочей версии.
Путь всегда / или пропали повторыПроверьте payload_format_version 2.0 у обеих интеграций и использование rawPath/rawQueryString.
Служебный адрес работает, свой домен — нетПроверьте CNAME/ANAME, привязку домена, покрытие сертификатом и Issued.
HTTPS работает, HTTP — нетЭтот рецепт не настраивает HTTP-вход; нужна отдельная проверенная схема для порта 80.

Перед изменением сохраните предыдущую версию функции, спецификацию и DNS. Для отката восстановите версию/спецификацию или переведите источник на заранее готовый прежний обработчик. Не удаляйте шлюз и сертификат, пока DNS ещё ведёт на них; кешированный 301 также может пережить откат.

Ресурсы и расходы

ОС сервера обновлять не нужно, но остаются версия функции, спецификация, права сервисного аккаунта, доменная привязка, сертификат и DNS. Следите за ошибками вызовов и состоянием продления сертификата. Потребление Cloud Functions, API Gateway и Cloud DNS при его использовании учитывается по отдельным правилам.

Универсальной фиксированной суммы для этой комбинации нет. Оцените свой трафик по официальным условиям:

Этот пример не развёрнут в вашем облаке. Провайдерские настройки и результат на вашем домене нужно проверить после применения.

Документация

Нужен редирект, а не ещё одна настройка?

Связка функции и шлюза полезна, если вы уже управляете ресурсами Yandex Cloud. Redirect301 позволяет передать настройку редиректов и HTTPS без отдельной функции, спецификации и доменной привязки шлюза.

Redirect301 — 500 ₽ за домен в год. Мы берём на себя правила перенаправления, HTTPS и продление сертификата исходного домена. Подключение через DNS охватывает весь исходный хост; для отдельных страниц живого сайта нужна отдельная интеграция.

Что входит в 500 ₽

Redirect301

Узнать о запуске Redirect301

Оставьте email, чтобы получить уведомление об открытии доступа.