← Назад к вопросам

Как работает HTTP?

1.7 Middle🔥 181 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

HTTP: как работает протокол обмена данными

HTTP (HyperText Transfer Protocol) — это фундамент веб-разработки. За 10+ лет я понял, что глубокое понимание HTTP критично для оптимизации, дебагинга и архитектуры приложений. Разбираю от основ до практики.

1. Основы HTTP: Request-Response модель

HTTP работает по простому принципу: клиент отправляет запрос, сервер отправляет ответ.

// В браузере всё HTTP это по сути fetch() API
const httpRequest = async () => {
  // ШАГИ
  // 1. ESTABLISH CONNECTION (TCP handshake)
  //    - Браузер подключается к серверу по IP и портам
  //    - Для HTTPS добавляется SSL/TLS handshake
  
  // 2. SEND REQUEST (отправка запроса)
  const response = await fetch('https://api.example.com/users/123', {
    method: 'GET',          // HTTP метод
    headers: {              // HTTP заголовки
      'Content-Type': 'application/json',
      'Authorization': 'Bearer token'
    },
    body: JSON.stringify({}),  // Тело запроса (для POST/PUT)
    // body не используется для GET
  });
  
  // 3. RECEIVE RESPONSE (получение ответа)
  const status = response.status;          // 200, 404, 500 итд
  const headers = response.headers;        // Заголовки ответа
  const data = await response.json();      // Тело ответа
  
  // 4. CLOSE CONNECTION (закрытие соединения)
  // Обычно автоматически (HTTP/1.1 Keep-Alive)
  
  return { status, headers, data };
};

Визуально:

Клиент (Браузер)                  Сервер
    |
    |--- TCP Connect ---------->|
    |<------- TCP ACK ----------|
    |
    |--- SSL Handshake (HTTPS) ->|
    |<-- SSL Handshake Response --|
    |
    |--- HTTP Request ---------->|
    |   (метод, URL, заголовки)  |
    |   (тело запроса)           |
    |
    |<-- HTTP Response ----------|
    |   (статус, заголовки)      |
    |   (тело ответа)            |
    |
    |--- TCP Close ------------>|
    |<------- TCP FIN -----------|

2. HTTP методы (CRUD операции)

const httpMethods = {
  // GET - получить данные (безопасен, кэшируется)
  get: async () => {
    const user = await fetch('/api/users/123').then(r => r.json());
    // Можно кэшировать, безопасен, нет побочных эффектов
  },
  
  // POST - создать ресурс (изменяет состояние)
  post: async () => {
    const newUser = await fetch('/api/users', {
      method: 'POST',
      body: JSON.stringify({ name: 'John', email: 'j@e.com' })
    }).then(r => r.json());
    // Каждый вызов создаёт новый ресурс
  },
  
  // PUT - заменить ресурс полностью (идемпотентен)
  put: async () => {
    const updated = await fetch('/api/users/123', {
      method: 'PUT',
      body: JSON.stringify({ name: 'Jane', email: 'j@e.com' })
    }).then(r => r.json());
    // Множественные вызовы = одинаковый результат
  },
  
  // PATCH - частичное обновление
  patch: async () => {
    const updated = await fetch('/api/users/123', {
      method: 'PATCH',
      body: JSON.stringify({ name: 'Jane' }) // только что менять
    }).then(r => r.json());
  },
  
  // DELETE - удалить ресурс (идемпотентен)
  delete: async () => {
    const status = await fetch('/api/users/123', {
      method: 'DELETE'
    }).then(r => r.status);
    // Второй вызов тоже вернёт 204 или 404
  }
};

// Правило: GET/PUT/DELETE идемпотентны (повторяемы)
// POST - не идемпотентен (каждый вызов = новый результат)

3. HTTP статус-коды

const statusCodes = {
  // 2xx - успех
  '200': 'OK - запрос успешен',
  '201': 'Created - ресурс создан (обычно POST)',
  '204': 'No Content - успех, но нет тела ответа (DELETE)',
  '206': 'Partial Content - часть контента (диапазоны)',
  
  // 3xx - редирект
  '300': 'Multiple Choices - несколько вариантов',
  '301': 'Moved Permanently - постоянный редирект',
  '302': 'Found - временный редирект',
  '304': 'Not Modified - кэш актуален',
  '307': 'Temporary Redirect - как 302, но сохраняет метод',
  
  // 4xx - ошибка клиента
  '400': 'Bad Request - неверный синтаксис',
  '401': 'Unauthorized - нужна аутентификация',
  '403': 'Forbidden - доступ запрещён',
  '404': 'Not Found - ресурс не найден',
  '408': 'Request Timeout - время ожидания истекло',
  '429': 'Too Many Requests - rate limit',
  
  // 5xx - ошибка сервера
  '500': 'Internal Server Error - что-то сломалось',
  '502': 'Bad Gateway - неверный ответ от upstream',
  '503': 'Service Unavailable - сервер перегружен',
  '504': 'Gateway Timeout - время ожидания от upstream'
};

