Кастомные HTTP-заголовки
К любому статическому сайту можно добавить собственные HTTP response
headers — например Strict-Transport-Security, X-Frame-Options,
Content-Security-Policy или Referrer-Policy. Задаются они файлом
_headers в корне собранного сайта (синтаксис как у Netlify).
Отдельной панели «Custom Headers» в дашборде нет — управление заголовками живёт в этом файле, чтобы конфиг версионировался в git рядом с кодом.
Почему через файл, а не в коде сайта
У статического сайта нет вашего серверного процесса: это набор файлов,
которые отдаёт CDN и edge-nginx Layero. HTTP response headers — часть
ответа сервера, поэтому их проставляет тот, кто отдаёт байты, а не
сам HTML-файл. Тег <meta http-equiv> здесь не помогает: браузеры
игнорируют его для Strict-Transport-Security, X-Frame-Options,
Content-Security-Policy — эти политики действуют только как настоящие
HTTP-заголовки.
Файл _headers — это и есть способ передать ваше намерение на уровень
отдачи: сборщик читает его и прикрепляет указанные заголовки к ответам
edge.
:::note SSR и runtime-приложения
Всё описанное — про статические сайты. Если у вас SSR или
runtime-приложение (Next.js в режиме сервера, Streamlit, Flask), процесс
работает на сервере и может ставить заголовки сам — в коде
(res.setHeader(...), next.config.js → headers() и т.п.). Для таких
проектов _headers не нужен.
:::
Быстрый старт
Положите файл с именем _headers (без расширения) в корень выходной
папки сборки — рядом с index.html:
/*
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Задеплойте — и заголовки начнут отдаваться на всех страницах сайта.
Проверить можно любым запросом с -I:
curl -sI https://<org>-<project>.layero.ru/ | grep -i -E 'strict-transport|x-frame|referrer'
Синтаксис
Файл — это блоки. Блок начинается со строки-пути на левом краю (без
отступа, должна начинаться с /). Следом идут строки с отступом вида
Header-Name: значение, которые относятся к этому пути. Пустые строки и
строки, начинающиеся с #, игнорируются.
# Комментарий — игнорируется
/*
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000
/about/
X-Robots-Tag: noindex
/assets/*
Cache-Control: public, max-age=31536000, immutable
Поддерживаемые пути
| Шаблон | Что матчит | Пример |
|---|---|---|
/* | Все страницы (catch-all) | весь сайт |
| Точный путь | Ровно этот URL | /about/, /index.html |
/prefix/* | Все пути внутри префикса | /assets/*, /docs/* |
Wildcard в середине пути (/foo/*/bar) пока не поддерживается — такая
строка молча пропускается, а в лог сборки пишется предупреждение.
Как разрешаются пересечения
Порядок в файле сохраняется, и при совпадении нескольких блоков для одного
заголовка побеждает последний. Поэтому общий блок /* обычно ставят
первым, а точечные переопределения — ниже:
/*
Cache-Control: public, max-age=0, must-revalidate
/assets/*
Cache-Control: public, max-age=31536000, immutable
Здесь /assets/style.css получит «долгий» кэш, а всё остальное —
must-revalidate.
Что можно и нельзя задавать
Разрешены любые заголовки, включая Content-Security-Policy,
Permissions-Policy, X-Content-Type-Options, Cross-Origin-* и т.д.
Несколько заголовков зарезервированы платформой и переопределить их нельзя — они пропускаются с предупреждением в логе сборки:
Set-CookieX-Layero-Cache,X-Layero-Deploy-Id,X-Layero-Project-Id,X-Layero-S3-Path,X-Layero-Redirects,X-Layero-Headers(служебные заголовки Layero).
Layero не добавляет security-заголовки автоматически. HSTS,
X-Frame-Options и подобное появятся только после того, как вы объявите
их в _headers.
Куда положить файл
_headers должен попасть в корень артефакта — ту папку, содержимое
которой публикуется (output в настройках проекта или в
layero.json).
- Многие генераторы копируют «как есть» файлы из папки статики (
public/,static/,assets/). Кладите_headersтуда — на выходе он окажется в корне сборки. - Обычный проект: файл виден в собранной папке рядом с
index.html. - Monorepo: в пределах
root_directoryпроекта, чтобы после сборки оказаться в корнеoutput.
После деплоя убедитесь, что заголовки применились, запросом curl -sI
(см. Быстрый старт). Если их нет — почти всегда файл не
попал в корень output.
Ошибки в файле не роняют деплой
Парсер устойчив к ошибкам: некорректные строки (путь без /, заголовок
без :, неподдерживаемый wildcard, зарезервированное имя) пропускаются,
корректные — применяются. Все замечания попадают в лог сборки, деплой не
падает. Пустой или отсутствующий _headers очищает ранее заданные
правила — так что состояние заголовков всегда отражает последнюю сборку.
Работает и на кастомных доменах
Заголовки применяются одинаково и на адресе <org>-<project>.layero.ru, и
на подключённом кастомном домене — правила привязаны к
деплою, а не к конкретному хосту.
Редиректы
Постраничные редиректы задаются соседним файлом _redirects (тоже
Netlify-совместимый формат, матчит по пути). Обратите внимание: _redirects
работает по пути, а не по хосту, поэтому host-based редирект
www.site.ru → site.ru им не сделать — это настраивается на стороне
DNS/регистратора (см. Кастомные домены).