21 сент. 2025 г.·8 мин. чтения

Проверка email не работает: исправьте ссылки, токены и логику повторной отправки

Проверка email не работает? Узнайте, почему ломаются ссылки, токены и логика повторной отправки в приложениях, сгенерированных ИИ, и простые решения для надёжности и защиты от злоупотреблений.

Проверка email не работает: исправьте ссылки, токены и логику повторной отправки

Как проявляется проблема, когда проверка не работает

Когда проверка email не работает при регистрации, это редко случается как что-то очевидное и чистое. Пользователи просто застревают. Они зарегистрировались, сделали то, что просили, а приложение всё ещё говорит, что они не подтверждены.

Чаще всего — тишина: письмо не приходит. Люди смотрят в спам, ждут несколько минут, нажимают «Отправить повторно», а потом сдаются. Кому‑то письмо всё же приходит, но кнопка ведёт на пустую страницу, страшную ошибку или перенаправляет обратно на экран входа без объяснения.

Худшая версия — бесконечный цикл:

“Пожалуйста, подтвердите email” -> “Отправить повторно” -> “Письмо отправлено” -> клик -> всё ещё “Пожалуйста, подтвердите email.”

Вот типичные паттерны, которые пользователи описывают (часто со скриншотами и раздражением):

  • Письмо подтверждения не приходит (или приходит через часы)
  • Ссылка открывается, но показывает «Invalid token» или «Verification link expired»
  • Ссылка работает один раз, а потом уже никогда, даже при новой регистрации
  • После подтверждения приложение всё равно считает пользователя неподтверждённым
  • Кажется, что «Отправить повторно» работает, но действует либо только самое новое, либо только самое старое письмо (не оба)

Это не просто несколько заблокированных регистраций. Каждый сломанный поток подтверждения создаёт тикеты в поддержку, возвраты денег и плохие отзывы. Это также риск: если кнопку повторной отправки можно «долбить», ваша система может отправить тысячи писем, что вредит доставляемости и выглядит как спам.

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

Цель проста: подтверждение должно быть надёжным для реальных пользователей (включая тех, кто кликает ссылку с другого устройства), понятным при ошибке и достаточно строгим, чтобы этим нельзя было злоупотреблять. Если вы унаследовали прототип, собранный ИИ-инструментами вроде Lovable, Bolt, v0, Cursor или Replit, такие проблемы — обычное дело и обычно лечатся прицельным аудитом и несколькими осторожными правками.

Типичные симптомы — как их быстро распознать

Некоторые баги верификации громкие (очевидная ошибка). Другие тихие и проявляются только как «люди не могут зарегистрироваться». Самый быстрый способ отладить — назвать точный симптом, потому что каждый указывает на разный слой.

1) Письмо не приходит

Если пользователи говорят, что письма нет, не думайте сразу, что приложение не отправило его. Часто оно отправляло, но провайдер блокировал, письмо попало в спам или ушло на неправильный адрес.

Типичные признаки: проблемы только у некоторых доменов (например, корпоративные почты), сообщения попадают в спам или в логах почтового провайдера ничего нет. Также проверьте базовые вещи: опечатки в email пользователя, неверный домен отправителя или переменные окружения, которые всё ещё указывают на тестовый сервис в продакшене.

2) Ссылка открывается, но показывает «invalid» или «expired»

Классическая жалоба «проверка email не работает». Обычно это значит, что токен в URL не может быть сопоставлен с тем, что ожидает бэкенд, или логика истечения слишком строга.

Распространённые причины:

  • Токен некорректно URL-кодируется.
  • Бэкенд хеширует токены, но сравнивает их как plain text.
  • Секреты меняются между деплойами, и ранее выданные токены никогда не проходят валидацию.
  • Сдвиг времени или логика часовых поясов помечает токены как сразу просроченные.

3) Ссылка говорит «подтверждено», но пользователь всё ещё не залогинен

