Skip to content

Server Modes

Stowry supports three server modes that change how requests are handled. Choose the mode that fits your use case.

ModeUse CasePath Fallback
storeObject storage APINone (404 if not found)
staticStatic file server{path}.html{path}/index.html
spaSingle-page application/index.html

The default mode for object storage operations.

server:
mode: store
  • GET /{path} - Returns the exact file or 404
  • GET / - Returns JSON list of objects
  • Full CRUD operations (PUT, DELETE)
  • No automatic fallbacks
  • Backend file storage for applications
  • User upload handling
  • Asset management systems
  • API-driven file operations
Terminal window
# Start in store mode
./stowry serve --mode store
# Upload a file
curl -X PUT -d "content" http://localhost:5708/documents/report.pdf
# Download - returns exact file
curl http://localhost:5708/documents/report.pdf # 200 OK
# Missing file - returns 404
curl http://localhost:5708/documents/missing.pdf # 404 Not Found
# List objects
curl http://localhost:5708/ # JSON response

Serves static files with S3+CloudFront-style path resolution and clean URLs.

server:
mode: static
error_document: 404.html # optional custom error page
  • GET /{path} - Tries exact match → {path}.html{path}/index.html
  • GET /{path}/ - Tries {path}/index.html (trailing slash = directory index)
  • GET / - Returns /index.html
  • Missing paths return an HTML 404 page (default Stowry-branded, or custom via error_document)
  • Read-only: PUT and DELETE return 405 Method Not Allowed
  • Always public access (auth settings are ignored)
  • Use stowry add or store mode to populate content

For /foo (no trailing slash):

  1. Try exact path foo
  2. Try foo.html (clean URLs)
  3. Try foo/index.html (directory index)
  4. Return 404 error page

For /foo/ (trailing slash):

  1. Try foo/index.html (directory index)
  2. Return 404 error page

By default, Stowry serves a minimal HTML 404 page:

<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>stowry</center>
</body>
</html>

Configure a custom error page:

server:
error_document: 404.html

The custom error document is served from storage with a 404 status code. If the custom document is not found, the default page is used.

  • Static website hosting
  • Documentation sites
  • Marketing pages
  • Any site with traditional URL structure

Given this storage structure:

data/
├── index.html
├── about.html
├── docs/
│ └── index.html
├── blog/
│ ├── index.html
│ └── post-1.html
└── assets/
└── style.css

Request handling:

GET / → data/index.html
GET /about → data/about.html (clean URL)
GET /about.html → data/about.html (direct)
GET /docs/ → data/docs/index.html (trailing slash)
GET /docs → data/docs/index.html (fallback chain)
GET /blog/post-1.html → data/blog/post-1.html
GET /assets/style.css → data/assets/style.css
GET /missing → 404 HTML page

Designed for single-page applications with client-side routing.

server:
mode: spa
  • GET /{path} - Returns file if exists, otherwise /index.html
  • Enables client-side routing (React Router, Vue Router, etc.)
  • Read-only: PUT and DELETE return 405 Method Not Allowed
  • Always public access (auth settings are ignored)
  • Use stowry add or store mode to populate content
  1. Try exact path
  2. If not found, return /index.html
  3. Client-side JavaScript handles routing
  • React applications
  • Vue.js applications
  • Angular applications
  • Any SPA with client-side routing

Given this storage structure:

dist/
├── index.html
├── assets/
│ ├── main.js
│ └── style.css
└── favicon.ico

Request handling:

GET / → dist/index.html
GET /dashboard → dist/index.html (SPA handles route)
GET /users/123 → dist/index.html (SPA handles route)
GET /assets/main.js → dist/assets/main.js
GET /favicon.ico → dist/favicon.ico

Ensure your SPA handles the current URL on load:

React Router:

import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/users/:id" element={<UserProfile />} />
</Routes>
</BrowserRouter>
);
}

Vue Router:

import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/dashboard', component: Dashboard },
{ path: '/users/:id', component: UserProfile },
],
});

RequestStoreStaticSPA
GET /List objects (JSON)/index.html/index.html
GET /aboutExact file or 404aboutabout.htmlabout/index.html/index.html
GET /about/Exact file or 404about/index.html/index.html
GET /file.txtExact file or 404Exact file or 404Exact file or 404
GET /missing404 (JSON)404 (HTML)/index.html
OperationStoreStaticSPA
PUT /{path}Allowed405405
DELETE /{path}Allowed405405
GET / (list)JSON listindex.htmlindex.html

Use Store when:

  • Building an API-driven application
  • You need full control over responses
  • Implementing custom 404 handling on the client
  • File paths are dynamic/generated

Use Static when:

  • Hosting a traditional static website
  • Each URL corresponds to a specific page
  • You want automatic directory index resolution
  • SEO is important (each route has its own file)

Use SPA when:

  • Building a single-page application
  • Using client-side routing
  • All routes should load the same entry point
  • Deep linking should work for all routes

Since static and SPA modes are read-only, use one of these methods to add files:

Terminal window
# Using the add command (recommended)
stowry add -r ./dist
# Using store mode temporarily
stowry serve --mode store
# Upload files via PUT, then restart in static/spa mode

You can change modes without modifying your stored files:

Terminal window
# Development with SPA mode
./stowry serve --mode spa
# Production static hosting
./stowry serve --mode static
# API mode for backend
./stowry serve --mode store

Or via configuration:

server:
mode: ${STOWRY_MODE:-store} # Default to store, override with env var