Отдайте один HTML-документ с Content-Type: text/html; charset=utf-8, заголовком и описанием в секции head документа, а также читаемым содержимым в секции body. В этом руководстве для Mac создаётся тестовая страница и проверяется, что все три элемента передаются в одном ответе.

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

Что должно входить в один типизированный HTTP-ответ?

«Типизированный» означает, что в ответе указан тип медиаданных. Поле Content-Type сообщает получателю формат представления и способ его обработки. В данном случае это HTML. Оно не задаёт схему приложения с отдельными полями. RFC 9110, раздел 8.3

Ответ HTTP/1.1 состоит из строки состояния, полей заголовка, пустой строки и необязательного тела. Поэтому транспортные метаданные можно передать в заголовках, а полный документ — в теле, не создавая отдельный запрос для метаданных. RFC 9112, раздел 2.1

Для этой задачи публикации используйте следующую схему размещения. Она отделяет сведения о представлении HTTP от сведений о странице.

Сведения Расположение Пример
Формат представления и кодировка Заголовок HTTP Content-Type: text/html; charset=utf-8
Заголовок документа Секция head HTML <title>Бумажные фигуры: круг и квадрат</title>
Краткое описание страницы Секция head HTML <meta name="description" content="A short guide to arranging paper circles and squares.">
Содержимое для читателей Секция body HTML Заголовок, введение и инструкции по расположению фигур

WHATWG определяет элемент title как заголовок или название документа, а значение метаданных description — как описание страницы. Используйте один заголовок и не более одного объявления description. Поскольку они находятся внутри HTML-документа, содержимое и описательные метаданные передаются вместе. Стандарт HTML: метаданные документа

Как создать работающую локальную копию на Mac?

Откройте Terminal и проверьте доступные команды:

python3 --version
curl --version

Программе ниже требуется Python 3.7 или новее; она использует только стандартную библиотеку. Если python3 недоступна, следуйте официальным инструкциям по установке для macOS, а затем снова откройте Terminal. В документации Python описаны и установщик, и запуск python3 из терминала. Использование Python в macOS

Вставьте весь блок ниже в Terminal, включая начальную команду и последнюю строку PY. Программа запустится напрямую и не создаст и не перезапишет файл. Оставьте это окно терминала открытым, пока проверяете страницу.

python3 - <<'PY'
from html import escape
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

TITLE = "Paper Shapes: Circle and Square"
SUMMARY = "A short guide to arranging paper circles and squares."
PAGE = f"""<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{escape(TITLE)}</title>
<meta name="description" content="{escape(SUMMARY, quote=True)}">
</head>
<body>
<main>
<h1>{escape(TITLE)}</h1>
<p>{escape(SUMMARY)}</p>
<h2>Try a simple arrangement</h2>
<p>Place one paper circle beside one paper square.</p>
<p>Switch their positions — then compare the two arrangements.</p>
</main>
</body>
</html>
""".encode("utf-8")