Пользователь кликает ссылку, видит сообщение об успехе, затем его выбрасывает на экран входа, как будто ничего не произошло.

Обычно так происходит, когда верификацию и вход рассматривают как разные шаги, но UI подразумевает, что это единый процесс. Другие причины: cookies сессии не устанавливаются (неправильный домен, отсутствуют secure-флаги, блокируются сторонние куки), или endpoint подтверждения обновляет базу, а фронтенд всё ещё использует устаревшее состояние пользователя.

4) Повторная отправка работает, но старые ссылки всё ещё действуют (или ничего не работает)

Потоки повторной отправки часто ведут себя странно в коде, сгенерированном ИИ. Вы можете увидеть повторные отправки, которые создают новые письма, в то время как каждая ссылка остаётся валидной навечно, или наоборот — каждое новое письмо сразу же терпит неудачу.

Надёжный поток повторной отправки нуждается в ясном правиле: новый токен инвалидирует старый или любой активный токен всё ещё действует?

  • Если вы не отзываете старые токены, злоумышленники могут использовать любую утёкшую ссылку.
  • Если вы отзываете слишком агрессивно без уведомления пользователя, он нажмёт на старое письмо и увидит «expired», хотя только что запросил повторную отправку.

5) Множественные аккаунты, дубликаты и состояния гонки

Повторные клики и попытки регистрации могут порождать крайние случаи: два ряда пользователей для одного и того же email, флаг verified, который переключается туда‑сюда, или ошибки «уже подтверждён», которые всё равно не разрешают вход.

Ищите гонки вроде: пользователь регистрируется дважды до завершения первой верификации, две проверки выполняются одновременно или повторная отправка создаёт второй pending-запись. Такое чаще встречается у нетерпеливых мобильных пользователей, которые тапают ссылку несколько раз.

Если симптомы появляются вместе, это часто значит, что поток собран из кусков без единого источника правды для токенов, сроков и состояния пользователя.

Где обычно живут ошибки (доставка, приложение, база)

Когда «проверка email не работает», команды часто тыкаются не в том месте сначала. Рассматривайте это как эстафету: почтовый провайдер должен доставить сообщение, приложение — принять клик, а база — записать новое состояние.

Разделите проблему на три зоны

