Перейти к основному содержимому
megapromotingДавайте обсудим

Экспертиза · Веб-сайты

Этот сайт — наша техническая спецификация, включая измерения, которые нам не на пользу.

Мы строим сайты, отрисовываемые на сервере, с изображениями в AVIF и WebP, проверенными извне заголовками безопасности, картой сайта, генерируемой из данных, и проверкой приёмки, запускаемой после каждой публикации. Всё это можно проверить на странице, которую вы читаете.

Уже построеноNu putem cita un site de client fără acordul lui, dar nu avem nevoie: dovada e chiar pagina asta. Rulează Next.js 16.2.4 cu React 19.1.0, e servită prerandat din cache (`x-nextjs-prerender: 1`, `x-nextjs-cache: HIT`), poartă HSTS cu `preload`, `nosniff`, `SAMEORIGIN`, `Referrer-Policy` și o politică de conținut completă — verificate cu `curl -I` pe 06.09.2026. Harta de site conține 147 de adrese, toate generate din date, nu scrise de mână. A doua implementare, în același depozit: infrastructura formularului — jurnal append-only, sondă publică de sănătate, limitare de rată — și scriptul de acceptare rulat după publicare.

Сайт-визитка имеет только один честный тест: можете ли вы извне проверить то, что компания о нём говорит? Поэтому эта страница перечисляет не технологии, а вещи, которые вы можете самостоятельно проверить командой. Сайт работает на Next.js 16.2.4 и React 19.1.0, а страницы prerendered при сборке и выдаются из cache — это видно в заголовках ответа: `x-nextjs-prerender: 1`, `x-nextjs-cache: HIT`, `s-maxage=31536000`, с окном в 300 секунд, в течение которого старая страница ещё может быть отдана, пока обновляется.

Server-side rendering — не архитектурное предпочтение. Посетитель со слабым соединением и поисковый робот получают один и тот же полный HTML, не ожидая JavaScript. Сверху добавляется дисциплина ресурсов: изображения выходят в AVIF и WebP, с cache на один год; шрифты размещаются у нас, а не запрашиваются у внешнего сервиса; восемь библиотек интерфейса имеют оптимизированные import, чтобы не тянуть целые модули ради одной иконки.

Заголовки безопасности отправляются из двух мест и проверяются в реальном ответе, а не только в конфигурации: принудительный защищённый transport на два года с поддоменами и включением в preload list, запрет угадывания типа файла, ограничение встраивания в чужие frames, policy referrer и policy permissions, которая закрывает камеру и оставляет микрофон только для собственного origin — потому что голосовые демонстрации требуют его, а без этого исключения браузер молча отказывает.

Последняя часть — та, которая отличает нас, и она неприятна: мы публикуем и то, что у нас измерено плохо. Сервер пока не поддерживает Brotli и HTTP/2. Каталог переводов передаётся при каждом переходе и стоит примерно 70 KB compressed каждый раз. Пакет 443 KB содержит четыре каталога языков, из которых routable только один. Шрифты также приносят кириллический subset, хотя проверенные страницы не содержат ни одного кириллического символа. Эти цифры измерены, записаны в репозитории и в списке исправлений. Поставщик, у которого нет таких цифр о собственном сайте, либо не измерял, либо не хочет говорить.

Что входит

Работа, по составляющим

Server-side rendering, выдаваемый из cache

Страницы prerendered при сборке и выдаются как полный HTML, а не строятся в браузере. В реальном ответе присутствуют `x-nextjs-prerender: 1` и `x-nextjs-cache: HIT`, с `cache-control: s-maxage=31536000` и окном в 300 секунд, в течение которого старая версия ещё может быть отдана, пока обновляется. Практический эффект: посетитель со слабым соединением и crawler получают один и тот же контент в одном и том же запросе.

Изображения и шрифты с бюджетом, а не на удачу

Настроенные форматы — AVIF и WebP, с минимальным временем cache 31.536.000 секунд — один год — и заголовком `immutable` для static файлов, изображений и шрифтов. Шрифты размещаются у нас при сборке, а не запрашиваются у внешнего сервиса при каждом визите, и отображаются с `swap`, чтобы текст появился до загрузки шрифта. Есть и batch-скрипт сжатия, с quality 80 для WebP и 75 для AVIF, чтобы source не зависел от того, что кто-то экспортировал из графического редактора.

Заголовки безопасности отправляются из двух мест и проверяются в ответе

