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

ModeConfigBehavior
SPA (default)spa: trueAssets served normally. Unknown paths → index.html
Static Sitespa: falseAll files served incl. .html. Clean URLs. 404 on missing
API OnlyNo staticNo 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

ScenarioBehavior
404.html exists in static dirServed with Content-Type: text/html and status 404
404.html does not existFalls 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)
})
ConfigDefaultDescription
staticCacheMax500Maximum number of files held in the LRU cache
staticCacheFileSize262144 (256 KB)Files larger than this are streamed from disk (not cached)

How it works

  1. First request — file is read from disk, cached in memory with its ETag
  2. Subsequent requests — served directly from cache
  3. If-None-Match — if the client sends the ETag, server returns 304 Not Modified (zero body transfer)
  4. Large files — files exceeding staticCacheFileSize are streamed from disk with a weak ETag (W/"size-mtime"), not cached
  5. LRU eviction — when the cache is full, the least recently used file is evicted

Features