Большинство сбоев укладывается в одну из этих зон:

  • Доставка: письмо не приходит, попадает в спам или клиент почты меняет ссылку (оборачивает, обрезает или переписывает «safe-link")
  • Приложение: endpoint верификации отклоняет запрос, фронтенд показывает неправильное сообщение или endpoint подтверждает, а UI остаётся в подвешенном состоянии
  • База данных: запись токена отсутствует или перезаписана, метки времени неверны, флаг пользователя не обновляется

Начните с простого вопроса: дошёл ли клик до бэкенда? Если в логах есть запись запроса на верификацию, доставка, вероятно, в порядке, и проблема в приложении или базе.

Смена состояния должна происходить на бэкенде

Типичная ошибка в AI‑сгенерированных потоках: фронтенд притворяется, что верификация удалась (показывает экран успеха), но никакого изменения на бэкенде не происходит. Верификация должна быть серверным действием, которое обновляет реальные записи: статус пользователя, пометку токена как использованного и время.

Ваша модель данных должна чётко отвечать на вопросы:

  • Какому пользователю принадлежит этот токен?
  • Когда он создан и когда истекает?
  • Был ли он уже использован?
  • Какой текущий статус подтверждения пользователя?

Если чего‑то из этого не хватает, вы увидите нестабильное поведение вроде «работает один раз», «работает только на мобильном» или «постоянно говорит, что ссылка просрочена».

Несоответствие окружений (dev vs prod)

Даже если код правильный, настройки могут изменить поведение между окружениями. Бэкенд может генерировать ссылки с неправильным хостом или использовать другие секреты для подписи токенов в продакшене. Ещё классика: в продакшене запущено несколько инстансов, а токены хранятся в памяти — поэтому половина запросов не может их проверить.

Если вы унаследовали AI‑сгенерированный прототип (Lovable/Bolt/v0/Cursor/Replit), корневая причина часто не в одном баге, а в нескольких небольших рассинхронизациях по доставке, приложению и базе, которые проявляются только в продакшене.

Ошибки со ссылками и токенами, которые ломают верификацию

Когда «проверка email не работает», баг часто не в самом письме, а в том, как токен создаётся, хранится и проверяется.

Ссылка подтверждения надёжна ровно настолько, насколько надёжны правила вокруг её токена. Если эти правила размыты (или отсутствуют), вы получаете случайные отказы у реальных пользователей и лёгкие способы злоупотребления для атакующих.

Ошибки с токенами, которые тихо ломают поток

Эти проблемы чаще всего встречаются в коде, сгенерированном ИИ:

  • Токен не хранится на сервере, поэтому приложение не может проверить его позже. Некоторые прототипы хранят токен в клиентском состоянии (localStorage) и сравнивают его с самим собой — это ничего не доказывает.
  • Токен попадает в логи. Если вы логируете полные URL или тела запросов, токен может оказаться в логах сервера, аналитике, тракерах ошибок или в скриншотах поддержки.
  • Токен хранится в открытом виде. При утечке базы атакующие могут сразу подтвердить аккаунты. Более безопасный подход: хранить хэш токена, как пароля.
  • Неверный период жизни. Слишком короткий — обычные задержки приводят к «ссылка просрочена». Отсутствие срока — старые ссылки работают вечно.
  • Токен привязан не к тому объекту. Частая несостыковка: токен создан для user_id, а endpoint ищет по email, или ссылка указывает на неправильный API‑хост.
  • Endpoint не проверяет назначение и статус. Токен должен быть действителен только для одной цели (подтверждение email) и только если аккаунт ещё не подтверждён.
  • Существует несколько валидных токенов без ясных правил. Если вы допускаете неограниченное количество активных токенов, нужна чёткая политика: побеждает самый новый или любой активный токен сработает.

Небольшой реалистичный пример: пользователь регистрируется, запрашивает два повторных письма, затем кликает по первому. Ваша система принимает только последний токен, поэтому отвечает «invalid token». Пользователь не виноват — правила и сообщения не совпадают с тем, что произошло.

Правила, которые работают на практике: генерируйте случайный токен, храните только его хэш, задавайте разумный срок жизни (часы, а не минуты), привязывайте токен к пользователю и цели, и выберите политику для множества токенов (часто «побеждает самый новый», а старые аннулируются).

Логика повторной отправки, которая работает и не приглашает злоупотреблений

Free email verification audit
We’ll pinpoint why verification fails and what to change, before you commit.

При поломке верификации пользователи сначала нажимают «Отправить повторно». Если логика повторной отправки слабая, это превращается в бесконечный цикл для реальных пользователей и бесплатный инструмент для злоумышленников.

Хорошая кнопка «Отправить повторно» делает три вещи каждый раз: создаёт свежий токен, делает старые токены бесполезными и замедляет повторные запросы. Генерация нового токена без инвалидирования старого приводит к запутанным состояниям вроде «уже использовано» или «несовпадающий токен», в зависимости от того, какое письмо пользователь открыл.

Сделайте поведение повторной отправки предсказуемым:

  • Выпускайте новый токен и отзывайте предыдущие неиспользованные токены
  • Ограничивайте по скорости по аккаунту и по IP (короткая пауза плюс дневной лимит)
  • Возвращайте одинаковое сообщение независимо от того, существует ли email или нет (предотвращает обнаружение аккаунтов)
  • Храните хэши токенов (не сырые токены) и используйте ясное время жизни
  • Покажите понятный UI: «Письмо отправлено. Попробуйте через 30 секунд.»

Избегайте петли повторной отправки, используя единый источник правды для «ожидающей верификации» информации. Не полагайтесь на флаги фронтенда или localStorage. Базируйтесь на состоянии сервера (например, поле в user‑записи). UI должен читать это состояние и предлагать только подходящие действия: отправить снова, сменить email или связаться с поддержкой.

Обработка пользователей, которые поменяли email до подтверждения, — ещё одна точка отказа. Обрабатывайте это как новый поток: обновите ожидающий email, отзовите все предыдущие токены и потребуйте подтверждения нового адреса. Иначе вы рискуете подтвердить неправильный email или оставить несколько действующих ссылок.

Логи важны, но логируйте безопасно. Фиксируйте события и метки времени (verification_sent, verification_clicked, verification_succeeded, verification_failed) и грубую метаданную вроде user_id и кодов ответа провайдера. Никогда не логируйте полные токены или полные URL. Если нужны трассы, логируйте идентификатор токена (например, первые 6 символов хэша) и не показывайте это пользователю.

Пошагово: сделайте верификацию надёжной end‑to‑end

Если проблема с «проверкой email не работает» появляется изредка, обычно это потому, что поток распылён по разным местам. Сделайте его скучным: один токен, один endpoint, одно понятное изменение состояния.

1) Постройте простой повторяемый поток

