Проверка данных регулярными выражениями в JavaScript: практические шаблоны и ограничения

JS / Безопасность

Проверка данных регулярными выражениями в JavaScript: практические шаблоны и ограничения

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

Опубликовано 04.09.2026 5 минут чтения
Содержание статьи

Регулярное выражение хорошо проверяет форму строки: допустимые символы, длину, префикс или разделители. Оно не подтверждает, что email существует, дата реальна, а пользователь имеет право выполнить действие.

Надёжная валидация обычно состоит из нескольких шагов:

  1. убедиться, что получено значение ожидаемого типа;
  2. нормализовать его только по понятным правилам;
  3. проверить длину и формат;
  4. проверить бизнес-ограничения;
  5. повторить все проверки на сервере.

Закрепляйте проверку на всей строке

JavaScript
const codePattern = /^WEB-\d{4}$/;​console.log(codePattern.test('WEB-2048'));   // trueconsole.log(codePattern.test('XWEB-2048Y')); // false​

Без ^ и $ шаблон нашёл бы подходящий фрагмент внутри неверной строки.

Проверка имени пользователя

Допустим, продукт разрешает буквы любых языков, цифры, _ и -, а длина должна быть от 3 до 24 символов:

JavaScript
const usernamePattern = /^[\p{L}\p{N}_-]{3,24}$/u;​function validateUsername(value) {  if (typeof value !== 'string') {    return 'Имя должно быть строкой.';  }​  const username = value.trim();​  if (!usernamePattern.test(username)) {    return 'Используйте 3–24 буквы, цифры, дефис или подчёркивание.';  }​  return null;}​

Флаг u нужен для Unicode property escapes. Решение о допустимых символах — продуктовый allowlist, а не универсальное правило для всех сайтов.

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

Артикул с именованными группами

JavaScript
const skuPattern = /^(?<category>[A-Z]{3})-(?<year>20\d{2})-(?<number>\d{5})$/;​function parseSku(value) {  const match = value.match(skuPattern);​  if (!match?.groups) {    return null;  }​  return {    category: match.groups.category,    year: Number(match.groups.year),    number: match.groups.number,  };}​

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

Email: не пытайтесь воспроизвести весь стандарт

Слишком строгий самодельный шаблон отклоняет действительные адреса, а очень сложный всё равно не доказывает существование ящика. На клиенте начните с HTML:

HTML
<label for="email">Email</label><input id="email" name="email" type="email" maxlength="254" autocomplete="email" required>​

Если JavaScript нужен для немедленной обратной связи, используйте встроенную проверку:

JavaScript
const email = document.querySelector('#email');​email?.addEventListener('input', () => {  email.setCustomValidity('');​  if (email.validity.typeMismatch) {    email.setCustomValidity('Введите email в формате name@example.com.');  }});​

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

Телефон: сначала нормализация, затем проверка

Форматы телефонов зависят от страны и бизнес-требований. Для российского номера можно принять распространённые разделители, удалить их и привести ведущую 8 к коду страны:

JavaScript
function normalizeRussianPhone(value) {  if (typeof value !== 'string') {    return null;  }​  const digits = value.replace(/\D/g, '');  const normalized = digits.startsWith('8') && digits.length === 11    ? `7${digits.slice(1)}`    : digits;​  return /^7\d{10}$/.test(normalized)    ? `+${normalized}`    : null;}​console.log(normalizeRussianPhone('+7 (999) 123-45-67')); // '+79991234567'​

Это продуктовый пример, а не международный валидатор. Для нескольких стран понадобится библиотека или серверный сервис с актуальными метаданными нумерации.

URL лучше разбирать через URL

JavaScript
function parseHttpUrl(value) {  try {    const url = new URL(value);​    if (!['http:', 'https:'].includes(url.protocol)) {      return null;    }​    return url;  } catch {    return null;  }}​

Regex для полного URL быстро становится хрупким. Даже синтаксически правильный URL нельзя автоматически считать безопасным: при серверной загрузке дополнительно защищайтесь от SSRF, запрещайте внутренние адреса и следуйте allowlist.

Дата: формат и календарь — разные проверки

JavaScript
const datePattern = /^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/;​function isCalendarDate(value) {  const match = value.match(datePattern);​  if (!match?.groups) {    return false;  }​  const year = Number(match.groups.year);  const month = Number(match.groups.month);  const day = Number(match.groups.day);  const date = new Date(Date.UTC(year, month - 1, day));​  return date.getUTCFullYear() === year    && date.getUTCMonth() === month - 1    && date.getUTCDate() === day;}​

Одного \d{4}-\d{2}-\d{2} недостаточно: оно принимает 2026-99-99.

Динамический шаблон

Если нужно искать буквальный пользовательский текст, экранируйте метасимволы:

JavaScript
function escapeRegExp(value) {  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');}​const query = 'file.js';const pattern = new RegExp(escapeRegExp(query), 'i');​console.log(pattern.test('Open FILE.JS')); // true​

Не позволяйте недоверенной строке становиться произвольным regex. Сложный шаблон способен потреблять много процессорного времени.

Опасность катастрофического backtracking

Выражения с вложенными неоднозначными квантификаторами могут работать экспоненциально:

JavaScript
// Опасная форма для длинной строки без завершающего X.const riskyPattern = /^(a+)+X$/;​

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

Состояние флагов g и y

Объект RegExp с g или y меняет lastIndex при test():

JavaScript
const pattern = /^WEB/g;​console.log(pattern.test('WEB-1')); // trueconsole.log(pattern.test('WEB-1')); // false​

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

Что проверить тестами

Для каждого правила подготовьте:

  • минимальную и максимальную допустимую длину;
  • пустую строку, null, массив и число;
  • символы разных алфавитов;
  • значение на один символ длиннее лимита;
  • почти совпадающую длинную строку;
  • пробелы по краям и внутри;
  • недопустимый протокол или диапазон;
  • нормальный рабочий пример.

Что запомнить

Regex проверяет синтаксическую форму, а не истинность данных. Закрепляйте шаблон, ограничивайте вход, учитывайте Unicode и не используйте g для валидации. Email, URL, даты и телефоны требуют дополнительных механизмов, а клиентская проверка всегда повторяется на сервере.

Сообщить об ошибке в статье

Рекомендации редакции

Другие публикации из этого раздела.

Новые материалы

Получайте практические разборы без информационного шума

Одна общая рассылка о новых статьях и инструментах. Адрес будет активироваться только после подтверждения.

Подписка появится после подключения сервиса рассылок и Yandex SmartCaptcha.