Static File Serving
SpaceNode has built-in static file serving with zero configuration. Supports SPA mode, clean URLs, MIME types, and security.
Enable Static Serving
const app = await createApp({
static: './public', // path relative to entry script
})
Modes
| Mode | Config | Behavior |
| SPA (default) | spa: true | Assets served normally. Unknown paths → index.html |
| Static Site | spa: false | All files served incl. .html. Clean URLs. 404 on missing |
| API Only | No static | No file serving. Pure API mode |
SPA Mode (default)
const app = await createApp({ static: './public' })
// GET /style.css → serves public/style.css
// GET /app.js → serves public/app.js
// GET /api/users → API route (module handler)
// GET /about → serves public/index.html (fallback)
// GET /any/random/path → serves public/index.html (fallback)
Static Site Mode
const app = await createApp({ static: './public', spa: false })
// GET /style.css → serves public/style.css
// GET /about.html → serves public/about.html
// GET /about → serves public/about.html (clean URL)
// GET /nonexist → serves public/404.html (custom 404 page)
Custom 404 Page
SpaceNode automatically serves a custom HTML 404 page when a request doesn't match any route or static file. Just place a 404.html file in your static directory:
public/
index.html
404.html ← auto-detected, no config needed
style.css
app.js
How It Works
| Scenario | Behavior |
404.html exists in static dir | Served with Content-Type: text/html and status 404 |
404.html does not exist | Falls back to JSON: {"error": "Not found"} |
SPA mode (spa: true) | index.html is served as fallback (SPA router handles 404) |
Resolution Order
// Static site mode (spa: false):
// 1. Try exact static file → /style.css
// 2. Try clean URL (.html) → /about → about.html
// 3. Try clean URL (index.html) → /docs → docs/index.html
// 4. Try API route match → router.find()
// 5. Serve 404.html (if exists) → custom 404 page
// 6. JSON fallback → {"error": "Not found"}
Example 404 Page
<!-- public/404.html -->
<!DOCTYPE html>
<html>
<head>
<title>404 — Page Not Found</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>404</h1>
<p>This page doesn't exist.</p>
<a href="/">← Back to Home</a>
</body>
</html>
Note: The 404 page is served with Cache-Control: no-cache to ensure users always see the latest version. Your 404.html can reference any CSS/JS from your static directory — it's a regular HTML file with full access to your site's styles.
LRU Cache & ETag
SpaceNode caches static files in memory using an LRU (Least Recently Used) cache. Cached files are served with ETag headers for 304 Not Modified support:
const app = await createApp({
static: './public',
staticCacheMax: 500, // max files in LRU cache (default: 500)
staticCacheFileSize: 256 * 1024, // max file size to cache (default: 256 KB)
})
| Config | Default | Description |
staticCacheMax | 500 | Maximum number of files held in the LRU cache |
staticCacheFileSize | 262144 (256 KB) | Files larger than this are streamed from disk (not cached) |
How it works
- First request — file is read from disk, cached in memory with its ETag
- Subsequent requests — served directly from cache
- If-None-Match — if the client sends the ETag, server returns 304 Not Modified (zero body transfer)
- Large files — files exceeding
staticCacheFileSize are streamed from disk with a weak ETag (W/"size-mtime"), not cached
- LRU eviction — when the cache is full, the least recently used file is evicted
Features
- 30+ MIME types — HTML, CSS, JS, JSON, images, fonts, video, audio, WebAssembly
Show all supported extensions
| Extension | MIME Type |
.html | text/html |
.css | text/css |
.js, .mjs | application/javascript |
.json, .map | application/json |
.png | image/png |
.jpg, .jpeg | image/jpeg |
.gif | image/gif |
.svg | image/svg+xml |
.ico | image/x-icon |
.webp | image/webp |
.avif | image/avif |
.woff | font/woff |
.woff2 | font/woff2 |
.ttf | font/ttf |
.otf | font/otf |
.eot | application/vnd.ms-fontobject |
.mp4 | video/mp4 |
.webm | video/webm |
.mp3 | audio/mpeg |
.ogg | audio/ogg |
.wav | audio/wav |
.pdf | application/pdf |
.zip | application/zip |
.wasm | application/wasm |
.xml | application/xml |
.txt | text/plain |
- LRU memory cache — small files cached in memory for instant serving
- ETag + 304 — automatic conditional requests, saves bandwidth
- Large file streaming — files above cache threshold are streamed from disk
- Directory traversal protection — paths normalized, checked to stay within static dir
- Cache headers — Fonts:
max-age=31536000, immutable. Other: max-age=3600
- Custom 404 page — place
404.html in static dir for branded error pages
- Priority: Static files → API routes → SPA fallback / 404.html