Форма, зручна для ботів, стає підробітком для власника інбоксу. Форма, ворожа до людей, губить найм або угоду. Хороший intake — посередині: тертя для зловживань, ясність для живих.
Здається, що це невеликий шматок UI. Насправді форма careers чи contact одночасно торкається security, GDPR, storage і конверсії — саме тому це найменш улюблена частина сайту, яка тихо коштує найдорожче, коли зроблена неправильно. Чекліст, яким ми користуємось на careers і contact для клієнтських і студійних сайтів, і логіка за кожним пунктом.
Питайте лише те, що використаєте цього тижня
Кожне поле має ціну. Кожне додає рішення, яке має ухвалити відвідувач, правило валідації, яке хтось має підтримувати, і шматок персональних даних, які тепер треба захищати й колись видалити. Для контакту:
- Ім'я, email, повідомлення — зазвичай достатньо
- Компанія / телефон — опційно, якщо sales справді дзвонить кожному
- Тема — корисно для спільного інбоксу
Для кар'єри:
- Роль (підставте з вакансії)
- Ім'я, email, телефон
- Короткий cover note, зручний на мобільному
- CV з жорстким лімітом розміру й типу
Довгі «розкажіть життєпис» textarea на мобільному кидають і приваблюють paste-спам. Вони ж дають найгірший тип даних: довгий, неструктурований і рідко прочитаний далі перших двох рядків тим, хто розбирає інбокс. Якщо поле існує «бо колись знадобиться» — воно не заробило своє місце: або дайте йому реального власника, який діятиме за відповіддю, або приберіть. Коротка форма, яку доводять до кінця, краща за докладну, яку кидають на третьому полі.
Згода, яка є GDPR, а не театр
Непозначені маркетингові галочки в сірому тексті провалюють і закон, і довіру. Проблема тут зазвичай не в злому намірі — форму зробили швидко, з однією галочкою «погоджуюсь з умовами», яка одночасно відповідає за три різні речі: відповідь на повідомлення, зберігання даних і додавання людини в newsletter. Це не згода, це одна галочка, що вдає три окремі рішення, — і в момент, коли хтось поскаржиться, студія й клієнт опиняються не на тому боці GDPR.
Для EU-орієнтованих сайтів:
- розділіть необхідну обробку (щоб відповісти) і опційний маркетинг
- лінк на privacy поруч із контролом
- не претікайте опційну згоду
Записуйте час згоди на сервері разом із сабмітом. Сапорт подякує пізніше — питання «ця людина справді погодилась отримувати листи?» зʼявляється частіше, ніж команди очікують, зазвичай через місяці після заповнення форми, і «ми припускаємо, що так» — не та відповідь, яка витримає перевірку. Таймстемп і збережений стан згоди перетворюють це на двосекундний пошук замість здогадки.
Антиспам, якого користувач не бачить
Шари, не одна магія. Кожна окрема техніка нижче слабка сама по собі — наполегливий оператор ботів зазвичай обходить будь-який один шар за тиждень, якщо він єдине, що стоїть між ним і формою. Разом вони дешеві у впровадженні й дорогі в обході — а це і є той trade-off, що насправді має значення:
- Honeypot — приховане від AT і CSS поле
- Honeytime — відхиляти сабміти швидші за читання форми
- Rate limit по IP на API
- Серверна валідація схемою (zod тощо)
CAPTCHA — крайній засіб. Б'є по конверсії й a11y; ставте, коли аб'юз уже виміряний, не «на всяк випадок». Тут важливий порядок: команди тягнуться до CAPTCHA першою, бо вона здається «серйозним» рішенням, а потім виявляють, що вона додає тертя кожному реальному кандидату, поки трохи терплячий бот все одно проходить. Комбінація honeypot плюс honeytime сама по собі зупиняє переважну більшість скриптованого спаму, бо більшість ботів заповнюють усі поля й сабмітять менш ніж за секунду — жодна людина так не робить.
Аплоади без розриву storage
Ендпоінти CV / вкладень потребують:
- allow-list MIME (PDF, DOC/DOCX — не «будь-який файл»)
- max size у браузері і на сервері
- приватний bucket; без публічних URL, які вгадуються по ID
- сканування або карантин + завантаження лише з адмінки
Ніколи не робіть єдиною копією лист із файлом у спільний інбокс. Збережіть у storage, сповістіть лінком за auth. Це легко переплутати під час поспішного білда: емейл файлу здається швидким шляхом і працює нормально на перших кількох сабмітах. Проблеми починаються, щойно хтось завантажує файл на 40MB, або в інбоксу є ліміт розміру, або людина, яка має переглядати CV, у відпустці й ні в кого більше немає доступу до її пошти. Storage bucket з лінком в адмінку деградує акуратно в усіх трьох випадках; спільний інбокс — ні.
Невгадуваний URL важливіший, ніж здається. «Вгадуваний по ID» зазвичай означає послідовний filename чи передбачуваний шлях — один злитий лінк перетворюється на CV усіх кандидатів, доступні будь-кому, хто інкрементує число. Згенерувати випадковий токен замість цього нічого не коштує, а закриває цілий клас випадкового витоку.
Success-стани кращі за тиху помилку
Після сабміту:
- редірект на success або однозначне підтвердження
- скажіть що далі і коли («Відповімо протягом одного робочого дня»)
- при помилці валідації не стирайте cover letter
В адмінці дефолтний статус — new / unread із фільтрами. Якщо все одразу «processed» — нічого не обробляють. Це рішення виглядає дрібним, а наслідок має більший: поле статусу, яке за замовчуванням «read», або без лічильника непрочитаного, — це прибраний тиск на того, хто має розбирати інбокс. Нічого не виглядає терміновим, тому нічого не робиться, доки кандидат чи потенційний клієнт сам не напише «чому мовчите».
З боку відвідувача провал коштує так само дорого в інший бік. Людина, яка витратила пʼятнадцять хвилин на cover note, тисне submit, бачить помилку валідації й бачить, що textarea спорожніла, — вдруге писати не буде. Або кине, або надішле щось гірше. Збереження стану форми при провальній валідації — невелика клієнтська робота, яка напряму захищає якість того, що реально потрапляє в інбокс.
Типові помилки, які ми бачимо
Кілька патернів повторюються в contact- і careers-формах незалежно від індустрії:
- Валідація лише на клієнті. Все, що перевіряється тільки в браузері, — це порада, не правило: обійти прямим запитом до API тривіально, і саме так роблять спам-скрипти.
- Одне поле відповідає одразу за згоду, marketing opt-in і прийняття умов. Швидше зробити — і неможливо захистити, якщо хтось запитає, на що людина насправді погодилась.
- Немає rate limiting на API-ендпоінті, навіть коли сама форма має антиспам. Захист форми не працює, якщо ендпоінт приймає прямі запити.
- Success і error виглядають однаково, або загальне «Something went wrong» без підказки, що виправити, — і одна невдала маска телефону перетворюється на кинутий сабміт.
Окремо кожну з цих проблем полагодити нескладно. Вони накопичуються, бо форму контакту зазвичай роблять за пʼять хвилин наприкінці білда, хоча насправді це невеликий шматок продукту зі своїми вимогами до security, legal і конверсії.
Що ми свідомо пропускаємо
Не кожен захист вартий того, щоб додавати його за замовчуванням, — і це реальне рішення, не лінь. Ми не ставимо CAPTCHA з першого дня — це ціна, яку платить кожен легітимний відвідувач за проблему, яку ще навіть не виміряли. Ми не вимагаємо більше верифікації особи, ніж потрібно флоу: careers-форма не потребує коду підтвердження номера телефону для відсіювання спаму, коли honeypot і rate limiting роблять це безкоштовно. Правило просте: додавайте шар, коли дані показують аб'юз, а не заздалегідь, і обирайте той шар, що коштує аб'юзеру дорожче, ніж реальному кандидату.
Міряйте completion, не лише відкриття
Трекайте:
- старт форми → успішний сабміт
- відвал по кроках (мультистеп)
- spam reject vs human reject
Якщо completion падає після нового поля — у поля має бути бізнес-власник, або його треба прибрати. Це той зворотний зв'язок, який тримає форму чесною з часом. Форми зазвичай ростуть по одному полю за раз, кожне додавання окремо виглядає розумним, кожне зʼїдає відсоток completion, якого ніхто не помічає, бо ніхто не стежить за трендом. Розділення spam reject і human reject теж важливе саме по собі: сплеск human-помилок валідації зазвичай означає, що форма вимагає щось у неправильному форматі, а сплеск spam reject — що антиаб'юз-шар працює як задумано.
Stereasoft збирає contact і careers intake з Resend-сповіщеннями й переглядом у Supabase-адмінці. Мета проста: серйозні люди проходять; інбокс у понеділок лишається живим.