Из приложения: принудительно безопасная передача 63.072.000 секунд с поддоменами и включением в список предварительной загрузки, `X-Content-Type-Options: nosniff`, `X-Frame-Options: SAMEORIGIN`, `Referrer-Policy: strict-origin-when-cross-origin` и политика разрешений, которая закрывает камеру, оставляет микрофон только для собственного происхождения и отключает группировку по интересам. От веб-сервера, отдельно: полная политика безопасности контента с явным списком разрешённых источников. При передаче явно указывается, что мест два — иначе следующий, кто будет что-то менять, будет искать не там.

Карта сайта, сгенерированная из данных, а не написанная вручную

Карта сайта строится из пяти источников — 13 фиксированных адресов, статей, 19 продуктов, 12 услуг и 100 комбинаций услуга × домен — и проходит финальную дедупликацию. Результат: 147 адресов, подсчитанных и в коде, и в файле, который отдаётся в production. Новая страница входит автоматически; удалённая исчезает. Никто не ведёт параллельный список, который рассинхронизируется через три недели.

Старые адреса не умирают: 43 постоянных перенаправления

Структурная чистка оставляет после себя адреса, которые кто-то сохранил или опубликовал. В конфигурации есть 43 постоянных перенаправления — 9 руководств в блог, 20 осиротевших статей, 6 перенаправленных кейсов и 8 отдельных страниц. Есть и 11 страниц, которые перенаправляют изнутри приложения; они корректны функционально, но дороже, чем правило в конфигурации, и так и указаны в списке исправлений.

Форма — это инфраструктура, а не декор

Запрос записывается в журнал append-only до попытки доставки, со второй строкой для результата; есть публичная сетевая проверка состояния, которая показывает, может ли лид прямо сейчас дойти до человека; а запросы к интерфейсам ограничены 10 в минуту, с ключом, взятым из последнего хопа заголовка перенаправленных адресов, а не из первого — первый отправляет клиент. Причины записаны в коде, а не в отдельной документации.

Проверка приёмки после каждой публикации

Скрипт открывает каждую страницу продукта, группами по три, и падает, если отсутствует код 200, якорь вопросов, якорь примера или канонический адрес. Затем он проверяет карту сайта — чтобы в ней был каждый продукт и не было языковых вариантов, которые не открываются — и `robots.txt`, чтобы он не блокировал ресурсы, необходимые для рендеринга. Каждая проверка соответствует тому, что уже однажды ломалось.

Канонический адрес, решаемый один раз, для всего сайта

Канонический адрес объявлен относительно, в одном месте базовой структуры, и решается для каждого маршрута отдельно. То же решение для адреса социальной публикации, который раньше на большинстве страниц указывал на главную. Это строка кода, которая заменяет конвенцию, которую каждая новая страница должна была бы помнить сама — и которую, неизбежно, не помнит.

То, что не работает, записано в репозитории, рядом с кодом

Репозиторий содержит файл известных дефектов, который объясняет, например, почему языковые варианты в адресе не переключают язык, почему очевидное решение делает всё хуже — при серверном рендеринге нет ни local storage, ни браузера, поэтому детектор падает на английский и все страницы выходят на английском — и какие два реальных решения существуют. Мы предпочитаем, чтобы технический покупатель нашёл ограничение, записанное нами, а не обнаруженное им.

Как это выглядит

Путь, шаг за шагом.

01

Инвентаризация адресов и что происходит со старыми

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

02

Структура контента и источники, из которых она генерируется

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

03

Строительство с бюджетами, установленными до содержания

Мы с самого начала определяем, какой формат у изображений, что кэшируется и в каком объёме, что рендерится на сервере и что остаётся интерактивным в браузере. Затем содержание укладывается в этот бюджет. Обратный порядок приводит к сайтам, которые хорошо выглядят на запуске и утяжеляются с каждой добавленной страницей.

04

Публикация с воротами и проверкой приёмки

Публикация проверяет свободную память сервера, запускает аудит безопасности зависимостей, собирает из чистого состояния и подтверждает, что процесс запущен, прежде чем сообщить об успехе. Сразу после этого скрипт приёмки открывает страницы и проверяет якоря, канонические адреса, карту сайта и `robots.txt`. Оба передаются, у нас не остаются.

05

Передача, со списком недостающего и измерений

Мы передаём код, процедуру публикации, процедуру отката, адреса состояния — и письменный список того, что не покрыто, с цифрами. Для этого сайта список включает отсутствие Brotli и HTTP/2 на сервере, стоимость каталога переводов при каждой навигации и неиспользуемое подмножество шрифтов. Мы предпочитаем, чтобы список исходил от нас.

