Доступ к HTTP-заголовкам веб-страницы в JavaScript

Как получить доступ к заголовкам HTTP-ответа страницы через JavaScript?

Связанный с этим вопросом, который был изменен, чтобы спросить о доступе к двум определенным заголовкам HTTP.

Связанные с:
Как получить доступ к полям заголовка HTTP-запроса через JavaScript?

19 ответов

Решение

К сожалению, нет API, который бы предоставил вам заголовки ответа HTTP для вашего начального запроса страницы. Это был оригинальный вопрос, размещенный здесь. Это также неоднократно задавалось, потому что некоторые люди хотели бы получить фактические заголовки ответа исходного запроса страницы без выдачи другого.


Для запросов AJAX:

Если HTTP-запрос сделан через AJAX, возможно получить заголовки ответа с getAllResponseHeaders()метод. Это часть API XMLHttpRequest. Чтобы увидеть, как это можно применить, проверьтеfetchSimilarHeaders()функция ниже. Обратите внимание, что это обходной путь, который не будет надежным для некоторых приложений.

myXMLHttpRequest.getAllResponseHeaders();

Это не даст вам информацию о заголовках HTTP-ответа исходного запроса страницы, но ее можно использовать для обоснованного предположения о том, какими были эти заголовки. Подробнее об этом описано далее.


Получение значений заголовка из запроса начальной страницы:

Этот вопрос впервые был задан несколько лет назад, в частности, о том, как получить исходные заголовки ответа HTTP для текущей страницы (то есть той же самой страницы, внутри которой выполнялся JavaScript). Это совсем другой вопрос, чем просто получение заголовков ответов для любого HTTP-запроса. Для начального запроса страницы заголовки не всегда доступны для javascript. Будут ли значения заголовка, которые вам нужны, будут надежными и достаточно согласованными, если вы снова запросите ту же страницу через AJAX, будет зависеть от вашего конкретного приложения.

Ниже приведены несколько предложений по решению этой проблемы.


1. Запросы на ресурсы, которые в значительной степени статичны

Если ответ в значительной степени статичен, а заголовки не должны сильно меняться между запросами, вы можете сделать AJAX-запрос для той же страницы, на которой вы сейчас находитесь, и предположить, что это те же значения, которые были частью этой страницы. HTTP ответ. Это может позволить вам получить доступ к нужным заголовкам, используя красивый API XMLHttpRequest, описанный выше.

function fetchSimilarHeaders (callback) {
    var request = new XMLHttpRequest();
    request.onreadystatechange = function () {
        if (request.readyState === XMLHttpRequest.DONE) {
            //
            // The following headers may often be similar
            // to those of the original page request...
            //
            if (callback && typeof callback === 'function') {
                callback(request.getAllResponseHeaders());
            }
        }
    };

    //
    // Re-request the same page (document.location)
    // We hope to get the same or similar response headers to those which 
    // came with the current page, but we have no guarantee.
    // Since we are only after the headers, a HEAD request may be sufficient.
    //
    request.open('HEAD', document.location, true);
    request.send(null);
}

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


2. Сделайте выводы

Существуют некоторые свойства спецификации (объектная модель браузера), которые браузер определяет, просматривая заголовки. Некоторые из этих свойств отражают заголовки HTTP напрямую (например, navigator.userAgent устанавливается на значение HTTP User-Agent поле заголовка). Обнюхивая доступные свойства, вы сможете найти то, что вам нужно, или некоторые подсказки, чтобы указать, что содержится в HTTP-ответе.


3. Спрятать их

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

Невозможно прочитать текущие заголовки. Вы можете сделать еще один запрос к тому же URL и прочитать его заголовки, но нет никакой гарантии, что заголовки точно совпадают с текущими.


Используйте следующий код JavaScript, чтобы получить все заголовки HTTP, выполнив get запрос:

var req = new XMLHttpRequest();
req.open('GET', document.location, false);
req.send(null);
var headers = req.getAllResponseHeaders().toLowerCase();
alert(headers);

Ответ без дополнительного HTTP-вызова

