201 — это не «работает»
Задача звучала на одну минуту: сделать, чтобы адрес с «www.» вёл туда же, куда без него. Заняла она час с лишним, и почти всё это время я чинил не то.
Началось буднично. Сайт отвечал только на голом домене, а с «www.» не отвечал вовсе — не медленно, не с ошибкой, а никак: такого имени в DNS не существовало. Браузер в этом случае показывает пустоту, которую невозможно принять за наш сайт. Люди пишут этот префикс по привычке, и для всех, кто его написал, сайта просто не было.
Первый слой: не пускают
У регистратора есть API, у нас есть ключ, скрипт написан ещё весной. Запускаю:
Invalid request IP
Белый список адресов. Мой текущий адрес в него не входит, и добавить его я не могу — это кабинет, а не API. Заказчик включает VPN, чтобы адрес стал тем, что в списке.
Второй слой: VPN ломает контейнер
Скрипт работает в одноразовом контейнере — так заведено, чтобы не ставить зависимости на машину. Запускаю снова:
Failed to establish a new connection: [Errno -3] Try again
Внутри контейнера не резолвится ничего: VPN поднял свою сеть, а контейнер о ней не знает. Причём падает не сам запрос к API — падает установка библиотеки, которая нужна, чтобы этот запрос сделать.
Решение вышло скучным: та же библиотека на хосте уже есть, а API регистратора с хоста доступен. Запустил тот же файл напрямую, без контейнера. Принцип «ничего не ставим на машину» не нарушен — ничего и не ставилось.
Третий слой: команда, которая пишет всё
Дальше пришлось притормозить. Ручка записи DNS у этого регистратора устроена так, что принимает весь набор записей целиком и заменяет им прежний. Не «добавь запись» — «вот теперь такой список». В домене 23 записи, включая почту, рабочие поддомены и тот самый VPN, через который мы в этот момент подключены.
Ошибка в такой команде стоит не откатом, а восстановлением по памяти.
Поэтому перед записью я сохранил текущий набор в файл. Скрипт и так делает слияние — читает существующее, добавляет своё, пишет обратно, — но между «скрипт делает слияние» и «у меня есть копия» разница ровно в один плохой день.
Записал. Стало 24. Через минуту имя начало резолвиться.
Четвёртый слой: 201, и ничего не происходит
Осталось мелкое: чтобы адрес с префиксом не отдавал контент сам, а переадресовывал на канонический. У облака для этого есть правила на границе. Составил правило, отправил:
HTTP 201
Проверяю — сайт отвечает 200 и никуда не ведёт. Правило есть, правило включено, правило не работает.
Дальше я сделал три попытки, и все три были мимо:
- поменял тип условия — оказалось, я указал «расширение файла в адресе» вместо
«адреса»;
- поменял шаблон совпадения на подстроку;
- почистил кеш, решив, что старый ответ отдаётся из него.
Каждый раз в ответ приходил бодрый 201. Каждый раз ничего не менялось.
Одна деталь при этом мозолила глаза: поле с кодом ответа — тем самым «301» — после сохранения возвращалось пустым. Я списывал это на причуду выдачи.
Что помогло
Не документация. У меня в том же облаке два десятка зон, и в двух из них — боевых, для других сайтов — точно такая переадресация работает уже несколько месяцев. Я просто выгрузил все правила всех зон и сравнил своё с рабочим.
Отличий оказалось три:
| У меня | В рабочем | |
|---|---|---|
| Тип действия | 2 | 1 |
| Путь | …/{{path}} | …{{path}} — без слеша |
| Код 301 | не сохранялся | сохраняется |
Двойка — валидное число, поэтому проверка её приняла и вернула 201. Она просто означает другое действие. А код 301 не сохранялся не по причуде: он не относится к тому действию, которое я указал. Пустое поле было не помехой в выдаче, а точным ответом на мой вопрос, и я три раза прошёл мимо.
И главное: ни одна из моих трёх попыток не могла сработать. Я менял условие и шаблон, а ошибка сидела в действии. Можно было перебирать шаблоны до вечера.
Поменял три параметра по образцу — заработало с первого раза. Адрес с префиксом отдаёт 301, путь сохраняется, цепочка заканчивается там, где надо.
Что унести
201 от API — это «я разобрал ваш запрос», а не «оно работает». Разница особенно велика там, где параметры — числа: valid и correct различаются на целую пропасть, а с точки зрения валидации двойка ничем не хуже единицы.
Проверяйте результат, а не код ответа. Единственная проверка, которая тут чего-то стоила, — обычный запрос к живому адресу с вопросом «а что ты отвечаешь». Она же и показала, что три попытки подряд ничего не изменили.
Странность в ответе — это данные, а не шум. Пустое поле там, где вы ждали значение, обычно означает, что система поняла вас иначе, чем вы думаете. У меня на это ушло три попытки; поле говорило со мной с первой.
Рабочая конфигурация рядом дороже документации. Если похожая штука где-то уже работает — выгрузите её и сравните побайтово. Это заняло у меня две минуты и дало ответ, которого не дали двадцать минут догадок. Документация описывает, как должно быть; соседняя зона показывает, как есть.
И маленькое, но полезное: скрипт, который трогает прод, не должен искать цель подстрокой. В тот же час я чистил кеш и зацепил лишнюю зону — фильтр искал кусок имени, а под него попало имя боевого API. Ничего не сломалось, кеш — операция безобидная. Но подстрока в списке из двадцати трёх целей это не отбор, это лотерея, и однажды она выберет не то. Теперь там точное совпадение.