Одноразовая ссылка ·

Почему после вставки одноразовой ссылки в чат с превью число прочтений ещё не обязательно потрачено

Карточка под сообщением — это не то же самое, что получатель уже прочитал шифротекст. Чтобы нарисовать превью, Telegram, Slack или другой мессенджер сам отправляет HTTP GET на посадочную страницу. Этот запрос обычно читает заголовок и короткое описание. Он не нажимает Открыть и посмотреть и не уносит ключ после #. Ниже отделяем «карточка появилась» от «одно прочтение израсходовано» и даём проверку на тестовой ссылке. Это не инструкция к форме создания одноразовой ссылки.

Сверьте слой превью Можно проверить сразу Без регистрации
00 / Содержание

Сначала решите, какой слой читает карточка

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

01 / Запрос

Какой слой карточка превью реально забирает

Карточка появляется за несколько секунд вокруг отправки. Запрос идёт с серверов мессенджера, а не с пальца получателя.

Вставьте адрес вида https://…/s.html?id=…#… в Telegram, Slack, Discord или VK — и под полем ввода или под сообщением часто появляется маленькая карточка: заголовок, одна строка описания, иногда значок. Многие читают её как «ссылку уже открыли». Порядок обратный. Сначала мессенджер отправляет на этот адрес HTTP GET, вытаскивает из HTML og:title, og:description или обычный <title> и только потом рисует карточку. Получатель к этому моменту мог ещё ничего не нажимать.

У Slack это записано официально. По умолчанию сообщения с полным URL разворачиваются в превью. Робот, который это делает, представляется как Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots). Страница Unfurling links in messages называет такое поведение classic link unfurling: Slack обходит URL и прикрепляет превью. На странице Slack Robots сказано, что разворачиватель забирает как можно меньше страницы (через HTTP Range) и ищет теги oEmbed, Twitter Card и Open Graph. Файл картинки, видео или звука он качает только если тег на него указывает — чтобы проверить этот файл. Ответ кэшируется примерно на 30 минут. В документации нет фраз «выполнить JavaScript страницы» или «нажать кнопку за пользователя».

Telegram для той же задачи шлёт своего краулера. Он часто представляется как TelegramBot (like TwitterBot): серверы Telegram забирают исходный HTML и читают Open Graph. Публичные разборы этого бота сходятся в одном рабочем допущении: скрипты страницы он не исполняет. Если теги не лежат в ответе сервера, карточка либо пустая, либо её нет. Когда браузер или такой робот отправляет GET, текст после # в строку запроса не входит. IETF RFC 3986 §3.5 определяет фрагмент как клиентский идентификатор: сервер при обработке URI его не использует. Даже если робот скопировал всю строку из поля ввода, до UsePwd обычно доходит только s.html?id={id}. Почему ключ не появляется в журналах доступа — в статье Почему фрагмент # в URL не попадает в логи сервера. Здесь нужен следующий слой: считается ли GET без ключа «одним прочтением».

Одна фраза

Полезный вопрос не «увидел ли мессенджер этот URL», а «дошёл ли этот GET до интерфейса шифротекста». Карточка доказывает только то, что заголовок прочитали. На месте ли шифротекст — отдельная проверка.

02 / Слои

Почему первый GET — это ещё не прочтение

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

Многие одноразовые ссылки вешают секрет на тот же URL, который бот превью запрашивает методом GET. Робот сходил один раз — сервер отдал тело и удалил запись. Получатель потом открывает «уже прочитано» или 404. Такой сбой встречается часто. Это не Slack и не Telegram «назло сжигают секрет». Это реализация, которая безопасный и кэшируемый GET — метод, который RFC 9110 по-прежнему описывает как safe, — принимает за действие, уничтожающее шифротекст.

