Files
easy-png-tools2/docs/plan-seo.md
T
2026-10-12 17:32:16 +05:00

482 lines
43 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# План: SEO + GEO — индексация и конкуренция в image tools
> **Статус: active.** Адреса страниц инструментов — `/tools/<slug>` (`crop-png`
> и подобные), каталог — `/list-tools`; реестр двухслойный: страница `slug`
> ссылается на инструмент по `id` (`docs/architecture.md`, раздел 3).
>
> **`noindex, nofollow` стоит на странице инструмента без текста**
> (`web/src/routes/tools/[slug]/+page.svelte`) и снимается автоматически, как
> только у страницы появляется EN-`seo`-блок: индексность следует за контентом
> (S1h, §4).
>
> **Цель:** страница каждого инструмента — индексируемый лендинг под конкретный
> интент («crop png», «make png transparent», «jpg to png»…) плюс попадание в
> ответы генеративных систем (ChatGPT, Perplexity, AI Overviews) — GEO.
>
> **Позиционирование:** не «PNG-инструменты», а **image tools с PNG-ядром** —
> любой инструмент принимает PNG/JPG/WebP/GIF/BMP и отдаёт результат в
> PNG/JPG/WebP/BMP через селектор в кнопке Download. Дифференциатор —
> прозрачность/альфа/фон и no-upload-приватность. Референс-конкурент по ядру —
> onlinepngtools.com (§2). Долгосрочно ядро переезжает на wasm и обрастает API +
> PWA (`docs/plan-platform.md`); для SEO это аргументы дистрибуции и GEO-факты,
> схему сайта они не меняют.
>
> **Слаги в этом документе:** `jpg-to-png`, `crop-jpeg` и подобное — интенты и
> URL конкурента, а не наши страницы. Наши страницы — в `docs/tools-map.md`
> (карта перечисляет `slug`, не `id`); в тексте ниже названия инструментов
> приводятся по этим слагам, потому что для SEO важны адреса.
## 0. Решения
- **Бренд: `easy-image-tools`, домен `easy-image-tools.com`.** Переименование до
индексации выполнено (R1): хардкод убран, точка истины — константа `brand` в
i18n + README (хардкод в компонентах недопустим). Домен зарегистрирован
2026-10-11 (R2); остался перевод прода на него (DNS, HTTPS, снятие
`BASE_PATH`, `PUBLIC_SITE_ORIGIN`, 301 со старого GitHub Pages) — теперь это
блокер S1-domain (§4). Легенда честная: «started as PNG tools, now all
formats» — это контент-плюс, а не баг. Адреса страниц (`/tools/crop-png`) и
localStorage-ключи не переименовывать (localStorage-ключи бренда не содержат).
- **Форматы: формат = выбор в UI, не отдельные страницы.** Download-кнопка
каждого инструмента — с селектором PNG/JPG/WebP/BMP (+quality для lossy). Райз
«выбор формата/качества в Download» вытащен из backlog №7 в S1f: механика уже
есть (`Tool.output`), нужна как продуктовая основа «image
tools»-позиционирования. В направлении «из формата в PNG» отдельных
инструментов нет (загрузка принимает любой формат) — см. §5.
- **Адреса страниц `-png` сохраняются, внутренние имена — без.** Инструмент
работает с любым форматом, поэтому его `id` не должен врать (`crop`, а не
`crop-png`); png-интент держат адрес страницы и имя скачиваемого файла.
**Реализовано:** реестр разделён на инструменты (`registry/tools/`, уникальный
`id`) и страницы (`registry/pages/`, уникальный `slug`), роут —
`tools/[slug]/`; модель — `docs/architecture.md`, раздел 3. **Следствие для
SEO: редиректы не понадобились, canonical и sitemap не меняются, адреса те
же** — поэтому это была безопасная правка до индексации, а не после. Побочная
выгода: широкие интенты («crop image online») переносятся теми же страницами
контентом — H1 «Crop image», текст «works with PNG, JPG, WebP»; URL-алиасы без
`-png` по-прежнему не создаются (канонический беспорядок). Побочный эффект
модели: страница может ссылаться на один инструмент, а инструмент —
обслуживать много страниц, поэтому следующие шаги (свести convert-страницы на
один `convert`, селектор формата в Download — S1f) не требуют новой миграции
реестра. Задача на консолидацию — `docs/backlog.md`.
- **Индексируемая локаль — EN** (базовая; конкурент англоязычный). RU-версия —
отдельно и позже: префикс `/ru/`, prerender обеих локалей, hreflang
(сознательно отложено). Пока язык переключается на клиенте (localStorage) —
для SEO не учитываем.
- **Прод-домен без `BASE_PATH`.** Сборка с базой (GitHub Pages `/repo/`, см.
`BASE_PATH` в `web/vite.config.ts` и `deploy.yml`) ломает абсолютные URL,
canonical и «корневость» страниц. Свой домен + корень. Текущий деплой — GitHub
Pages (`.github/workflows/deploy.yml`); перенос на свой домен выполняется
сменой origin в CI и `PUBLIC_SITE_ORIGIN`.
- **URL-схема инструментов:** короткие плоские адреса — `/tools/<slug>` для
страницы, `/list-tools` для каталога, где `<slug>` берётся из записи
`registry/pages/`. Схему не менять без отдельного SEO-плана: после индексации
переименование URL означает 301 и потерю позиций. Разделение инструмента и
страницы (решение выше) адресов не меняет.
- **Аналитика — GA4, в режиме «только агрегаты».** GA4 уже стоит в проде
(`web/src/app.html`, prerender-оболочка, значит на каждой странице). Решение —
не выпиливать, а ограничить: cookieless-конфигурация, без рекламных audiences,
signals и ремаркетинга. Граница приватности проходит по данным изображения: GA
не видит ни файла, ни его содержимого, ни результата инструмента — только факт
открытия страницы. **Рекламу и ремаркетинг не ставим** — они противоречат
позиционированию, в отличие от счётчика посещений. Формулировку «без трекинга»
на публичных страницах не использовать: она теперь неточна, см. §1 и S4b.
- **Три разных артефакта на странице, три разных источника.** `<title>` для
выдачи (`seo.metaTitle`, ~55–60 символов **без префикса бренда** — бренд не
приносит собственных поисковых запросов и съедает треть выдачи),
`meta description` (`seo.metaDescription`, ~150–160 символов) и H1
(`pages.<slug>.title`). **«free / no upload / нет лимитов» не идут в H1:**
Google не вознаграждает ключи в заголовке, переписывает title примерно в 60%
случаев, а список выгод в H1 читается как сборка ключей — эти обещания
работают в description (влияет на кликабельность) и в первом абзаце. **Форматы
в H1 не перечисляем:** широкие интенты («crop jpg») закрывает текст,
description и FAQ — это же запрещено §5 как doorway-подход. Реестр не трогаем:
`title` в нём остаётся для каталога и цепочек, SEO-заголовки живут в словаре.
- **Честные методы.** Никакого клонированного текста, дорвеев, PBN. Контент — по
каждому инструменту, с ручной вычиткой.
## 1. Аудит (2026-09-09)
Фундамент уже есть:
- SvelteKit + `adapter-static`, `prerender = true` на весь сайт
(`web/src/routes/+layout.ts`) — весь контент в статичном HTML, ботам не нужен
JS. Это главный технический козырь.
- Страницы в `web/src/lib/registry/pages` со «SEO-шными» slug (`crop-png`,
`add-border-png`, …) — URL уже повторяют паттерн конкурента, при этом
внутренние имена инструментов png-интента не держат. Актуальный состав:
`docs/tools-map.md`.
- На корневых страницах инструментов есть базовые `<title>` и `robots`; полный
SEO-слой (canonical, OG, content и JSON-LD) остаётся S1/S2.
- Каталог со ссылками на все инструменты (внутренняя перелинковка) и карта
инструментов против конкурента: `docs/tools-map.md`.
- EN-тексты в реестре + i18n-словари ru/en.
Чего нет на момент аудита (закрыто этапами §4 — смотри чекбоксы там, здесь
состояние не дублируется):
| Пробел | Где тогда | Этап |
| -------------------------------------------------- | ----------------------------------- | ---- |
| `noindex` на страницах инструментов | стоит (`+page.svelte`) | S1 |
| `sitemap.xml` | нет | S1 |
| `robots.txt` без `Sitemap:` | allow-all (`web/static/robots.txt`) | S1 |
| `rel=canonical` | нет | S1 |
| OG/Twitter-теги, OG-картинки | нет | S1 |
| Канонический домен в абсолютных URL | нет, деплой на GitHub Pages с базой | S1 |
| Контент страницы (how-to, FAQ, related) | title, robots и отрендеренный H1 | S2 |
| JSON-LD (SoftwareApplication, FAQPage, Breadcrumb) | нет | S3 |
| `llms.txt`, хабы категорий с текстом | нет | S3 |
| GSC + доводка GA до cookieless-конфигурации | GA4 стоит, GSC не подключён | S4 |
Строка `noindex` — первая не потому, что её сложнее всего, а потому, что она
блокирует всё остальное: пока тег стоит, остальные строки таблицы не дают
трафика. Подробности и условие снятия — §4, S1.
## 2. Конкурент (onlinepngtools.com)
Снято с живого сайта (2026-09-09):
- ~311 URL в sitemap, все плоские (`/crop-png`, `/create-transparent-png`);
title вида «Crop a PNG – Online PNG Maker».
- Страница инструмента: h1 + 2–3 абзаца («World's simplest…», import/crop
примеры), примеры «before/after», how-to-блок, огромные футерные перелинковки
(серии Logo*/Icon*/Stamp*), страницы-хабы, price-wall.
- Сила: возраст домена, ссылочная масса, бренд, охват, серии-клоны под длинные
хвосты.
- Слабость (наш контраргумент): реклама, ежедневные лимиты free-плана, платное
коммерческое использование, апселл-модалки, страницы по ~450 КБ.
- Наш ответ в контенте каждой страницы: **no upload (всё локально в браузере),
no signup, no limits, бесплатно в т.ч. для коммерческого использования, open
source**.
## 3. Стратегия
1. **Не лобовая, а ядро интентов.** 80% объёма поиска приносят ~30–50 запросов.
Покрытие инструментов у нас почти полное (список — `docs/tools-map.md`);
приоритет — контент и качество страниц ядра, затем хвосты.
2. **Страница = лендинг, а не «пустая коробка».** Шаблон контента: H1 → 1–2
абзаца «что делает» → как использовать (шаги) → параметры → FAQ (3–5 вопросов
уровня People-Also-Ask) → related tools по категории.
3. **CWV как измеримое преимущество.** Лёгкие быстрые страницы против ~450-КБ
страниц конкурента; после редизайна не потерять скорость.
4. **GEO.** LLM-системы цитируют страницы с прямыми определениями, списками,
таблицами и уникальными фактами. Всё это уже есть в данных реестра — отдавать
в HTML-структуре, без воды.
5. **Информационный контур — потом, не вместо.** Гайды по форматам (PNG/JPEG/
WebP: история, модификации, lossy/lossless, прозрачность, «png vs jpeg») дают
E-E-A-T, перехватывают сравнительные запросы и служат хабами внутренних
ссылок на инструменты. Но это этап S3e и только после того, как написано ядро
S2: транзакционная страница приносит конверсию, информационная — только
трафик. Тонкие FAQ-страницы «про всё» не заводить — несколько сильных
материалов с ссылками на несколько связанных инструментов.
6. **Дистрибуция.** GitHub-репо (звёзды/форки = сигналы), alternative.to,
Product Hunt, Reddit по интенту — реальный путь набрать первые бэклинки без
бюджетов.
## 4. Этапы
### S1. Технический минимум (закрывает блокер индексации)
Разбито на две группы: **domain-free** (делаем параллельно с покупкой домена) и
**domain-required** (после покупки и DNS-настройки).
- [x] **[S1h]** Robots-тег сделан **условным, а не снятым**: `index, follow`
ставится тогда и только тогда, когда у страницы есть EN-`seo`-блок, иначе
`noindex, nofollow`. Единого места правки нет — индексность следует за
контентом, поэтому страницу нельзя забыть при написании текста и нельзя
проиндексировать с заготовкой. Тот же признак выкидывает noindex-страницы
из sitemap, так что список в sitemap и набор индексируемых страниц не
разойдутся. **Страховка от «сломать руками»:** контентный гейт в
`web/src/lib/i18n/schema-tool-strings.test.ts` — страница с текстом
обязана иметь ≥250 слов, ≥3 шага how-to, ≥3 FAQ и title/description в
границах SERP, иначе тест падает до индексации. **Осталось:** снять
`noindex` совсем, когда текст появится у всех страниц, которые имеет смысл
индексировать.
#### S1-free: без домена
- [ ] **[S1e]** OG-картинки per-tool: генерация PNG-карточек скриптом на
существующем ядре (мы сами умеем рисовать PNG) — имя инструмента + бренд,
файл `static/og/[slug].png`, ссылка из `Seo.svelte`. Путь к картинке
строить через `resolve()` из `$app/paths` (как favicon в
`+layout.svelte`), а не литералом `/og/…`: под `BASE_PATH` корневой путь
ломается. Домен для этого не нужен; при деплое на собственном домене
OG-картинки будут абсолютными и корректными.
- [x] **[S1f]** Download-кнопка с селектором формата PNG/JPG/WebP/BMP (+quality
для lossy) для всех инструментов — механика `Tool.output` уже есть
(`docs/backlog.md`, «Идеи» №6, UX-райз «Download»). Продуктовая основа
«image tools»-позиционирования: без неё сайт остаётся PNG-сайтом. Вести
параллельно с текущими quality-задачами. Побочно снимает `quality` из схем
**конвертеров** — после этого convert-страницы сводятся на один инструмент
(`docs/backlog.md`). Не из всех схем: у `jpeg-artifacts` `quality` —
функциональное поле (сила артефактов), а `output` у инструмента нет, так
что селектор формата его не покрывает.
- [ ] **[S1g]** Поведение конвертеров после S1f: не предлагать в меню форматов
формат исходника (disable) — чтобы `convert-png-to-jpg` не мог сохранить в
png. **Под вопросом:** блок мешает легальному сценарию «сохранить
обработанный PNG / JPEG с другим качеством»; решение не принято, см.
`docs/backlog.md`. Существующие страницы не трогать: `convert-png-to-jpg`,
`convert-png-to-webp`, `png-to-bmp` остаются самостоятельными лендингами
со своим контентом (§5).
#### S1-domain: свой домен (подключён; §6 R2-cut)
- [x] **[S1a]** Перевести прод на собственный домен: `BASE_PATH` убран из
`deploy.yml`, задан `PUBLIC_SITE_ORIGIN`, добавлен `web/static/CNAME`.
Редирект `https://<юзер>/<repo>/…` → корневые URL (301) делает сам GitHub
Pages при включённом кастомном домене.
- [x] **[S1b]** `web/scripts/gen-sitemap.mjs` пишет `sitemap.xml` в `build/`
после `vite build`. Адреса берутся **из собранного HTML**, а не из импорта
`PAGES`: prerender-страницы и есть фактический набор URL, а `noindex`
исключается автоматически — sitemap не может разойтись с тем, что
задеплоено. Origin — `PUBLIC_SITE_ORIGIN`, фолбэк — `static/CNAME`, чтобы
не плодить третий литерал домена.
- [x] **[S1c]** `robots.txt`: `Sitemap:` добавлен, allow-all сохранён.
**Служебный `/kit` закрыт через `noindex` в HTML, а не через `Disallow:`**
— заблокированный краулером, но не помеченный noindex URL Google помечает
«проиндексирован, несмотря на блокировку в robots.txt».
- [x] **[S1d]** `Seo.svelte` (canonical, OG, `twitter:card`) подключён на `/`,
`/list-tools`, `/chains`, `/tools/[slug]`. Картинка пока общая
`og/default.png` — per-tool остаётся за S1e.
> **Порядок S1:** S1e/S1f/S1g → покупка домена → S1a+S1b+S1c+S1d → **S1h**
> (снятие `noindex`) → S2-контент. Гейты: `pnpm --dir web build` зелёный; в
> `build/` лежит корректный `sitemap.xml` со всеми страницами инструментов;
> canonical/OG присутствуют на всех страницах; `noindex` отсутствует в собранном
> HTML (`pnpm --dir web build` и проверка `build/tools/*/index.html`);
> `pnpm --dir web test:e2e` — по правилам `docs/quality-gates.md`, в CI job
> `e2e` non-blocking, поэтому его статус читается по отдельному job
> (`docs/decisions.md`, раздел 10).
>
> **Фактический порядок другой:** контент S2 написан раньше, а S1h стал
> условным. Гейт «`noindex` отсутствует» выполняется для страниц с текстом;
> полное снятие тега остаётся отдельной задачей.
### S2. Контентный слой (параллельно quality-плану, волнами по категориям)
- [x] **[S2a]** Тип `SeoContent` (`longDescription`, `howTo`, `faq`) в
`web/src/lib/i18n/dict.ts`, доступ `pageSeo(page)`. Хранится в словаре
`pages.<slug>.seo`, не в реестре: длинного текста в реестре быть не может.
EN-only до решения по `/ru/` — паритет локалей снят исключением
`ignoreMissingPatterns` в `web/eslint.config.js`.
- [x] **[S2b]** `web/src/lib/components/PageContent.svelte`: абзац → `<ol>`
how-to → FAQ в `<details>`, под рабочей областью инструмента. Рендерится
только при наличии `seo`-блока, поэтому страницы без текста не меняются.
- [x] **[S2c]** `Breadcrumbs.svelte` над H1 (средняя крошка — текст до появления
хабов категорий, S3c) и блок Related под FAQ. **Related выводится, а не
перечисляется:** `relatedPages()` берёт соседей по категории и добирает из
каталога, если категория мала; новый инструмент попадает в чужие related
сам.
- [ ] **[S2d]** Первая волна контента — топ приоритетных инструментов ядра
(приоритет по вордстату: transparent png, jpg→png, compress, resize, crop,
rotate, circle crop, add text, pixelate, grayscale…); отбор — §5. **Ядро
закрыто: 20 страниц из §5** — `crop-png`, `resize-png`,
`remove-background-png`, `convert-png-to-jpg`, `pixelate-png`,
`grayscale-png`, `rotate-png`, `create-empty-png`, `linear-gradient-png`,
`change-png-opacity`, `set-alpha-channel-png`, `round-corners-png`,
`invert-colors-png`, `flip-png`, `convert-png-to-webp`, `png-to-bmp`,
`svg-to-png`, `png-to-base64`, `change-aspect-ratio-png`, `add-text-png`.
Текст написан по фактической схеме инструмента, без подстановки в шаблон.
**Правило набора текста:** H1 — чистый интент, «free / no upload» — в
`metaTitle` и `metaDescription`, форматы — в первом абзаце и FAQ,
ограничения инструмента проговариваются прямо («это не AI-удаление фона»,
«только 90/180/270, произвольный угол ресемплит»): честное ограничение
отвечает на вопрос People-Also-Ask лучше приписывания. Остальное ядро §5 —
тем же шаблоном.
- Гейты: на каждой странице инструмента — уникальный текст ≥ 300 слов, H1
совпадает с интентом, FAQ ≥ 3, related ≥ 3; текст в статике (prerender), не в
рантайме.
### S3. Структурированные данные + GEO
- [x] **[S3a]** JSON-LD — на странице инструмента `SoftwareApplication`
(`offers` price 0), `FAQPage` (из уже написанного FAQ) и `BreadcrumbList`
(из тех же крошек, что видны в UI); на главной — `WebSite` +
`Organization`; на каталоге — `BreadcrumbList`. Построители в
`web/src/lib/seo.ts`, вывод — `web/src/lib/components/JsonLd.svelte`.
**Известная грязь:** в `JsonLd.svelte` инлайновый
`eslint-disable svelte/no-at-html-tags` — `<script>{json}</script>` не
подставляет значение (Svelte читает содержимое script как raw text),
`{@html}` остаётся единственным способом. Suppress обоснован
экранированием `<`; пересмотр — задача в `docs/backlog.md`.
- [ ] **[S3b]** `llms.txt` в статике: короткий манифест сайта + список
инструментов с одним предложением на каждый (генерится из реестра тем же
скриптом, что sitemap).
- [ ] **[S3c]** Хабы категорий с текстом: 2–3 абзаца на категорию (что за
группа, когда какие инструменты применять), не голые списки карточек.
- [ ] **[S3d]** GEO-редактура: каждое описание начинается прямым определением
(«Crop PNG is a free tool that cuts a rectangular area from a PNG image
right in your browser»), списки/таблицы для фактов, без вводных «в
современном мире…». Проверка: страница отвечает на запрос одним абзацем
«без скролла».
- [ ] **[S3e]** _(опционально)_ Информационные гайды по форматам: 3–4 сильных
материала (`/guides/png-format`, `/guides/jpeg-vs-png`, …): история и
модификации формата, lossy/lossless, прозрачность, когда какой формат
выбирать. Каждый — с ссылками на 3–5 связанных инструментов (хабы
перелинковки), FAQ из гайдов продублирован как FAQPage-разметка. Приоритет
ниже S2/S3a–d: только когда ядро написано и есть ресурс.
- Гейты: валидация JSON-LD (Rich Results Test без ошибок); `llms.txt` актуален
при добавлении инструментов (генерация, а не ручное редактирование).
### S4. Измерение и запуск
- [ ] **[S4a]** Google Search Console: собственность на домен, отправка sitemap,
мониторинг индексации и запросов. Бесплатно и обязательно.
- [x] **[S4b]** GA4 в cookieless-конфигурации: Consent Mode v2 со всеми `denied`
**до** вызова `config`, рекламные signals и audiences выключены,
`ip_collection: false`. **Про `anonymize_ip` из исходной формулировки
задачи:** в GA4 параметра нет, IP там не сохраняется целиком — поэтому
`ip_collection: false`, а не мёртвый флаг. Запрет на данные картинки
проверяем: тег нигде, кроме `ga.js`, не вызывается
(`web/src/lib/analytics-config.test.ts`). Формулировка «без трекинга» на
публичных страницах по-прежнему не используется.
- [ ] **[S4c]** Дистрибуция: закрепить GitHub-репо (README со скриншотами),
подача на alternative.to (страница против конкурента), Product Hunt,
тематические Reddit/HN-треды. Бэклинки — главный дефицит для конкуренции.
- [ ] **[S4d]** _(опционально, позже)_ Сравнительные посадочные «vs …» (без
негатива: «no limits, no signup, no upload») — по мере роста доверия
домена.
- [ ] **[S4e]** Контроль CWV: Lighthouse на страницы ядра после каждого крупного
этапа редизайна; цель — зелёные метрики на мобильных.
## 5. Приоритизация инструментов (первая волна контента)
Источник — `docs/tools-map.md` (наш состав и замер по конкуренту — §2). Слаги
ниже — **фактические `slug` страниц** из `docs/tools-map.md` §1; первая волна —
топ-интенты, где конкуренты ранжируются, а у нас уже есть инструмент и он не
хуже:
- прозрачность/фон: create-empty-png, remove-background-png, change-png-opacity,
set-alpha-channel-png, round-corners-png
- конвертация: convert-png-to-jpg, convert-png-to-webp, png-to-bmp, svg-to-png,
png-to-base64
- геометрия: crop-png, resize-png, rotate-png, flip-png, change-aspect-ratio-png
- цвет/фильтры: grayscale-png, invert-colors-png, pixelate-png
- текст: add-text-png, watermark-tile-png
- генерация: linear-gradient-png
> Группы здесь — по категории страницы в реестре (`registry/pages/`), а не по
> смыслу: `round-corners-png` лежит в `alpha`, `add-text-png` и
> `watermark-tile-png` — в `text`.
>
> **Открытый вопрос по интенту «сжать до N КБ».** Лимит размера теперь живёт в
> кнопке Download (`fitWithinBytes` в `web/src/lib/core/io.ts`); интенты вида
> «compress png to 500kb / 1mb» — high-volume и лимит-ориентированные, селектор
> в Download их не закрывает как посадочная. Решение (отдельная страница / приём
> в контенте существующих / ничего) требует вордстата; имя страницы не выбрано.
> Прорабатывать вместе с вопросом «из формата в PNG» выше.
>
> **Открытый вопрос по интентам «из формата в PNG».** У конкурента есть
> `jpg-to-png`, `webp-to-png`, `gif-to-png` как отдельные страницы; у нас таких
> инструментов нет и создавать их не хочется — загрузка файла уже принимает
> любой формат, а конвертер «во что-то» закрывает обратное направление. Это
> high-volume интент без посадочной страницы. Решение (страница-обёртка на
> существующий инструмент / приём в H1 и тексте / ничего) требует вордстата и
> должно быть принято до старта S2, иначе первая волна контента закроет не ядро
> запросов.
Новая волна форматов (после S1f): jpg↔webp и другие конвертации «формат →
формат» — только по подтверждённому вордстату; базовые кейсы закрывает селектор
формата в Download (S1f), страницы заводим лишь там, где интент стабильно
ищется.
- **crop-png: H1 «Crop image online — free, no upload»**, текст «works with PNG,
JPG, WebP» — широкий интент обслуживается контентом страницы с png-слагом,
слаг при этом остаётся якорем дешёвого png-интента.
### Форматные варианты страниц (`crop-jpeg`, `crop-webp`, …)
Три возможных механики — важно не перепутать:
1. **301-редирект на существующую страницу** (`/crop-jpg` → `/crop-jpeg`, или
`/crop-png` → новый слаг после переименования) — законно **только** для
опечаток, альтернативных написаний и переименований **уже опубликованной**
страницы: URL, которого никогда не было в индексе, редиректом не закрывается.
Для самостоятельного запроса редирект — это отказ от страницы: Google
перестаёт показывать редирект, ранжировать будет только цель. Как посадочная
под «crop jpeg» не работает вообще.
2. **Зеркало с canonical на `/crop-png`** — технически легально, но бесполезно:
canonical-дубль в индекс не попадает, видимости не добавляет, а массовое
производство таких зеркал выглядит как накрутка площади и при ошибке в
canonical превращается в дорвейный кластер. Не делаем.
3. **Самостоятельная страница = самостоятельный контент.** Заводить `/crop-jpeg`
можно только если: (а) интент подтверждён вордстатом, (б) на странице
собственный текст — H1 «Crop JPEG», FAQ про качество/артефакты/ когда
выбирать JPEG вместо PNG, отличия от png-кейса, (в) текст написан или выверен
человеком, а не получен подстановкой формата в шаблон. Реестром
автоматизировать **данные** (тот же tool, другой дефолтный output) — можно;
автоматизировать **текст** — нельзя: массовая подстановка {tool}×{format} в
шаблон — doorway-паттерн, риск deindex всего сайта.
Как правильно получать видимость под «jpeg»/«webp» без клонов: широкие
формулировки закрывает контент существующей страницы (H1 «Crop image», «works
with PNG, JPG, WebP» в тексте + селектор формата в Download, S1f). Отдельные
форматные страницы — точечные исключения по вордстату, не серия.
### Convert-инструменты: страницы и «универсальный конвертер»
Разводить два разных интента:
- **«convert png to jpg»** — интент «страница-конвертер»: пользователь ищет
конкретную пару форматов (лучшее качество конверсии, меньше ошибок), это
готовые лендинги. Существующие (`convert-png-to-jpg`, `convert-png-to-webp`,
`png-to-bmp`, `svg-to-png`, `base64-to-png`, …) **оставить и наполнить
контентом как все**: свой H1 («Convert PNG to JPG — free, no upload»), FAQ про
качество/прозрачность/фон, related. Селектор формата (S1f) их **не заменяет**
— он про «уже открыл инструмент и хочу другой формат на выходе».
- **«convert image» / «image converter»** — интент «швейцарский нож». Под него —
**один будущий инструмент `/convert-image`**: сначала пустой **core-tool** в
`core/` — функция ре-энкода («копия с перекодированием»: декодирует любой
поддержанный исходник в ImageData и кодирует в выбранный формат; поверх неё
S1f строит селектор формата). Инструмент и страницу добавлять **только вместе
с собственным текстом** («Convert any image to PNG, JPG or WebP in your
browser») — core в коде не обязывает к странице. Этот же шаблон потом
закрывает форматные волны (jpg→webp и т.п.) без новых страниц.
Не делать: серию `convert-jpeg`/`convert-webp`-клонов с подстановкой формата
(doorway, см. выше) и «универсальный конвертер» как единственную страницу вместо
форматных лендингов — теряется ядро «convert png to jpg»-запросов.
Правило добавления новых инструментов: брать из хвоста конкурента только те
интенты, которые подтверждены вордстатом, и сразу с контентом S2 (страница без
текста — потраченный URL).
## 6. Переименование бренда и домен
Бренд — `easy-image-tools`, домен — `easy-image-tools.com`.
- [x] **[R1]** Переезд бренда: хардкод убран, бренд — i18n-константа
`t("brand")` (точка истины — словари `brand` + README). Репозиторий и
remote переименованы, привязанные ссылки/пути переключены. **НЕ трогать:**
адреса страниц инструментов (`/tools/crop-png`), localStorage-ключи
(бренда не содержат), историю `docs/archive`.
- [x] **[R2]** Домен `easy-image-tools.com` зарегистрирован; `SITE_URL` в
`web/src/lib/seo.ts` переключён на него.
- [x] **[R2-cut]** Прод на домене без `BASE_PATH` (§0): DNS + HTTPS,
`PUBLIC_SITE_ORIGIN` в `deploy.yml`, `web/static/CNAME`. 301 со старого
GitHub Pages делает сам GitHub при включённом кастомном домене.
- **Порядок:** переименование делали до индексации: после снятия `noindex` это
был бы переезд с 301 и потеря позиций — не делать.
## 7. Что осознанно НЕ делаем
- Клонирование всего каталога конкурента (низкокачественные дубли + нет ресурсов
на контент) — вместо этого ядро + обоснованные хвосты.
- RU-локаль в индексе до решения о структуре `/ru/` + hreflang.
- Реклама, ремаркетинг, pixels сторонних сетей — противоречит позиционированию.
Счётчик посещений GA4 — не противоречит, условия ограничений — §0 и S4b.
- Дорвеи, программные зеркала, покупные ссылки.
- Массово-сгенерированные форматные клоны (`crop-jpeg`/`crop-webp` как копии
`crop-png` с подстановкой формата в шаблонный текст) — doorway-паттерн, риск
deindex всего сайта. Форматная страница — только уникальный контент + вордстат
(§5); редиректы — только для опечаток/переименований.
- URL-алиасы без `-png` для существующих инструментов и зеркальные
страницы-конвертеры «на каждый формат» без вордстата.
## 8. Связанные документы
- `docs/tools-map.md` — карта «наш инструмент ↔ интент конкурента».
- `docs/backlog.md` — пункты SEO/GEO в общем бэклоге.
- `docs/roadmap.md` — фазы 2–8 (ядро TS→Rust/wasm, CLI), фаза 11 (пайплайн);
фазы 9–10 — API и PWA.
- `docs/plan-platform.md` — планы API и PWA (общее ядро, оффлайн-режим).