// На фронтенде обрабатываем так:
const handleResponse = async (response) => {
  if (response.ok) {
    // 2xx - успех
    return response.json();
  } else if (response.status === 401) {
    // Не авторизирован - редирект на логин
    window.location.href = '/login';
  } else if (response.status === 429) {
    // Rate limit - показываем пользователю
    throw new Error('Слишком много запросов, подождите');
  } else if (response.status >= 500) {
    // Ошибка сервера - retry
    throw new Error('Сервер недоступен');
  }
};

4. HTTP заголовки (Headers)

const httpHeaders = {
  // Основные заголовки запроса
  request: {
    'Content-Type': 'application/json',        // тип тела запроса
    'Accept': 'application/json',              // какой формат хотим
    'Authorization': 'Bearer token123',        // токен
    'User-Agent': 'Mozilla/5.0...',            // браузер
    'Accept-Language': 'en-US',                // предпочитаемый язык
    'Cache-Control': 'no-cache',               // кэширование
    'Cookie': 'sessionId=abc123',              // куки
    'Origin': 'https://example.com',           // откуда запрос (CORS)
    'Referer': 'https://example.com/page'      // откуда пришли
  },
  
  // Основные заголовки ответа
  response: {
    'Content-Type': 'application/json',        // тип тела ответа
    'Content-Length': '1234',                  // размер тела
    'Cache-Control': 'max-age=3600',           // кэш на 1 час
    'Set-Cookie': 'sessionId=xyz789; Path=/',  // установить куки
    'Location': 'https://example.com/new',     // редирект URL
    'ETag': '"w/123abc"',                      // версия ресурса
    'Last-Modified': 'Mon, 01 Jan 2024',       // когда изменён
    'Access-Control-Allow-Origin': '*',        // CORS разрешение
    'Access-Control-Allow-Methods': 'GET, POST', // какие методы
    'Access-Control-Allow-Headers': 'Content-Type', // какие заголовки
    'X-RateLimit-Remaining': '99',             // сколько запросов осталось
    'X-RateLimit-Reset': '1704067200'          // когда сбросится
  }
};

