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>
Link Types Rewritten
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>
When using --static mode, script tags are removed entirely rather than rewritten.
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
External Links
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
--staticmode to remove JS - Manually fix JS files post-capture
- Ensure JS uses relative URLs
Next Steps
- Output Structure — File organization
- Troubleshooting — Common issues
- Static Mode — Strip JavaScript