Хотя в целом невозможно прочитать произвольные заголовки HTTP-ответа для HTML-навигации верхнего уровня, если вы управляете сервером (или промежуточными ящиками в пути) и хотите предоставить некоторую информацию для JavaScript, которую нельзя легко отобразить в любом другом способом, чем через заголовок:

Вы можете использовать заголовок для отображения произвольных данных ключ-значение, и он будет доступен для чтения JavaScript.

(* в поддерживаемых браузерах: Firefox 61, Chrome 65, Edge 79; Safari пока нет с 2021.02; нет IE)

Пример:

      server-timing: key;desc="value"
      server-timing: key1;desc="value1"
server-timing: key2;desc="value2"
  • или используйте его компактную версию, в которой вы представляете несколько фрагментов данных в одном заголовке, разделенных запятыми.
      server-timing: key1;desc="value1", key2;desc="value2"

Пример того, как Википедия использует этот заголовок для предоставления информации о попадании / промахах в кеш:

Пример кода (необходимо учитывать отсутствие поддержки браузеров в Safari и IE):

      if (window.performance && performance.getEntriesByType) { // avoid error in Safari 10, IE9- and other old browsers
    let navTiming = performance.getEntriesByType('navigation')
    if (navTiming.length > 0) { // still not supported as of Safari 14...
        let serverTiming = navTiming[0].serverTiming
        if (serverTiming && serverTiming.length > 0) {
            for (let i=0; i<serverTiming.length; i++) {
                console.log(`${serverTiming[i].name} = ${serverTiming[i].description}`)
            }
        }
    }
}

Это журналы cache = hit-front в поддерживаемых браузерах.

