Link Rewriting

Smippo automatically rewrites all links to work offline. This transforms absolute URLs into relative paths pointing to local files.

Why Rewriting is Necessary

When a browser loads https://example.com, resources are referenced with absolute or root-relative URLs:

<link href="https://cdn.example.com/style.css" rel="stylesheet">
<img src="/images/logo.png">
<a href="https://example.com/about">About</a>

These won't work when opened from a local file. Smippo rewrites them to relative paths:

<link href="./cdn.example.com/style.css" rel="stylesheet">
<img src="./images/logo.png">
<a href="./about/index.html">About</a>

HTML Links (<a href>)

<!-- Before -->
<a href="https://example.com/about">About</a>
<a href="/contact">Contact</a>
<a href="../faq">FAQ</a>

<!-- After -->
<a href="./about/index.html">About</a>
<a href="./contact/index.html">Contact</a>
<a href="../faq/index.html">FAQ</a>

Stylesheet Links (<link href>)

<!-- Before -->
<link href="https://example.com/style.css" rel="stylesheet">
<link href="//cdn.example.com/theme.css" rel="stylesheet">

<!-- After -->
<link href="./style.css" rel="stylesheet">
<link href="./cdn.example.com/theme.css" rel="stylesheet">

Script Sources (<script src>)

<!-- Before -->
<script src="https://example.com/app.js"></script>
<script src="/vendor/react.js"></script>

<!-- After -->
<script src="./app.js"></script>
<script src="./vendor/react.js"></script>

Images (<img src>, <img srcset>)

<!-- Before -->
<img src="https://example.com/logo.png">
<img srcset="small.jpg 300w, large.jpg 800w">

<!-- After -->
<img src="./logo.png">
<img srcset="./small.jpg 300w, ./large.jpg 800w">

Source Elements (<source>)

<!-- Before -->
<source src="https://cdn.example.com/video.mp4" type="video/mp4">

<!-- After -->
<source src="./cdn.example.com/video.mp4" type="video/mp4">

Iframes (<iframe src>)

<!-- Before -->
<iframe src="https://example.com/embed"></iframe>

<!-- After -->
<iframe src="./embed/index.html"></iframe>

Object Data (<object data>)

<!-- Before -->
<object data="https://example.com/diagram.svg"></object>

<!-- After -->
<object data="./diagram.svg"></object>

CSS URL Rewriting

url() References

/* Before */
.hero {
  background-image: url('https://example.com/hero.jpg');
}

@font-face {
  src: url('/fonts/inter.woff2');
}

/* After */
.hero {
  background-image: url('./hero.jpg');
}

@font-face {
  src: url('./fonts/inter.woff2');
}

@import Statements

/* Before */
@import url('https://fonts.googleapis.com/css2?family=Inter');
@import 'components/buttons.css';

/* After */
@import url('./fonts.googleapis.com/css2-family-Inter.css');
@import './components/buttons.css';

Inline Styles

<!-- Before -->
<div style="background: url('/bg.png')">

<!-- After -->
<div style="background: url('./bg.png')">

Relative Path Calculation

Smippo calculates relative paths based on the current file's location:

Same Directory

File: example.com/about/index.html
Link: example.com/about/team.html
Result: ./team.html

Parent Directory

File: example.com/about/team/index.html
Link: example.com/about/index.html
Result: ../index.html

Different Branch

File: example.com/about/index.html
Link: example.com/blog/post.html
Result: ../blog/post.html

Cross-Domain (with --external-assets)

File: example.com/index.html
Link: cdn.example.com/style.css
Result: ../cdn.example.com/style.css

Same Scope

Links within the capture scope are rewritten:

<a href="https://example.com/docs">  →  <a href="./docs/index.html">

Out of Scope

Links outside the capture scope are preserved:

<a href="https://github.com/example">  →  <a href="https://github.com/example">

External Assets

With --external-assets, external resources are captured and rewritten:

<!-- CDN styles -->
<link href="https://cdn.tailwindcss.com/style.css">

<link href="./cdn.tailwindcss.com/style.css">

Without --external-assets, they're preserved as absolute URLs.

Special Cases

Hash Links

Anchor links within the same page are preserved:

<a href="#section">  →  <a href="#section">

Query Strings

Query strings in resource URLs are encoded:

<!-- Before -->
<img src="https://example.com/image?size=large&format=webp">

<!-- After -->
<img src="./image-size-large-format-webp">

Protocol-Relative URLs

Protocol-relative URLs (//) are converted:

<script src="//cdn.example.com/lib.js">

<script src="./cdn.example.com/lib.js">

Data URLs

Data URLs are preserved as-is:

<img src="data:image/png;base64,iVBORw0KGgo...">

Blob URLs

Blob URLs cannot be captured and are removed or replaced.

JavaScript URL Handling

Smippo attempts to rewrite URLs in JavaScript strings, but this is best-effort:

// May be rewritten
const url = "https://example.com/api";

// Complex cases may not be detected
const base = "https://example.com";
const path = "/api";
const url = base + path;  // Not rewritten

For reliable offline JavaScript, use --static mode to strip scripts, or ensure your app handles relative URLs.

Verification

After capture, you can verify rewriting worked:

# Check a specific file
grep -E 'href=|src=' ./site/example.com/index.html

# Find any remaining absolute URLs
grep -r 'https://' ./site/*.html

Troubleshooting

Broken Images

Images may not load if:

  • The image wasn't captured (filtered out)
  • The link wasn't properly rewritten

Check if the file exists:

ls ./site/example.com/images/

Missing Styles

Check that CSS files were captured and url() references were rewritten:

cat ./site/example.com/style.css | grep url

JavaScript Errors

JavaScript relying on absolute URLs may break. Solutions:

  • Use --static mode to remove JS
  • Manually fix JS files post-capture
  • Ensure JS uses relative URLs

Next Steps

Was this page helpful?