Back to guides
By Khalid DanishyarAbout 4 min read

HTML outputs, and when Jupyter calls a notebook trusted

Saved HTML and SVG can do more than show a table. Trust is a local Jupyter decision. It does not travel with the file you email.

Updated

Reading a notebook locally, where saved outputs are displayed without starting a kernel

A notebook can show a table, a styled dataframe, or an SVG chart without running any code, because that HTML was saved the last time a kernel ran. The same slot can hold markup you would not want a browser to act on. Jupyter treats that as a trust decision. The decision is local. It is not a flag that travels with the file.

This is separate from “does the Python look safe.” Stripping outputs, which the cleaning guide covers, removes the recording. Trust is about whether a program should render that recording as HTML.

A notebook read locally from saved cells, without a kernel
A notebook read locally from saved cells, without a kernel

What gets stored as HTML

For execute_result and display_data, the interesting field is data.

  • text/plain is text. A viewer can show it in a fixed-width block and it will not do anything.
  • text/html is a fragment. Pandas uses it for tables. IPython.display.HTML uses it for whatever string the author passed.
  • image/svg+xml is markup too. A drawing can contain more than shapes.
  • application/javascript is a script output. Some older display helpers write one.

A normal dataframe is the boring case: a <table> and some CSS. The file is still “HTML output” and it is usually harmless. The format does not have a separate type for “safe table” versus “markup someone constructed.” Both are text/html.

Markdown source can also contain raw HTML. That is the cell the author typed, not an output. A renderer that turns Markdown into HTML will face the same question there.

What Jupyter means by trusted

Jupyter’s notebook UI will show outputs from a file you just opened. It will not treat JavaScript in those outputs as something to run unless the notebook is trusted.

Trust is a signature checked by that Jupyter server, stored in its own signature database, not a portable "trusted": true field that every other tool must honor. Change the notebook and the signature no longer matches. Send the file to someone else and their Jupyter has never signed it, so they get an untrusted notebook again.

When Jupyter asks you to trust a notebook, it is asking you to accept the outputs, including any HTML, as something you are willing to render with the privileges of that page. Trusting does not re-run the Python. It does not prove the outputs match the source. It only changes how far the frontend is willing to go when it displays what was saved.

If you did not run the notebook yourself, the honest default is to leave it untrusted until you have read the outputs or removed them.

A viewer is not a kernel, and HTML is still HTML

Opening a notebook in a viewer does not start Python. That limit is real, and it is the one that matters for “will this cell execute.” It does not, by itself, answer “will this page interpret saved HTML.”

Any tool that inserts text/html or SVG into a browser page is letting the browser parse that markup. A <script> block assigned as HTML text often does nothing, because browsers do not run scripts introduced that way. Event-handler attributes and other active markup are a different story. You should assume a hostile HTML output can do something in a page that renders it raw.

So “I only viewed it” is not the same sentence as “I only looked at the text.” If the file is from someone you do not know, prefer the plain-text output, or strip outputs before you open it in a tool that renders HTML. The viewer on this site shows saved HTML and SVG so that ordinary tables and figures are readable. It does not start a kernel. It also does not certify that an HTML output is inert. Do not use it as a sandbox for a notebook you already suspect.

The HTML export has the same boundary. An HTML file is a page. Saved HTML outputs inside it are part of that page.

What to do with a notebook you are willing to share

Decide which recording you meant to publish.

  • If the point is the code, strip outputs. HTML, images, and tracebacks leave with them. Secrets printed into an output leave too. Secrets typed into source do not.
  • If the point is a table or a figure, keep those outputs and accept that you are publishing that HTML and those images. Open the HTML as text once and confirm it is a table or a picture, not a block you do not recognize.
  • If the notebook is untrusted in Jupyter, do not click Trust just to make a widget move, and then forward the same file as if the click had checked it.

The public sample notebook has a stdout stream and no text/html. There is nothing in it for a browser to interpret as a document. That is the shape of a file you can hand to a renderer without a trust conversation. Most real notebooks are not that shape, because a displayed dataframe is HTML on purpose.

A short test

  1. Search the JSON for text/html, image/svg+xml, and application/javascript.
  2. If the only hits are pandas tables you produced, you already know what they are.
  3. If you find HTML you did not generate, do not render it to “see what it does.” Read it as text, or delete the output in a copy.
  4. If you need a file other people can open without that question, send the copy with outputs removed and keep the original locally.

Reading a notebook without Jupyter is about seeing cells when you have no kernel. It assumes you are willing to display what the file stored. This page is the check you do first when the file is not yours.