Patru straturi verificabile din exterior1HTML prerandat servit din cache2imagini AVIF/WebP și fonturi găzduite local3antete de securitate din aplicație și din serverul web4harta de site și adresele canonice generate din date
Patru straturi verificabile din exterior

Данные

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

Вопросы, которые задаёт всякий, у кого есть ответственный за защиту данных, — заданы здесь раньше, чем их задаст он.

Данные из формы контакта
Имя, адрес электронной почты, телефон, компания, выбранная услуга, сообщение, страница происхождения и сетевой префикс посетителя. Они записываются в журнал на собственном сервере, с правами `0700` на каталог и `0600` на файлы, вне каталога версии. Срок: 24 месяца, применяется кодом при каждой записи, а не планировщиком, о котором можно забыть.
IP-адрес, обрезанный до записи
Сохраняется только сетевой префикс — `/24` для IPv4, `/48` для IPv6 — и читается последний hop из заголовка, потому что первый отправляется клиентом и ничего не доказывает. Та же функция используется и для ограничения скорости, поэтому нет второго места, куда полный адрес мог бы случайно попасть.
Статистика трафика
Измерение проходит через настроенный инструмент аналитики и активируется после согласия. Реальные cookie были эмпирически проверены на живом сайте, до и после принятия, а политика cookie была переписана после этой проверки — не наоборот. Предыдущая политика перечисляла cookie, которых не существовало.
Реестр обработок, версионируемый рядом с кодом
Для каждого типа данных, с которым работает сайт, есть запись с целью, основанием, категориями и сроком в документе, хранящемся в том же репозитории. Когда код меняет срок, документ меняется в том же коммите. То, что не установлено — например, реальное хранение в аналитическом аккаунте — явно помечается как элемент, который нужно уточнить, а не заявляется как факт.
Что не выходит с сервера
Токены уведомлений, ключи поставщика и идентификаторы канала. Состояние health probe возвращает исключительно логические значения, именно чтобы его можно было запрашивать внешней службой, не раскрывая ничего о конфигурации.

Случай

Что показывает аудит собственного сайта, когда вы публикуете его целиком

Ситуация

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

Что мы построили

Мы прошли по каждому известному адресу прямыми запросами — 249 адресов, 23 столбца на адрес, без отслеживания редиректов, максимум шесть запросов параллельно — и положили результат в версионный табличный файл. Поверх него мы запустили отдельные проверки: реальное сжатие HTML, что передаётся при каждой навигации, что содержат пакеты JavaScript, какие шрифты загружаются, какие заголовки кэша и транспорта отдаёт сервер. Каждая цифра была проверена в реальном ответе, а не выведена из конфигурации.

Что получилось

244 адреса отвечают 200, а 5 перенаправляют. У 131 страницы не было канонического адреса, а 219 из 244 объявляли главную страницу адресом для шаринга — оба случая теперь исправлены одной строкой в базовой конфигурации, которая применяется к каждому маршруту. Сервер не предоставляет Brotli и HTTP/2. Каталог переводов, 2.268 ключей, передаётся при каждой навигации и стоит примерно 70 KB в сжатом виде каждый раз. Пакет размером 443 KB содержит четыре каталога языков, из которых только один маршрутизируемый. Шрифты включают и кириллический поднабор, хотя проверенные страницы не содержат ни одного кириллического символа. Все цифры вместе с командами, которые их воспроизводят, записаны в репозитории.

Чего этот случай не говорит

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

Вопросы

О чём нас спрашивают перед тем, как позвонить

На чём вы, конкретно, строите?

Next.js 16.2.4 с React 19.1.0 и TypeScript, с Tailwind для стилей и доступных интерфейсных компонентов на основе примитивов Radix. Страницы предварительно рендерятся при сборке и отдаются из кэша — это проверяется извне: заголовки ответа содержат `x-nextjs-prerender: 1` и `x-nextjs-cache: HIT`. Это не список технологий, который мы прочитали; это стек, на котором работает страница, которую вы читаете сейчас.

Как мне самому проверить, что вы говорите о безопасности?

Одной командой: `curl -I https://www.megapromoting.com/servicii`. Вы увидите `strict-transport-security: max-age=63072000; includeSubDomains; preload`, `x-content-type-options: nosniff`, `x-frame-options: SAMEORIGIN`, `referrer-policy: strict-origin-when-cross-origin`, политику разрешений, которая закрывает камеру и оставляет микрофон только для собственного origin, а также политику безопасности контента со списком явных источников. Первые пять идут из конфигурации приложения; последняя отправляется веб-сервером — два разных места, и мы так и пишем в передаче.

