DEV Community

frank
frank

Posted on

Markdown Images Are Network Requests, Not Embedded Assets

The line ![diagram](../assets/flow.png) does not embed an image. It creates an image node whose destination still has to be resolved, fetched, allowed, and rendered by another system.

That distinction explains why a document can parse perfectly and still show broken images—or make network requests its author did not account for.

Parsing and URL resolution are separate stages

I fixed the experiment to Node 25.3.0 and Marked 18.0.7 with GFM enabled:

const html = marked.parse('![diagram](../assets/flow.png "Flow")');
// <p><img src="../assets/flow.png" alt="diagram" title="Flow"></p>
Enter fullscreen mode Exit fullscreen mode

Marked preserves the destination. A browser or publisher later resolves it against a base URL:

new URL("../assets/flow.png", "https://docs.example.test/guides/setup/").href
// https://docs.example.test/guides/assets/flow.png

new URL("../assets/flow.png", "https://docs.example.test/guides/setup.html").href
// https://docs.example.test/assets/flow.png
Enter fullscreen mode Exit fullscreen mode

Same Markdown, different request. The parser did its job in both cases.

Six cases that need different policies

Input Parser output What the host must decide
../assets/flow.png relative src base URL and asset copy rules
/assets/logo.svg root-relative src which origin owns the root
https://.../pixel.png?doc=42 absolute src remote trust and request privacy
data:image/... data URL sanitizer and CSP policy
empty alt alt="" decorative or accessibility defect
empty destination src="" reject, rewrite, or host behavior

CommonMark defines image syntax and maps the description to alt text. It does not upload files or promise that a URL exists.

Remote images are third-party dependencies

When HTML contains a remote <img src>, the browser requests that resource. The remote server observes the request; the page's Referrer Policy controls how much referring-page information is sent. Query parameters can also encode a document or campaign identifier.

Treat remote images like any other external dependency:

  • allow only reviewed schemes and hosts;
  • proxy or self-host assets when appropriate;
  • set a deliberate Referrer Policy;
  • restrict image sources with CSP img-src;
  • sanitize final HTML after AST transformations;
  • never put sensitive identifiers in image URLs.

Parser configuration cannot replace those controls.

Relative paths need a publishing contract

A stable workflow defines:

  1. where the Markdown file lives;
  2. what base URL the final page uses;
  3. how referenced assets are copied;
  4. whether root-relative paths are allowed;
  5. what happens when an image is missing.

If you are debugging a real document, this Markdown image path guide provides a concrete path checklist. The important step is to test the published URL, not just the editor preview.

Data URLs trade path stability for other costs

Data URLs avoid a separate file lookup, but they make source files larger and harder to review, prevent independent caching, and may be blocked by a sanitizer or CSP. They are reasonable for small controlled assets, not a universal fix for image portability.

Validate the final request graph

My recommended pipeline is:

  1. parse Markdown to an AST;
  2. inspect image destinations;
  3. resolve them against the declared publication base;
  4. validate scheme, host, path, and query;
  5. copy or rewrite managed assets;
  6. sanitize rendered HTML and apply CSP/referrer policy;
  7. open the public page and verify actual responses and layout.

The AST tells you what the author referenced. URL resolution tells you where it points. A browser test tells you what really happened.

Should Markdown hosts block cross-origin images by default, or preserve compatibility and require each application to opt into a stricter policy?

Primary references

Top comments (0)