Одноразовая ссылка UsePwd делит работу на три шага. При создании текущая вкладка шифрует через Web Crypto алгоритмом AES-256-GCM. Открытый текст — не больше 32 КБ. На сервер уходят только шифротекст, срок жизни и число прочтений. Срок: 1 час, 24 часа, 7 дней или «только после прочтения» без TTL по часам. Прочтений можно задать от 1 до 10, по умолчанию 1. Вид ссылки: s.html?id={id}#{key}. И создание, и чтение открываются сразу — ни одна сторона не регистрируется. Сервер хранит только шифротекст. Открытый текст он не видит, аккаунта, по которому секрет можно вернуть пользователю, нет.

Когда получатель открывает страницу чтения, вкладка сначала спрашивает /api/secrets/{id}/status, на месте ли шифротекст. Ответ: active, burned или expired. Счётчик прочтений от этого не растёт, запись не удаляется. Только после Открыть и посмотреть браузер запрашивает /api/secrets/{id}, забирает шифротекст и расшифровывает его локально ключом после #. Когда достигнуто число, заданное при создании, сервер удаляет шифротекст; следующий запрос получает 410. Бот превью, который останавливается на HTML посадочной страницы, обычно не имеет ключа и эту кнопку не нажимает.

GET посадочной
s.html?id={id}

Отдаёт статическую страницу чтения. Заголовок — «Открыть одноразовую ссылку». Открытого текста в нём нет. Карточки превью обычно останавливаются здесь.

Запрос статуса
/secrets/{id}/status

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

Выдача шифротекста
/secrets/{id}

Именно этот вызов считается прочтением. После значения по умолчанию — 1 — шифротекст удаляется. На странице чтения это кнопка Открыть и посмотреть.

На странице чтения есть фраза, которую можно сверить сразу: кнопка «может засчитать одно прочтение». Без скриптов расшифровать нельзя; блок <noscript> это говорит. Бот, который читает только HTML и JavaScript не запускает, даже статус не спросит. Если встроенный браузер скрипты всё же выполнит, страница остановится на «Шифротекст ещё на месте», пока кто-то не нажмёт кнопку.

03 / Боты

Какие боты приходят и что они уносят

Карточку умеют рисовать многие продукты. Уносят они шапку страницы, а не ключ после # и не расшифрованный открытый текст.

Робот разворачивания ссылок Slack, краулер встраиваний Discord и серверный запрос, которым Telegram или VK собирают карточку, — один класс выборки: GET страницы, затем поиск заголовка и описания в HTML. Discord представляется как Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com). Публичные описания Discordbot сходятся в том, что он разбирает HTML, который вернул сервер, и JavaScript не исполняет. Slack отдельно пишет про другой предел. После попыток жить по robots.txt Slack перестал относиться к нему как поисковый краулер: разворачивание ссылки — «действие от имени человека», а не обход сайта. Метка noindex на странице чтения может не пустить её в индекс поисковика. Карточку в канале она сама по себе не отменяет.

Страница чтения UsePwd — временный просмотр шифротекста. У неё стоит noindex, nofollow, в карту сайта она не входит. Заголовок документа зафиксирован: «Открыть одноразовую ссылку». Описание говорит только, что расшифровка заканчивается в этой вкладке, а ключ стоит после #. Если карточка превью появилась, в чате видна эта общая строка — не пароль, не токен и не текст тикета, который вы ввели. Это совместимо с тем, что «в журнале доступа нет фрагмента». Карточка доказывает: кто-то сделал GET посадочной страницы. В логе по-прежнему нет ключа после #.

Slack пишет, что результат разворачивания одного и того же URL кэшируется примерно на 30 минут. Если дважды подряд вставить одну тестовую ссылку в канал, второй запрос может не появиться. Telegram кэширует карточку дольше; чтобы пересобрать превью, в клиенте обычно пользуются служебным ботом @WebpageBot. Чтобы понять, «приходил ли бот», смотрите свой журнал доступа на User-Agent вроде Slackbot-LinkExpanding, Discordbot или TelegramBot. Не смотрите, сколько раз мигнула карточка. GET на s.html сам по себе не значит, что вызвали /api/secrets/{id}.