Решите, как работают токены, прежде чем трогать UI.

  1. Сгенерируйте токен, который нельзя угадать. Используйте длинное случайное значение (не короткий код, не user id, не предсказуемый JWT, если вы не уверенны).
  2. Храните только необходимое. Сохраните хэш токена (не сырой токен), плюс user_id, expires_at и used_at (или простой status).
  3. Отправьте одну ссылку подтверждения, которая указывает на один backend endpoint, например: GET /verify-email?token=....
  4. Подтвердите один раз и установите один источник правды. Когда токен валиден, отметьте пользователя как подтверждённого (например, users.email_verified_at = now()), и пометьте токен как использованный.
  5. Обрабатывайте истечение с дружелюбным путём. Если токен просрочен, покажите понятное сообщение и предложите безопасный способ запросить новое письмо.

2) Сделайте повторную отправку и повторные клики безопасными

Два правила удерживают это надёжным: инвалидируйте старые токены и сделайте каждое действие идемпотентным.

Когда пользователь кликает ту же ссылку дважды (или клиент почты предзагружает её), ничего не должно ломаться. Если пользователь уже подтверждён, endpoint должен вернуть «Вы подтверждены» и остановиться.

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

Короткий чеклист, который ловит большинство ошибок AI‑сгенерированных потоков:

  • Один активный токен на пользователя, невалидный после повторной отправки
  • Один endpoint верификации, который совершает полное изменение состояния (не частично на фронтенде)
  • Атомарное обновление: подтвердить пользователя и пометить токен использованным в одной операции
  • Чётный интервал жизни (например, от 15 минут до 24 часов) и чистый путь «отправить снова»
  • Идемпотентные ответы для уже подтверждённых и уже использованных токенов

Пример: основатель тестирует регистрацию, кликает ссылку — всё работает. Позже коллега пытается использовать ту же ссылку и получает «invalid token». Это может быть нормальным, но только если endpoint отвечает «Уже подтверждён» вместо ошибки. Часто состояние правильное, но не хватает корректных сообщений и идемпотентности, поэтому кажется, что всё ломается.

Защита от злоупотреблений и проверки безопасности, которые стоит добавить рано

Security check while we fix
We’ll check for exposed secrets and common auth vulnerabilities while fixing verification.

Команды часто фокусируются на доставляемости и UI. Но многие «сломанные» потоки на самом деле блокируются отсутствием защит или работают, но легко поддаются злоупотреблению. Добавьте несколько ограничителей рано, чтобы верификация оставалась надёжной под реальной нагрузкой.

Ограничивайте повторные отправки и попытки верификации

Кнопка «Отправить повторно» привлекает злоумышленников. Если бот спамит сотни писем в минуту, ваш провайдер может задержать или отклонить сообщения, и реальные пользователи окажутся заблокированы.

