117

I understand that you can set HTTP request headers very easily when making AJAX calls in JavaScript.

However is it also possible to set custom HTTP request headers when inserting an iframe into a page via script?

<iframe src="someURL"> <!-- is there any place to set headers in this? -->
onlywei
  • 1,447
  • 2
  • 11
  • 14

4 Answers4

108

You can make the request in javascript, setting any headers you'd like. Then you can URL.createObjectURL(), to get something suitable for the src of the iframe.

var xhr = new XMLHttpRequest();

xhr.open('GET', 'page.html');
xhr.onreadystatechange = handler;
xhr.responseType = 'blob';
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
xhr.send();

function handler() {
  if (this.readyState === this.DONE) {
    if (this.status === 200) {
      // this.response is a Blob, because we set responseType above
      var data_url = URL.createObjectURL(this.response);
      document.querySelector('#output-frame-id').src = data_url;
    } else {
      console.error('no pdf :(');
    }
  }
}

The MIME type of the response is preserved. So if you get an html response, the html will show in the iframe. If you requested a pdf, the browser pdf viewer will kick in for the iframe.

If this is part of a long-lived client-side app, you may want to use URL.revokeObjectURL() to avoid memory leaks.

The object URLs are also pretty interesting. They're of the form blob:https://your.domain/1e8def13-3817-4eab-ad8a-160923995170. You can actually open them in a new tab and see the response, and they're discarded when the context that created them is closed.

Here's a full example: https://github.com/courajs/pdf-poc

FellowMD
  • 2,142
  • 3
  • 16
  • 15
  • you tha man! I'm working on an Angular 5 component inspired by this code to show pdf previews in Angularjs. this has helped me greatly – FireDragon Jan 23 '18 at 14:42
  • 1
    @BSSchwarzkopf looks like you're right. Blob URLs are supported in Edge, but they won't work in the src attribute of an iframe. I consider it to be a violation of the spec: "This scheme should be able to be used with web APIs... and with elements that are designed to be used with HTTP URLs... In general, this scheme should be designed to be used wherever URLs can be used on the web." Issue on the Edge tracker: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/5411066/ Spec: https://www.w3.org/TR/FileAPI/#use-cases-scheme – FellowMD May 15 '18 at 18:44
  • 1
    I get "Failed to execute 'createObjectURL' on 'URL': No function was found that matched the signature provided." on Chrome 84.0.4147.105. – poiuytrez Jul 31 '20 at 13:51
  • 1
    @poiuytrez that means you passed the wrong kind of argument. It needs to a File, Blob, or MediaSource. Maybe you're passing it null, undefined, a promise, or a Request object? – FellowMD Aug 15 '20 at 12:21
  • thank you for the idea! I implemented it using Axios. – naXa stands with Ukraine Sep 21 '20 at 21:23
  • 3
    But if we face CORS error, how to overcome? – Sam Arul Raj T Mar 18 '21 at 06:58
  • @SamArulRajT did you found a way to solve CORS issue? – godblessstrawberry May 05 '22 at 10:33
  • 2
    This works great but I have a problem. The generated url is using the top level domain in the blob so the iframe is trying to load it's resources through the top level domain, for example localhost. Am I doing something wrong? – Souleste Aug 02 '22 at 22:38
  • @FellowMD Should this solution be working with external relative links? From my understanding no, since `src` is pointing to a `blob` and link can be e.g. `/sub-page`. – Mateusz Dec 29 '22 at 10:21
43

No, you can't. However you could set the iframe source to some kind of preload script, which uses AJAX to fetch the actual page with all the headers you want.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
8

As @FellowMD answer is not working on modern browsers due to the depreciation of createObjectURL, I used the same approach but using iframe srcDoc attribute.

  1. Retrieve the content to display in the iframe using XMLHttpRequest or any other method
  2. Set the srcdoc parameter of the iframe

Please find below a React example (I know it is overkill):

import React, {useEffect, useState} from 'react';