class Handler(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"

    def respond(self, include_body):
        if self.path != "/paper-shapes":
            self.send_error(404, "Page not found")
            return
        self.send_response(200)
        self.send_header("Content-Type", "text/html; charset=utf-8")
        self.send_header("Content-Length", str(len(PAGE)))
        self.end_headers()
        if include_body:
            self.wfile.write(PAGE)

    def do_GET(self):
        self.respond(True)

    def do_HEAD(self):
        self.respond(False)

if __name__ == "__main__":
    with ThreadingHTTPServer(("127.0.0.1", 8000), Handler) as server:
        print("Preview: http://127.0.0.1:8000/paper-shapes", flush=True)
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            pass
PY

Обработчик использует документированные методы Python для формирования ответа: send_response() начинает ответ, send_header() добавляет поля, а end_headers() завершает секцию заголовков перед записью байтов тела. Документация Python по HTTP-серверу

В примере значения TITLE и SUMMARY намеренно заданы по одному разу. Они используются и в метаданных, и в видимом тексте, что упрощает согласованное редактирование этого небольшого документа. Для более длинной страницы описание может отличаться от первого абзаца, если оно точно передаёт ту же тему.

Страница кодируется до вычисления её длины. В HTTP значение Content-Length задаётся в октетах, то есть байтах; подсчёт символов дал бы неверный результат из-за длинного тире в этом примере. RFC 9110, раздел 8.6

Как проверить, что содержимое и метаданные пришли вместе?

Откройте второе окно Terminal и выполните эту команду. Она отправит обычный запрос документа и выведет заголовки ответа вместе с содержимым:

curl -i --max-time 10 http://127.0.0.1:8000/paper-shapes

Параметр -i включает заголовки ответа в тот же поток вывода, что и тело. Ограничение времени задаёт максимальную длительность передачи — десять секунд. Описание этих параметров есть в справочном руководстве по командной строке curl.

Проверьте вывод по порядку:

  1. В строке состояния должно быть указано 200 OK.
  2. Единственное поле Content-Type должно указывать text/html; charset=utf-8.
  3. После пустой строки, завершающей секцию заголовков, в HTML должны быть заголовок и описание.
  4. В том же теле должны быть инструкции по работе с бумажными фигурами.
  5. Длинное тире должно корректно отображаться в декодированном тексте.

Затем введите http://127.0.0.1:8000/paper-shapes в адресную строку Safari. На странице должны отображаться заголовок, краткое описание и инструкции. Объявление description — это метаданные; его наличие проверяется в полученной разметке, а не определяется по внешнему виду страницы.

Можно также проверить запрос только заголовков:

curl -I --max-time 10 http://127.0.0.1:8000/paper-shapes

Он отправляет HEAD, поэтому по нему нельзя проверить, есть ли в теле заголовок или описание. Ответ HEAD не содержит контента, но описывает соответствующее представление; чтобы изучить сам документ, используйте предыдущую проверку GET. RFC 9110, раздел 8.2

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

Симптом Следующее действие
При запуске сообщается, что адрес уже используется Замените 8000 на 8001 в программе, запустите её снова и указывайте 8001 во всех командах проверки.
Запрос сообщает об ошибке соединения Убедитесь, что в первом окне терминала всё ещё работает сервер и что порты совпадают.
Ответ — 404 Используйте путь /paper-shapes точно в таком виде; пример не принимает завершающий слеш или строку запроса.
Браузер по-прежнему показывает прежнюю версию Остановите программу сочетанием Control-C, снова запустите отредактированный блок и перезагрузите страницу.
Заголовок выглядит правильно, но метаданные отсутствуют Изучите вывод GET и проверьте, что объявления находятся внутри секции head документа.

Что Google Search может использовать из этого ответа?

Описательный заголовок, точное описание и полезный видимый текст дают Google документированные данные для представления страницы. Среди источников для автоматической генерации ссылок-заголовков Google указывает элемент title, основной визуальный заголовок, элементы заголовков и другие данные. Эти элементы должны соответствовать теме документа. Как повлиять на ссылки-заголовки в результатах поиска

Google в первую очередь формирует фрагменты из содержимого страницы, а иногда использует метаописание, если оно точнее описывает страницу. Для разных запросов могут показываться разные фрагменты. Рассматривайте описание как полезное краткое изложение, а не как неизменную подпись в результатах поиска. Как управлять фрагментами в результатах поиска

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

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

Как превратить локальную копию в схему публичной публикации?

Локальный адрес в этом руководстве служит для предварительного просмотра на компьютере, а не для публикации страницы. В документации Python также указано, что http.server не рекомендуется для промышленного использования. Перенесите структуру HTML и требования к ответу в систему публикации, а не используйте эту демонстрацию в качестве публичного хостинга. Документация Python по HTTP-серверу

Если вы используете систему управления контентом, найдите её документированные настройки заголовка страницы и метаописания. Google отмечает, что CMS может предоставлять настройки для поиска вместо прямого редактирования HTML. Свяжите эти настройки с полями, показанными здесь, а затем изучите сформированный ответ документа. Рекомендации Google по добавлению метаописаний

HTTP поддерживает составные представления, но это другой способ упаковки. Для задачи создания документа, который можно прочитать в браузере, достаточно структуры HTML, позволяющей включить содержимое и метаданные документа в одно представление. Использование multipart потребовало бы от получателя специальной обработки, которая в этом руководстве не нужна. RFC 9110, раздел 8.3.3

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

Источники