Ограничения, которые обычно работают без наказания реальных пользователей:

  • Лимит повторной отправки на аккаунт (например, 1 раз в минуту с коротким суточным лимитом)
  • Лимит попыток верификации по токену (остановить после нескольких неправильных попыток)
  • Троттлинг по IP или устройству для повторных регистраций и повторных отправок
  • Прогрессивные задержки, когда каждая следующая отправка увеличивает ожидание
  • Ясная обратная связь для пользователя (таймер лучше, чем бесконечно кликающая кнопка)

Один важный момент: не генерируйте новый токен на каждый клик без ограничений. Это засоряет базу и усложняет поддержку («какое письмо правильное?»).

Предотвращайте повторное воспроизведение и популярные сценарии захвата аккаунта

Токены должны быть одноразовыми и недолговечными. После успешной верификации пометьте токен как использованный и навсегда отвергайте его.

Также следите за путями, удобными для перехвата аккаунта. Подтверждение не должно тихо менять email аккаунта просто потому, что кто‑то кликнул ссылку. Более безопасный сценарий: запрос изменения -> отправка ссылки на новый email -> подтверждение -> уведомление старого email.

Ещё тихая проблема — перенаправления. Если endpoint принимает параметр redirect, разрешайте только известные внутренние направления. Иначе злоумышленники могут использовать ваш домен, чтобы переадресовать пользователей на фишинговые страницы.

Обращайтесь с токенами как с паролями: они легко утекают. Не кладите полные токены в логи, аналитику, отчёты об ошибках или скриншоты поддержки. Если нужно логирование, храните только короткий префикс хэша.

Реалистичный пример: прототип захватывает URL верификации (с токеном) в инструмент аналитики, и он попадает в сторонние дашборды. Любой, у кого есть доступ, может подтвердить аккаунты. Такое случается чаще, чем думают, потому что шаблонный код по умолчанию часто логирует полные URL.

Частые ловушки в потоках регистрации, сгенерированных ИИ

Когда «проверка email не работает» в приложении, созданном ИИ, люди часто сначала винят почтового провайдера. Доставка может быть частью проблемы, но повторяющиеся отказы обычно идут из запутанного состояния приложения: база говорит одно, сервер проверяет другое, а UI показывает третье.

Один частый паттерн — «слишком много токенов, нет правил». Флоу регистрации создаёт новый токен при каждой повторной отправке, но старые токены никогда не инвалидируются. Тогда пользователь кликает первое письмо, сервер проверяет последний токен, и получается несоответствие, хоть ссылка и выглядит валидной. Две вкладки или два устройства также могут создать конкурирующие запросы, которые оставляют состояние в странном виде.

Дизайн токенов — ещё одна ловушка. Код, сгенерированный ИИ, иногда использует предсказуемые токены или кладёт user ID (или email) прямо в ссылку и называет это «подтверждением». Это проще угадать или повторно использовать, и сложнее безопасно инвалидировать.

Сервер — источник правды, а не клиент

Если UI ставит isVerified = true после клика (без серверного обновления), в том браузере всё будет выглядеть нормально, а везде ещё — нет. Нужен один источник правды на сервере, в базе, и каждое защищённое действие должно его проверять. Иначе люди могут обойти подтверждение, обращаясь напрямую к API.

Время — ещё одна тихая причина отказов. Реальные пользователи кликают поздно, кликают дважды или получают письма в неверном порядке. Если вы не тестируете окна истечения, рассинхрон времени между сервисами и задержки доставки, вы выпустите поток, который работает только в идеальных условиях лаборатории.

Быстрые проверки, которые ловят большинство багов регистрации:

  • В один момент времени действует только один валидный токен на пользователя, с понятными правилами приоритета
  • Токены хранятся в виде хэшей (не сырые) и валидация всегда происходит на сервере
  • Повторная отправка — это изменение состояния: отзывать старые токены, устанавливать новый срок и логировать событие
  • Верификация идемпотентна: второй клик отвечает «уже подтверждён», а не ошибкой
  • Тестируйте таймауты: просроченные ссылки, письма вне порядка, двойные клики и переход между устройствами