HTML посадочной страницы 01 Превью обычно берёт этот слой. Заголовок общий. Открытого текста и ключа после # в нём нет.
Интерфейс статуса 02 Спрашивают, только если выполнился скрипт страницы. Сам вопрос прочтение не тратит.
Интерфейс шифротекста 03 Это «Открыть и посмотреть». Обычная выборка карточки сюда не доходит.
Полный URL в истории чата 04 Это вторая копия. Счётчик, который не упал, не значит, что ссылка существует только в голове получателя.
04 / Когда сгорает

Когда прочтение действительно тратится

«Не обязательно потрачено» — не «никогда не тратится». Визит, который может нажать Открыть и посмотреть, — это визит, который доходит до слоя шифротекста.

Обычный потребитель — сам получатель: открывает полную ссылку, видит «Шифротекст ещё на месте», затем нажимает Открыть и посмотреть. По умолчанию прочтений 1. После успешной выдачи сервер удаляет шифротекст. Повторное открытие той же ссылки показывает «Уже сожжена». Если при создании задать 2 и больше, ранние выдачи оставляют шифротекст на месте, пока счётчик не упрётся в потолок. Срок тоже удаляет: по умолчанию 24 часа, либо 1 час, либо 7 дней. Вариант «только после прочтения» удаляет по счётчику, а не по часам.

Второй класс — среда, которая выполняет скрипты и имитирует нажатие. Некоторые почтовые шлюзы и песочницы ссылок открывают URL в изолированном браузере. Часть из них ещё и нажимает главную кнопку. Это уже не «нарисовать карточку». Это второй визит, похожий на человеческий. Если песочница нажала Открыть и посмотреть, прочтение потрачено, и получатель потом видит состояние «сожжена». Это сверяют по отчёту самого шлюза. Из карточки в чате этого не вывести.

Третий класс — отправка самого адреса интерфейса шифротекста. Адрес страницы чтения — s.html?id=. Вызов, который тратит прочтение, — /api/secrets/{id}. Обычный пользователь вторую строку не копирует. Если кто-то вставит этот API-URL в канал с превью, а продукт примет первый GET за выдачу, прочтение уйдёт ещё на этапе карточки. Форма шаринга UsePwd — не этот адрес. Страница создания копирует ссылку на страницу чтения с #.

Не переворачивайте

Превью, которое не потратило прочтение, не значит, что секрет существует только для получателя. Мессенджер всё равно хранит полный URL. Администратор рабочего пространства может выгрузить историю. Лимит прочтений задаёт, сколько живёт шифротекст на сервере. Он не задаёт, кто ещё может открыть этот адрес из канала.

05 / Проверка

Как сверить превью сразу на месте

Цель не доказать, что «ни один мессенджер на земле никогда не прочитает шифротекст». Цель — доказать: после появления карточки интерфейс шифротекста ещё не вызывали.

  1. 01
    Подготовьте тестовый текст, которым не будете передавать настоящий секрет

    Не вводите живой API-ключ и не вводите пароль от входа. Откройте одноразовую ссылку, введите PreviewCard-20260905, оставьте 1 прочтение и срок 24 часа. Страница открывается сразу — регистрироваться не нужно. Эти шаги сравнивают карточку с интерфейсами. Это не способ отдать эту строку коллеге как настоящий секрет.

  2. 02
    Сохраните полную ссылку и сами откройте страницу чтения

    После создания должны быть и s.html?id=, и сегмент #. Откройте ту же ссылку в текущем браузере. Страница должна остановиться на «Шифротекст ещё на месте» и показать Открыть и посмотреть. Кнопку пока не нажимайте. Нажмите F12 и откройте панель Network. Должен быть запрос к /api/secrets/{id}/status. Запроса шифротекста к /api/secrets/{id} без суффикса /status ещё быть не должно.

  3. 03
    Вставьте ту же ссылку в тестовый чат, который строит превью

    Возьмите «Избранное» в Telegram, канал Slack, который видите только вы, или личку Discord с собой. Дождитесь карточки. Заголовок должен быть близок к «Открыть одноразовую ссылку». Строки PreviewCard-20260905 на карточке быть не должно. Обновите страницу чтения или откройте ту же ссылку в окне, где кнопку ещё не нажимали. Должно по-прежнему быть «Шифротекст ещё на месте». Если уже написано «Уже сожжена», до интерфейса шифротекста дошёл другой визит. Это не «карточка сама по себе равна прочтению».

  4. 04
    Если есть журналы доступа, сверьте User-Agent

    На этапе превью обычно виден GET на /ru/s.html?id=…. В User-Agent могут быть Slackbot-LinkExpanding, Discordbot или TelegramBot. В том же окне не должно быть успешной выдачи /api/secrets/{id}. Slack кэширует один URL примерно 30 минут, поэтому вторая вставка в этом окне может не дать второй запрос.

  5. 05
    Только потом нажмите «Открыть и посмотреть» и убедитесь, что прочтение потрачено

    Нажмите кнопку. В панели Network должен появиться запрос шифротекста, а на странице — тестовый текст. Закройте и откройте ту же ссылку снова. Должно быть «Уже сожжена». Этот шаг нужен, чтобы «после кнопки» и «после карточки» стали двумя разными состояниями, которые вы видели сами. Ссылку после проверки выбросьте. Не отдавайте её настоящему получателю.