// Практический пример
const makeRequest = async () => {
  const response = await fetch('/api/data', {
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${getToken()}`
    }
  });
  
  // Читаем важные заголовки ответа
  const cacheControl = response.headers.get('Cache-Control');
  const rateLimit = response.headers.get('X-RateLimit-Remaining');
  const etag = response.headers.get('ETag');
  
  return response.json();
};

5. Кэширование HTTP

// Cache-Control направляет браузер как кэшировать
const caching = {
  // Не кэшировать (для данных, которые всегда меняются)
  'no-cache, no-store': `
    Браузер не кэширует
    Каждый раз свежий запрос
  `,
  
  // Кэшировать на время
  'max-age=3600': `
    Браузер кэширует на 3600 секунд (1 час)
    Если просишь опять в течение часа - вернёт из кэша
  `,
  
  // Стратегия переопроверки
  'must-revalidate': `
    Кэш истёк? Обязательно проверить на сервере
  `,
  
  // Для статических ресурсов
  'public, max-age=31536000': `
    Публичный кэш (CDN, прокси) на 1 год
    Хорош для файлов с версиями в имени
  `
};

// ETag и Last-Modified для проверки изменений
const handleCache = async () => {
  // Первый запрос
  let response = await fetch('/api/data');
  const etag = response.headers.get('ETag');
  const data = await response.json();
  
  // Второй запрос с ETag
  response = await fetch('/api/data', {
    headers: {
      'If-None-Match': etag  // "если не изменился"
    }
  });
  
  if (response.status === 304) {
    // Not Modified - используй старые данные
    console.log('Данные не изменились');
  } else {
    // Данные изменились
    const newData = await response.json();
  }
};

6. Persistent connections и HTTP/2

const httpVersions = {
  // HTTP/1.1 (2009) - по умолчанию до недавна
  'HTTP/1.1': {
    keepAlive: 'Переиспользует одно соединение для нескольких запросов',
    lineOfSight: 'Запросы идут по очереди (Head of Line Blocking)',
    performance: 'Многие маленькие запросы = медленно'
  },
  
  // HTTP/2 (2015) - мультиплексирование
  'HTTP/2': {
    multiplexing: 'Множество запросов в одном соединении параллельно',
    binaryProtocol: 'Бинарный формат вместо текста (быстрее)',
    serverPush: 'Сервер может отправить ресурсы без запроса',
    headerCompression: 'Заголовки сжимаются (HPACK)',
    performance: 'Много маленьких запросов = быстро'
  },
  
  // HTTP/3 (2022) - на QUIC
  'HTTP/3': {
    quic: 'Использует QUIC вместо TCP',
    faster: 'Быстрее переподключение (мобильные)',
    noCongestion: 'Нет head-of-line blocking даже при потере пакетов'
  }
};

// На практике
const checkVersion = async () => {
  const response = await fetch('/api/data');
  // response.type даст информацию, но версию HTTP не видно прямо
  console.log('Браузер автоматически использует лучшую версию');
};

7. CORS (Cross-Origin Resource Sharing)

// Когда фронт (example.com) делает запрос к API (api.example.com)
const corsRequest = async () => {
  // Браузер автоматически отправляет Origin
  const response = await fetch('https://api.example.com/data', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
      // Браузер добавляет: 'Origin': 'https://example.com'
    },
    body: JSON.stringify({})
  });
  // Сервер проверяет Access-Control-Allow-Origin
  // Если не совпадает - браузер блокирует ответ (CORS error)
};

const handleCORS = () => {
  // ✓ Сервер разрешил (предпродакшн)
  const corsHeader = 'Access-Control-Allow-Origin: https://example.com';
  
  // ❌ Сервер заблокировал (безопасность)
  // Access-Control-Allow-Origin: https://other.com
  // Браузер скроет ответ: "CORS policy blocked"
};

8. Производительность HTTP

const performanceTips = {
  // 1. Минимизируй количество запросов
  bundling: {
    wrong: '100 маленьких файлов = 100 запросов',
    right: 'Объедини в бандлы = 5 запросов',
    tool: 'Webpack, Vite автоматически это делают'
  },
  
  // 2. Используй кэширование
  caching: {
    static: 'Картинки, CSS, JS - кэширование на год',
    api: 'API данные - кэширование на 5-10 минут'
  },
  
  // 3. Сжатие (Gzip, Brotli)
  compression: {
    wrong: 'Отправляем 500KB JSON',
    right: 'Сжимаем до 50KB Gzip',
    header: 'Accept-Encoding: gzip, deflate, br'
  },
  
  // 4. CDN для географического распределения
  cdn: {
    what: 'Копируем статические файлы на серверы по миру',
    benefit: 'Пользователь получает из ближайшего сервера'
  },
  
  // 5. Ленивая загрузка
  lazy: {
    images: 'Загружаем только видимые изображения',
    api: 'Запрашиваем только нужные данные'
  }
};

// Мониторим в браузере
const monitorHTTP = () => {
  // DevTools -> Network tab -> всё видно
  // Или programmatically
  
  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      if (entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest') {
        console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`);
      }
    }
  });
  
  observer.observe({ entryTypes: ['resource'] });
};

9. Обработка ошибок в HTTP

const errorHandling = async () => {
  try {
    const response = await fetch('/api/users/123');
    
    // Проверяем статус
    if (!response.ok) {
      const error = await response.json();
      throw new Error(`HTTP ${response.status}: ${error.message}`);
    }
    
    return await response.json();
  } catch (error) {
    if (error instanceof TypeError) {
      // Сетевая ошибка (нет интернета, сервер недоступен)
      console.error('Сетевая ошибка:', error);
    } else if (error instanceof SyntaxError) {
      // Ответ не JSON
      console.error('Невалидный JSON:', error);
    } else {
      // Другая ошибка (включая HTTP ошибки)
      console.error('Ошибка:', error);
    }
  }
};

// Retry logic
const fetchWithRetry = async (url, maxRetries = 3) => {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, { timeout: 5000 });
      if (response.ok) return response;
      
      if (response.status >= 500) {
        throw new Error('Server error'); // retry
      }
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      const delay = Math.pow(2, i) * 1000; // exponential backoff
      await new Promise(r => setTimeout(r, delay));
    }
  }
};

Итог: как работает HTTP

  1. Клиент отправляет запрос (метод, URL, заголовки, тело)
  2. Сервер обрабатывает и отправляет ответ (статус, заголовки, тело)
  3. Браузер обрабатывает ответ и рендерит страницу
  4. Кэширование экономит время и трафик
  5. HTTP/2 делает множество запросов быстрым
  6. Обработка ошибок важна для UX

Это основание всей веб-разработки.