Реалистичный пример: как починить сломавшуюся регистрацию за неделю

Quick triage call
Explain what users see and we’ll tell you the likely root cause in minutes.

Основатель выпустил приложение, сгенерированное ИИ, и первые пользователи упёрлись: регистрация проходит, но никто не может войти, потому что приложение настаивает на подтверждении email. В тикетах поддержки одно и то же: «Я не получил письмо». Немного пользователей письмо получает, но при клике кнопки появляется ошибка «invalid token». Основатель ищет «email verification not working» и пробует быстрые правки, но проблема возвращается.

Начните с банальных вещей. Почтовый провайдер отказывал в отправке, потому что from‑адрес не совпадал с верифицированным доменом, поэтому многие сообщения вообще не уходили. Сверху — приложение генерировало токены в памяти, отправляло их, но никогда не сохраняло в базу. Когда пользователь кликал ссылку, серверу было нечем сравнить.

Стабильное исправление выглядело так:

  • Исправлены настройки отправителя (верифицированный домен, from и reply‑to)
  • Сохранён хэш токена с временем истечения и user id
  • При клике: проверка существования пользователя, совпадение токена, не просрочен и не использован
  • Добавлен лимит повторной отправки (cooldown и суточный кап) по аккаунту и по IP
  • Старые токены инвалидируются при выпуске нового

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

Во время перехода службе поддержки нужен простой сценарий, который не обвиняет пользователя. Пример: «Мы исправили отправку писем подтверждения. Пожалуйста, запросите новое письмо с экрана входа. Если не получите его в течение 5 минут, проверьте спам и напишите нам, какой домен вы использовали (Gmail, Outlook и т.д.).»

Быстрая проверка и следующие шаги

Если «проверка email не работает» блокирует регистрации, сделайте быстрый пробег по всей цепочке. Большинство «не работает» сводится к одному несоответствию между тем, что отправлено, и тем, что сервер ожидает при открытии ссылки.

Короткий чеклист, который можно пройти за ~15 минут:

  • Доставка: подтвердите, что письмо действительно отправлено, не застряло в очереди провайдера и не попадает в спам (проверьте события провайдера и bounced‑сообщения)
  • Корректность ссылки: откройте письмо и сравните домен, путь и query params ссылки с тем, что обрабатывает ваше приложение (следите за URL‑кодированием)
  • Правила токена: подтвердите формат токена, стратегию хеширования, время жизни и настройки времени на обеих сторонах
  • Обновления состояния: убедитесь, что поле verified записывается один раз в нужную запись пользователя и что логика входа читает именно это поле
  • Логирование: добавьте trace ID для каждого запроса, логируйте понятные коды причин (expired, already_used, invalid) и никогда не логируйте полные токены (только короткий префикс)

Затем прогоните несколько краевых тестов в режиме инкогнито, чтобы увидеть, что видят новые пользователи:

  • Просроченный токен: дождитесь истечения TTL, кликните ссылку, подтвердите понятный результат и безопасный путь повторной отправки
  • Дважды отправить: запросите две повторные отправки, подтвердите, что работает только самое новое письмо (или что старые явно инвалидированы)
  • Клик по старой ссылке после повторной отправки: убедитесь, что это не повернёт пользователя обратно в непроверенное состояние
  • Дважды кликнуть одну и ту же ссылку: подтвердите, что второй клик обрабатывается безопасно и не вызывает ошибку
  • Создать два аккаунта и поменяться ссылками: убедитесь, что ссылки не могут подтвердить другого пользователя

Если вы унаследовали AI‑сгенерированную кодовую базу, смотрите на запутанные auth‑потоки: несколько endpoint'ов подтверждения, токены хранятся в разных местах или UI помечает пользователя подтверждённым до того, как сервер это сделал. Такие вещи трудно заметить без просмотра полного потока.