Если нужно только передать новый ключ другому человеку, не вставляйте открытый текст в канал «чтобы посмотреть карточку». Тестовая строка и настоящий секрет — разные вещи. Настоящую передачу всё равно ведите через одноразовую ссылку. Про второй круг риска после утечки переменных окружения — в статье После утечки переменных окружения в облаке новый ключ нельзя снова слать в чат. Та статья — «не вставляйте сам API-ключ в историю, которую можно найти поиском». Эта — «после вставки ссылки на каком слое останавливается выборка превью».

06 / Границы

Что чат всё равно хранит, если счётчик не упал

Прочитать «карточка не сожгла шифротекст» как «эта ссылка уже исчезла из канала» — значит пропустить несколько границ, которые тоже можно сверить.

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

Скриншоты и пересылки — ещё одна копия. Если кто-то снял открытый текст после успешного чтения или переслал ссылку в другой чат с превью, граница сдвигается с «этого GET» на «кто стоит на следующем прыжке». Одноразовая ссылка ограничивает, сколько шифротекст живёт на сервере и сколько раз его можно забрать. Она не мешает получателю сделать снимок экрана. У UsePwd нет аккаунтов и хранилища паролей, потерянную ссылку по пользователю вернуть нельзя. Долгое хранение и синхронизация между устройствами — задача отдельного менеджера паролей. Кто мы — на странице О сервисе.

Файлы — третий путь. Локальная копия одного файла до 5 ГБ идёт через шифрование файлов и даёт .lock / .enc. Файл по умолчанию на сервер не загружается. Саму парольную фразу сгенерируйте в диапазоне 6–128 в генераторе паролей, затем проверьте стойкость в проверке пароля. Эти страницы, как и эта статья, открываются сразу. Не читайте «превью не потратило прочтение» как «вставьте в любой канал — и это безопасно». История канала и выборка превью — разные слои.

07 / Вопросы

Что спрашивают после превью

Четыре ответа остаются на границе этой статьи. Кнопки со страницы создания они не повторяют.

Карточка значит, что уже прочитали? 01 Обычно нет. Карточка показывает только GET посадочной страницы. Прочтение ждёт «Открыть и посмотреть».
Telegram или Slack исполняют скрипт страницы? 02 Серверная выборка карточки обычно читает только HTML. Скрипты запускаются, когда кто-то открывает встроенный браузер, а шифротекст всё равно ждёт кнопку.
Видит ли бот превью ключ после #? 03 HTTP-запрос фрагмент с собой не уносит. Когда бот ходит по URL, он обычно берёт только номер после вопросительного знака.
Нужен ли аккаунт для создания и чтения? 04 Нет. Открывается сразу. Справа в шапке только переключатель языка — без входа и без хранилища паролей.
08 / Дальше

Дочитали — сверьте одну карточку превью

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