Откуда вы знаете, что не сломали что-то при последней публикации?

Из скрипта приёмки, запущенного после публикации. Он открывает каждую страницу продукта и падает, если отсутствует код 200, якорь вопросов, якорь примера или канонический адрес; затем проверяет, что карта сайта содержит каждый продукт и не содержит языковых вариантов, которые не открываются, и что `robots.txt` не блокирует ресурсы рендеринга. Каждая проверка из списка соответствует тому, что уже ломалось. Скрипт передаётся вместе с проектом — его можете запускать и вы.

Ваш сайт быстрый?

Это зависит от того, что измерять, и у нас есть цифры в обе стороны. Хорошее: страницы предварительно рендерятся и отдаются из кэша, изображения выходят в AVIF и WebP с кэшем на один год, шрифты размещены локально. Плохое, измеренное на собственном сайте: сервер не предоставляет Brotli и HTTP/2 — запрос с `Accept-Encoding: br` получает несжатый ответ; каталог переводов передаётся при каждой навигации и стоит примерно 70 KB в сжатом виде каждый раз; пакет размером 443 KB содержит четыре каталога языков, из которых только один маршрутизируемый. Я не даю вам лабораторную оценку; я даю измеренные байты и список исправлений, который из них следует.

Кто поддерживает карту сайта?

Никто, и это правильный ответ. Она генерируется из пяти источников данных — фиксированных адресов, статей, продуктов, услуг и комбинаций услуга × домен — и проходит финальную дедупликацию. Получается 147 адресов, та же цифра в коде и в файле, который отдаётся в production. Новая страница входит автоматически. Чего сегодня не хватает, если говорить прямо: записи не содержат даты последнего изменения, а 93 индексируемых адреса ещё не находятся в карте. Они в списке исправлений, а не скрыты.

Вы делаете многоязычные сайты?

Здесь нужно быть точным, потому что это самое чёткое ограничение этого сайта. Переключение языка из адреса не работает: конфигурация фиксирует румынский, поэтому детектор никогда не читает параметр. Я записал в репозитории, почему очевидное решение хуже — при серверном рендеринге нет ни локального хранилища, ни браузера, поэтому детектор падает на английский и все страницы выходят на английском — и какие есть два реальных решения: маршруты по пути, с языковым сегментом, или определение языка на сервере на каждой странице. Первое рекомендуется. Для нового проекта мы сразу начинаем с маршрутов по пути; для существующего это работа, а не настройка.

Что происходит со старыми адресами, когда вы переделываете сайт?

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

Как я узнаю, что форма действительно отправляет?

Вы спрашиваете его: `GET /api/health/contact` отвечает 200, если лид может сейчас дойти до человека, и 503, если нет, отдельно проверяя, что токен канала уведомлений действителен и что журнал запросов можно записать. Ответ содержит только логические значения, никогда не токен. Он существует потому, что именно здесь однажды всё сломалось: отозванный токен, маршрут, возвращавший 500, потерянные без следа запросы. Теперь запрос записывается в журнал до попытки доставки.

Чего вы не делаете?

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

На чём основаны утверждения выше (15 источников)
  1. Producția servește pagini prerandate din cache: `x-nextjs-prerender: 1`, `x-nextjs-cache: HIT`, `cache-control: s-maxage=31536000`, `x-nextjs-stale-time: 300`https://www.megapromoting.com/servicii · 2026-09-06
  2. Antete de securitate în răspunsul real: HSTS `max-age=63072000; includeSubDomains; preload`, `nosniff`, `SAMEORIGIN`, `strict-origin-when-cross-origin`, politică de permisiuni cu `microphone=(self)`; plus o politică de securitate a conținutului completă servită de nginx și ABSENTĂ din `next.config.ts`https://www.megapromoting.com/servicii · 2026-09-06

13 из них — код и файлы из наших репозиториев. Мы не публикуем их название и строку: вместе, на одной странице, они слишком точно описали бы, как устроены системы, которые принадлежат не только нам. Мы разбираем их вместе с тобой, в репозитории, по запросу — проверка остаётся возможной, просто она проходит в разговоре.

Что вы хотели бы наладить?

Расскажите о своём процессе. Вместе определим, что стоит построить, что можно подключить и как проверим результат.

Давайте обсудим