function App() {
  const [content, setContent] = useState('');


  useEffect(() => {
    // Fetch the content using the method of your choice
    const fetchedContent = '<h1>Some HTML</h1>';
    setContent(fetchedContent);
  }, []);


  return (
    <div className="App">
      <iframe sandbox id="inlineFrameExample"
              title="Inline Frame Example"
              width="300"
              height="200"
              srcDoc={content}>
      </iframe>


    </div>
  );
}

export default App;

Srcdoc is now supported on most browsers. It seems that Edge was a bit late to implement it: https://caniuse.com/#feat=iframe-srcdoc

poiuytrez
  • 21,330
  • 35
  • 113
  • 172
  • 1
    `createObjectURL` is only being deprecated for MediaStream arguments. Passing a Blob is not deprecated, and in fact [sees pretty wide and increasing usage](https://www.chromestatus.com/metrics/feature/timeline/popularity/1604). I appreciate the effort to keep things up to date though :) – FellowMD Oct 24 '20 at 14:24
  • @poiuytrez when I want fetch the content with custom header, I get error as asked in [this question](https://stackoverflow.com/questions/66151403/wrong-behaviour-of-the-dynamically-populated-iframe) Could you please look at it – eneski Feb 11 '21 at 15:02
  • This solution does not show how to set the headers in the request to fetchContent – 32423hjh32423 Aug 18 '21 at 07:58
  • fetch('url', { headers: { token: your_token } }) – Alexey Kostyuhin May 16 '22 at 08:01
2

It turns out that URL.createObjectURL() is deprecated in Chrome 71
(see https://developers.google.com/web/updates/2018/10/chrome-71-deps-rems)
Building on @Niet the dark Absol and @FellowMD's excellent answers, here's how to load a file into an iframe, if you need to pass in authentication headers. (You can't just set the src attribute to the URL):

$scope.load() {
    var iframe = #angular.element("#reportViewer");
    var url = "http://your.url.com/path/etc";
    var token = "your-long-auth-token";
    var headers = [['Authorization', 'Bearer ' + token]];
    $scope.populateIframe(iframe, url, headers);
}

$scope.populateIframe = function (iframe, url, headers) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onreadystatechange = handler;
    xhr.responseType = 'document';
    headers.forEach(function (header) {
        xhr.setRequestHeader(header[0], header[1]);
    });
    xhr.send();

    function handler() {
        if (this.readyState === this.DONE) {
            if (this.status === 200) {
                var content = iframe[0].contentWindow ||
                    iframe[0].contentDocument.document || 
                    iframe[0].contentDocument;
                content.document.open();
                content.document.write(this.response.documentElement.innerHTML);
                content.document.close();
            } else {
                iframe.attr('srcdoc', '<html><head></head><body>Error loading page.</body></html>');
            }
        }
    }
}

and shoutout to courajs: https://github.com/courajs/pdf-poc/blob/master/script.js

TomEberhard
  • 907
  • 10
  • 11
  • 5
    From the Google link: "The URL.createObjectURL() method has been removed from the MediaStream interface." Is this deprecation that affects the MediaStream interface relevant to the other answer? (I would think not.) – Jared Thirsk Apr 11 '20 at 00:09
  • 4
    Not deprecated. Only removed from MediaStream – TheMaster Jul 29 '20 at 16:38
  • 1
    @TheMaster that is indeed what the documentation says, but I spent a few hours trying to get it to work and wasn't successful. It can't speculate on why. The code shown above is what ended up working at the time I coded it, I don't have the bandwidth to try it again. – TomEberhard Jul 30 '20 at 20:02
  • You can use that method with Blob objects. In your case that would be like `URL.createObjectURL(new Blob([this.response.documentElement.innerHTML]))` – u.unver34 Oct 01 '20 at 03:13
  • 2
    `createObjectURL` is only being deprecated for MediaStream arguments. Passing a Blob is not deprecated, and in fact [sees pretty wide and increasing usage](https://www.chromestatus.com/metrics/feature/timeline/popularity/1604). I appreciate the effort to keep things up to date though :) – FellowMD Oct 24 '20 at 14:23