Примечания:

  • как упоминалось в MDN, API поддерживается только через HTTPS
  • если ваш JS обслуживается из другого домена, вам необходимо добавить заголовок ответа Timing-Allow-Origin, чтобы данные были доступны для чтения в JS ( Timing-Allow-Origin: * или же Timing-Allow-Origin: https://www.example.com)
  • Server-Timing заголовки также поддерживают dur(заголовок) поле, читаемое как duration на стороне JS, но это необязательно и по умолчанию 0 в JS, если не прошел
  • относительно поддержки Safari: см. ошибку 1 и ошибку 2
  • Вы можете узнать больше о синхронизации сервера в этом сообщении в блоге.
  • Обратите внимание, что буферы записей производительности могут быть очищены JS на странице (через вызов API) или браузером, если страница выдает слишком много вызовов для подресурсов. По этой причине вам следует как можно скорее записать данные и / или использовать PerformanceObserverAPI вместо этого. Подробнее см. Сообщение в блоге .

Решение с сервисными работниками

Сервисные работники имеют доступ к сетевой информации, которая включает заголовки. Хорошая часть заключается в том, что он работает с любыми запросами, а не только с XMLHttpRequest.

Как это устроено:

  1. Добавьте работника сервиса на свой сайт.
  2. Смотреть каждый запрос, который отправляется.
  3. Сделай сервисный работник fetch запрос с respondWith функция.
  4. Когда ответ придет, прочитайте заголовки.
  5. Отправьте заголовки от сервисного работника на страницу с postMessage функция.

Рабочий пример:

Работники сферы обслуживания немного сложны для понимания, поэтому я создал небольшую библиотеку, которая делает все это. Он доступен на github: https://github.com/gmetais/sw-get-headers.

Ограничения:

  • сайт должен быть на HTTPS
  • браузер должен поддерживать новый API Service Workers
  • действуют те же доменные / междоменные политики, что и в XMLHttpRequest

С помощью XmlHttpRequest Вы можете открыть текущую страницу, а затем просмотреть http-заголовки ответа.

Лучший вариант - просто сделать HEAD запрос, а затем изучить заголовки.

Некоторые примеры этого можно посмотреть на http://www.jibbering.com/2002/4/httprequest.html

Просто мои 2 цента.

Другой способ отправить информацию заголовка в JavaScript - через куки. Сервер может извлечь любые данные из заголовков запроса и отправить их обратно в Set-Cookie Заголовок ответа - и куки могут быть прочитаны в JavaScript. Однако, как говорит Кепаро, лучше сделать это только для одного или двух заголовков, а не для всех них.

Для тех, кто ищет способ разобрать все заголовки HTTP в объект, доступ к которому можно получить как словарь headers["content-type"]Я создал функцию parseHttpHeaders:

function parseHttpHeaders(httpHeaders) {
    return httpHeaders.split("\n")
     .map(x=>x.split(/: */,2))
     .filter(x=>x[0])
     .reduce((ac, x)=>{ac[x[0]] = x[1];return ac;}, {});
}

var req = new XMLHttpRequest();
req.open('GET', document.location, false);
req.send(null);
var headers = parseHttpHeaders(req.getAllResponseHeaders());
// Now we can do:  headers["content-type"]

Вы не можете получить доступ к заголовкам http, но некоторая информация, предоставленная в них, доступна в DOM. Например, если вы хотите увидеть http referer (sic), используйте document.referrer. Могут быть и другие подобные заголовки http. Попробуйте поискать нужную вещь, например, "http referer javascript".

Я знаю, что это должно быть очевидно, но я продолжал искать такие вещи, как "http заголовки javascript", когда все, что я действительно хотел - это реферер, и не получил никаких полезных результатов. Я не знаю, как я не понимал, что могу сделать более конкретный запрос.

Как и многие люди, я копался в сети без реального ответа:(

Тем не менее, я нашел обходной путь, который мог бы помочь другим. В моем случае я полностью контролирую свой веб-сервер. На самом деле это часть моего приложения (см. Конец ссылки). Мне легко добавить скрипт в мой http-ответ. Я изменил свой httpd-сервер, чтобы внедрить небольшой скрипт в каждую html-страницу. Я только добавляю дополнительную строку "js script" сразу после создания моего заголовка, который устанавливает существующую переменную из моего документа в моем браузере [я выбираю местоположение], но возможен любой другой вариант. Хотя мой сервер написан на nodejs, я не сомневаюсь, что эту же технику можно использовать из PHP или других.

  case ".html":
    response.setHeader("Content-Type", "text/html");
    response.write ("<script>location['GPSD_HTTP_AJAX']=true</script>")
    // process the real contend of my page

Теперь на каждой html-странице, загруженной с моего сервера, этот скрипт выполняется браузером при получении. Затем я могу легко проверить из JavaScript, существует ли переменная или нет. В моем сценарии использования мне нужно знать, должен ли я использовать профиль JSON или JSON-P, чтобы избежать проблемы с CORS, но эту же технику можно использовать для других целей [то есть: выбрать между сервером разработки / производства, получить с сервера REST/API ключ и т.д....]

В браузере вам просто нужно проверить переменную непосредственно из JavaScript, как в моем примере, где я использую ее для выбора своего профиля Json/JQuery

 // Select direct Ajax/Json profile if using GpsdTracking/HttpAjax server otherwise use JsonP
  var corsbypass = true;  
  if (location['GPSD_HTTP_AJAX']) corsbypass = false;

  if (corsbypass) { // Json & html served from two different web servers
    var gpsdApi = "http://localhost:4080/geojson.rest?jsoncallback=?";
  } else { // Json & html served from same web server [no ?jsoncallback=]
    var gpsdApi = "geojson.rest?";
  }
  var gpsdRqt = 
      {key   :123456789 // user authentication key
      ,cmd   :'list'    // rest command
      ,group :'all'     // group to retreive
      ,round : true     // ask server to round numbers
   };
   $.getJSON(gpsdApi,gpsdRqt, DevListCB);

Для тех, кто когда-либо хотел бы проверить мой код: https://www.npmjs.org/package/gpsdtracking

Ссылка Аллена Лалонда сделала мой день лучше. Просто добавьте сюда простой рабочий html-код.
Работает с любым разумным браузером, начиная с IE9+ и Presto-Opera 12.

<!DOCTYPE html>
<title>(XHR) Show all response headers</title>

<h1>All Response Headers with XHR</h1>
<script>
 var X= new XMLHttpRequest();
 X.open("HEAD", location);
 X.send();
 X.onload= function() { 
   document.body.appendChild(document.createElement("pre")).textContent= X.getAllResponseHeaders();
 }
</script>

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


Другой способ
более современныйfetch()API
https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
На https://caniuse.com/ он поддерживается Firefox 40, Chrome 42, Edge 14, Safari 11
Рабочий пример кода:

<!DOCTYPE html>
<title>fetch() all Response Headers</title>

<h1>All Response Headers with fetch()</h1>
<script>
 var x= "";
 if(window.fetch)
    fetch(location, {method:'HEAD'})
    .then(function(r) {
       r.headers.forEach(
          function(Value, Header) { x= x + Header + "\n" + Value + "\n\n"; }
       );
    })
    .then(function() {
       document.body.appendChild(document.createElement("pre")).textContent= x;
    });
 else
   document.write("This does not work in your browser - no support for fetch API");
</script>

Если мы говорим о заголовках запроса, вы можете создавать свои собственные заголовки при выполнении XmlHttpRequests.

var request = new XMLHttpRequest();
request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
request.open("GET", path, true);
request.send(null);

Чтобы получить заголовки как более удобный объект (улучшение ответа Раджи):

var req = new XMLHttpRequest();
req.open('GET', document.location, false);
req.send(null);
var headers = req.getAllResponseHeaders().toLowerCase();
headers = headers.split(/\n|\r|\r\n/g).reduce(function(a, b) {
    if (b.length) {
        var [ key, value ] = b.split(': ');
        a[key] = value;
    }
    return a;
}, {});
console.log(headers);

Используя mootools, вы можете использовать this.xhr.getAllResponseHeaders()

Я только что проверил, и это работает для меня, используя Chrome версии 28.0.1500.95.

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

var xhr = new XMLHttpRequest(); 
xhr.open('POST', url, true); 
xhr.responseType = "blob";
xhr.onreadystatechange = function () { 
    if (xhr.readyState == 4) {
        success(xhr.response); // the function to proccess the response

        console.log("++++++ reading headers ++++++++");
        var headers = xhr.getAllResponseHeaders();
        console.log(headers);
        console.log("++++++ reading headers end ++++++++");

    }
};

Выход:

Date: Fri, 16 Aug 2013 16:21:33 GMT
Content-Disposition: attachment;filename=testFileName.doc
Content-Length: 20
Server: Apache-Coyote/1.1
Content-Type: application/octet-stream

Это мой скрипт для получения всех заголовков ответа:

var url = "< URL >";

var req = new XMLHttpRequest();
req.open('HEAD', url, false);
req.send(null);
var headers = req.getAllResponseHeaders();

//Show alert with response headers.
alert(headers);

Наличие в результате заголовков ответа.

Это сравнительный тест с использованием Hurl.it:

Современный способ ES6+ с использованиемfetch

(и дополнительная просьба)

Имеет полную поддержку браузера.

      const resp = await fetch(document.location.href, {method: 'HEAD'});
const headers = Object.fromEntries(resp.headers.entries());

Многие другие ответы используюти большинство из них синхронный XHR, который зависает на странице во время запроса. Это приводит к ужасному пользовательскому опыту, когда пользователь в это время не может совершать какие-либо действия.

Ответ Гаэля превосходен, если у вас уже есть сервисный работник.

Ответ Якуба превосходен и эффективен, если внесение значений в синхронизацию сервера подходит для вашего случая.

Это старый вопрос. Не уверен, когда поддержка станет более широкой, но getAllResponseHeaders() а также getResponseHeader() кажется, теперь достаточно стандартным: http://www.w3schools.com/xml/dom_http.asp

Я думаю, что вопрос пошёл не так, как надо. Если вы хотите получить заголовок запроса из JQuery/JavaScript, ответ просто нет. Другие решения - создать страницу aspx или страницу jsp, тогда мы можем легко получить доступ к заголовку запроса. Возьмите все запросы на странице aspx и поместите их в сеанс / файлы cookie, чтобы получить доступ к файлам cookie на странице JavaScript.

Как уже упоминалось, если вы контролируете серверную часть, тогда должна быть возможность отправлять начальные заголовки запроса клиенту в первоначальном ответе.

В Express, например, работает следующее:

app.get('/somepage', (req, res) => { res.render('somepage.hbs', {headers: req.headers}); }) Заголовки затем доступны в шаблоне, поэтому могут быть скрыты визуально, но включены в разметку и прочитаны клиентским JavaScript.

Другие вопросы по тегам