Если нужна вторая пара глаз, FixMyMess (fixmymess.ai) специализируется на диагностике и починке кода, сгенерированного ИИ, включая сломанные потоки аутентификации и верификации. Фокусированный аудит кода часто выявляет небольшие, но кумулятивные проблемы (настройки доставки, хранение токенов, правила повторной отправки, обновления состояния), которые видны только в продакшене.

Часто задаваемые вопросы

How do I quickly tell if the problem is email delivery or my app logic?

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

Users say the verification email never arrives—what should I check first?

Сначала убедитесь, что ваш почтовый провайдер принял и отправил письмо, затем проверьте отказы (bounces) и попадание в спам. Проверьте настройки отправителя (from-адрес и соответствие домена) и убедитесь, что в продакшене не используется тестовый почтовый сервис. Если проблема проявляется только для некоторых доменов — это обычно вопрос доставляемости или политики, а не кода.

Why do verification links show “invalid token” or “link expired” even right after signup?

Обычно это значит, что токен в URL не совпадает с тем, что сервер сохранил, или сервер считает токен просроченным. Частые причины: ошибки URL-кодирования, сравнение хэша с открытым токеном, смена секретов между деплойами или ошибки времени/часовых поясов. Логируйте чёткие коды ошибок вроде expired или not_found, чтобы быстрее сузить круг поиска.

The link says “verified” but the app still treats the user as unverified—why?

Верификация должна быть реальным действием на сервере — обновлением записи в базе. Если бэкенд обновил пользователя, а фронтенд продолжает читать устаревшие данные, интерфейс будет показывать «не подтверждён». Проверьте также настройки cookies/сессий (домен, secure-флаги), потому что пользователь может быть подтверждён, но автоматически не залогинен.

What’s the safest resend behavior: should old links still work or not?

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

How should I store verification tokens to avoid both bugs and security issues?

Храните хэш токена (не сам токен) вместе с user_id, целью (purpose), временем истечения и отметкой об использовании. Обращайтесь с токеном как с паролем: не логируйте полный токен или полную ссылку, и навсегда отклоняйте использованные токены. Это уменьшает риски при утечке и делает валидацию надёжной.

Why does clicking the same verification link twice sometimes break things?

Сделайте endpoint идемпотентным: если пользователь уже подтверждён, верните сообщение об успехе вместо ошибки. Некоторые почтовые клиенты или инструменты безопасности предварительно открывают ссылки (prefetch), и первая «проверка» может произойти до реального клика пользователя. Идемпотентность предотвращает ломание UX в таких случаях.

How do duplicate accounts or race conditions happen during signup and verification?

Обычно создаются несколько записей пользователя или несколько ожидающих токенов без единого источника правды. Введите уникальное ограничение по email и убедитесь, что верификация потребляет ровно один токен и атомарно обновляет запись пользователя. Обрабатывайте двойные клики и параллельные запросы так, чтобы состояние не «переключалось» туда-сюда.

How do I stop the resend button from being abused or hurting deliverability?

Ограничьте повторные отправки по аккаунту и по IP, используйте cooldown и дневной лимит. Возвращайте одинаковое сообщение, есть ли такой email в системе или нет, чтобы предотвратить поиск аккаунтов. Не создавайте новый токен на каждый клик без лимитов — это засоряет базу и усложняет поддержку.

Can FixMyMess help if this came from an AI-built prototype (Lovable, Bolt, v0, Cursor, Replit)?

Да, это частая ситуация. Поток, сгенерированный ИИ, часто хранит токены в памяти, строит ссылки с неправильным доменом или помечает verified только на фронтенде. FixMyMess (fixmymess.ai) может провести бесплатный аудит кода, локализовать проблему (настройки доставки, хранение токенов, правила повторной отправки, обновления состояния) и обычно вернуть работоспособность в течение 48–72 часов.