Когда стоит выбрать CloudFront
https://example.com/docs/start?tag=a&tag=bhttps://www.example.com/docs/start?tag=a&tag=bViewer request возвращает редирект. Оба значения tag переносятся в Location.
Нужны аккаунт AWS, права на CloudFront, ACM, приватный S3 origin и DNS источника. Пример рассчитан на отдельный standard distribution только для редиректов. Не присоединяйте эту функцию ко всем запросам действующего сайта: она заменит его ответы перенаправлениями.
CloudFront не требует вашего VPS, но это несколько облачных ресурсов с настройками и собственным учётом использования. Выбирайте его, если такая инфраструктура уже знакома и удобна вам.
Все адреса example.com и example.org здесь демонстрационные. Замените их своими. Меняйте только исходный хост: рабочие DNS-записи назначения должны остаться на месте. Источник и назначение не должны перенаправлять друг на друга.
Для проверки нового правила можно сначала выбрать 302. После подтверждённого постоянного переезда переключите его на 301: браузер способен кешировать постоянный редирект, и откат серверной настройки не удаляет уже сохранённый ответ у посетителя.
1. Подготовьте distribution и TLS
- В ACM региона
us-east-1запросите сертификат, покрывающий исходный example.com. Подтвердите владение доменом, дождитесь Issued и сохраните DNS-записи валидации для дальнейшего продления. - Подготовьте приватный S3 bucket как origin. Оставьте Block Public Access включённым; выбирайте обычный S3 origin, не website endpoint.
- Создайте отдельный standard distribution. Для S3 настройте Origin Access Control с подписью запросов и bucket policy, разрешающую нужное чтение только сервису CloudFront с условием на ARN этого distribution. Используйте точную policy для созданного ресурса из документации/консоли, не открывайте bucket всем.
- В alternate domain names добавьте example.com и выберите его ACM-сертификат. Рабочий домен назначения www не добавляйте как источник.
- Для default behavior выберите Viewer protocol policy
HTTP and HTTPS(Allow all) и допустимые методы GET, HEAD.
Origin обязателен для distribution, хотя функция обычно вернёт редирект раньше обращения к нему. Если функцию отключить, запрос может дойти до origin, поэтому тот должен оставаться приватным.
Политика Redirect HTTP to HTTPS добавляет отдельный переход на HTTPS источника. Здесь допускаются оба исходных протокола, чтобы функция могла сразу выдать HTTPS-назначение. Сертификат www остаётся на целевом хостинге.
2. Создайте функцию runtime 2.0
В CloudFront Functions создайте функцию с JavaScript runtime 2.0 и добавьте код ниже. Он возвращает ответ, а не изменённый request.
function handler(event) {
var request = event.request;
var pairs = [];
Object.keys(request.querystring).forEach(function (name) {
var item = request.querystring[name];
var values = item.multiValue || [item];
values.forEach(function (entry) {
pairs.push(name + '=' + entry.value);
});
});
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
location: {
value: 'https://www.example.com' + request.uri +
(pairs.length ? '?' + pairs.join('&') : '')
}
}
};
}request.querystring — объект, его нельзя просто прибавить к URL: получится [object Object]. При повторах берётся multiValue, иначе — одиночное value. Код использует уже представленные компоненты события, не применяя ко всем значениям повторный encodeURIComponent.
Путь берётся из request.uri. Значения query не теряются, но исходный побайтовый порядок параметров не гарантируется: объект группирует повторяющиеся имена, а сериализация добавляет знак =. Например, параметр без значения может стать flag=. Для подписанных URL, где важна точная сериализация, этого рецепта недостаточно без отдельной проверки.
3. Проверьте событие, опубликуйте и свяжите
{
"version": "1.0",
"context": { "eventType": "viewer-request" },
"viewer": { "ip": "192.0.2.1" },
"request": {
"method": "GET",
"uri": "/files/report%20one.pdf",
"querystring": {
"tag": {
"value": "a",
"multiValue": [{ "value": "a" }, { "value": "b" }]
},
"q": { "value": "a%2Fb" }
},
"headers": { "host": { "value": "example.com" } },
"cookies": {}
}
}Ожидаемый Location: https://www.example.com/files/report%20one.pdf?tag=a&tag=b&q=a%2Fb. Для uri: "/" и пустого querystring ожидается https://www.example.com/ без вопросительного знака.
Проведите Test функции, затем Publish в live stage. Только опубликованную функцию свяжите с default behavior нужного distribution на событии Viewer request. Дождитесь применения изменения distribution перед проверкой домена. Сохранение кода в development stage само по себе не обновляет рабочую ассоциацию.
Это рецепт для веб-ссылок GET/HEAD. Он не предназначен для переноса произвольного API-трафика с телами запросов; семантика 301/302 не обещает сохранения метода.
4. Направьте источник на distribution
Для исходного поддомена создайте CNAME на назначенное CloudFront DNS-имя. Для корневого домена используйте поддерживаемый DNS-провайдером apex alias, например соответствующую alias-запись Route 53. Не добавляйте обычный apex CNAME поверх MX, TXT и остальных записей зоны.
Меняйте только источник. Не направляйте работающий www на этот redirect-only distribution. Перед переключением проверьте, что alternate domain name и сертификат уже привязаны. DNS указывает, куда придёт запрос; код 301 возвращает функция, а не запись DNS.
5. Проверьте ответ на edge
curl -sS -o /dev/null -D - 'http://example.com/'
curl -sS -o /dev/null -D - 'http://example.com/docs/start?tag=a&tag=b&utm_source=mail'
curl -sS -o /dev/null -D - 'https://example.com/'
curl -sS -o /dev/null -D - 'https://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.
curl -sS -I 'https://example.com/docs/start?tag=a&tag=b'
curl --path-as-is -sS -o /dev/null -D - 'https://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. Порядок query может отличаться; проверяйте оба значения tag, пустые параметры и отсутствие повторного кодирования %.
Локальное исполнение функции проверяет только преобразование заданного события. Оно не подтверждает обработку URI на edge, сертификат, DNS или ассоциацию. После публикации функции повторите запросы к своему домену и сравните фактический ответ.
Ошибки и безопасный откат
| Симптом | Что проверить |
|---|---|
| Отвечает S3 или возвращается AccessDenied | Проверьте live-версию, Viewer request и нужный behavior. Не делайте bucket публичным, чтобы скрыть ошибку ассоциации. |
| В Location виден [object Object] или один tag | Query нужно сериализовать, используя multiValue; не берите только первое value. |
| Появилось %252F вместо %2F | Проверьте, не закодированы ли уже представленные компоненты повторно. |
| Ошибка TLS на исходном домене | Проверьте alternate domain name, покрытие сертификатом и регион ACM us-east-1. |
| HTTP даёт дополнительный переход | Проверьте Viewer protocol policy и другие правила перенаправления. |
Для отката сохраните предыдущий код и ассоциацию до изменения. Верните прошлую рабочую версию функции или переведите DNS на готовый прежний обработчик. Простое удаление функции с пустым приватным origin не восстановит сайт: запросы могут получать 403. Удаляйте отдельные облачные ресурсы только после отвода трафика и проверки отсутствия других зависимостей.
Что остаётся обслуживать и оплачивать
Нет ОС VPS, которую нужно обновлять. Остаются distribution, функция и её публикации, origin/OAC, сертификат, DNS и контроль ошибок. У CloudFront, функций, передачи данных, S3 и DNS свои условия расчёта; итог зависит от использования и выбранного плана.
Сверьте
Не считайте этот набор обязательно дешевле или дороже годового сервиса: при существующей AWS-инфраструктуре важны её реальные условия. Выгода управляемого варианта — меньше ресурсов и этапов настройки. Этот пример не развёрнут и не проверен на вашем edge.