Static files
This page shows how to serve static files with Vix Core.
Use it when you want to expose assets such as HTML, CSS, JavaScript, images, icons, fonts, or public files from a directory.
Public header
#include <vix.hpp>You can also include the app header directly:
#include <vix/app/App.hpp>What static files provide
Static files let a Vix application serve files from the filesystem.
Use static files for:
- HTML files
- CSS files
- JavaScript files
- images
- icons
- fonts
- downloads
- frontend assets
- public folders
Static files are mounted with app.static_dir(...).
app.static_dir("public", "/");Basic static directory
#include <vix.hpp>
int main()
{
vix::App app;
app.static_dir("public", "/");
app.run(8080);
return 0;
}With this structure:
public/
index.html
style.css
app.jsThese requests can be served:
GET /index.html
GET /style.css
GET /app.jsMount under a prefix
Mount a directory under a URL prefix.
#include <vix.hpp>
int main()
{
vix::App app;
app.static_dir("public/assets", "/assets");
app.run(8080);
return 0;
}With this structure:
public/
assets/
style.css
logo.svgThese requests can be served:
GET /assets/style.css
GET /assets/logo.svgStatic directory signature
static_dir accepts:
app.static_dir(
root,
mount,
index_file,
add_cache_control,
cache_control,
fallthrough);Arguments:
| Argument | Purpose |
|---|---|
root | Filesystem directory to serve. |
mount | URL prefix where files are exposed. |
index_file | File used when a directory is requested. |
add_cache_control | Whether to add a Cache-Control header. |
cache_control | Cache-Control header value. |
fallthrough | Whether missing files should continue to normal 404 handling. |
Default options
The default call:
app.static_dir("public", "/");is equivalent to:
app.static_dir(
"public",
"/",
"index.html",
true,
"public, max-age=3600",
true);Root directory
The first argument is the filesystem directory.
app.static_dir("public", "/");Example:
public/style.csscan be served as:
GET /style.cssMount path
The second argument is the URL mount point.
app.static_dir("public/assets", "/assets");Example:
public/assets/app.csscan be served as:
GET /assets/app.cssIndex file
When the requested path maps to a directory, Vix can serve an index file.
app.static_dir("public", "/", "index.html");Request:
GET /can serve:
public/index.htmlRequest:
GET /docscan serve:
public/docs/index.htmlwhen public/docs is a directory.
Cache-Control
By default, static files can receive a cache header.
app.static_dir(
"public",
"/assets",
"index.html",
true,
"public, max-age=3600",
true);This can add:
Cache-Control: public, max-age=3600Disable automatic cache control:
app.static_dir(
"public",
"/assets",
"index.html",
false,
"",
true);Fallthrough
The fallthrough option controls what happens when a file is not found.
When fallthrough is true, missing files continue to the normal not-found handling.
app.static_dir(
"public",
"/assets",
"index.html",
true,
"public, max-age=3600",
true);When fallthrough is false, Vix returns a static-file 404 immediately.
app.static_dir(
"public",
"/assets",
"index.html",
true,
"public, max-age=3600",
false);Static files and routing
Static files are integrated through the not-found fallback.
The request flow is:
request
-> router
-> route found?
yes -> run route handler
no -> try static files
found -> send file
missing -> default 404This means explicit routes have priority over static files.
app.get("/", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.text("home route");
});
app.static_dir("public", "/");For:
GET /the route handler runs first.
Serve one file from a handler
Use res.file(...) when you want to send a single file from a route.
#include <vix.hpp>
int main()
{
vix::App app;
app.get("/download", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.file("public/files/report.pdf");
});
app.run(8080);
return 0;
}Use static_dir(...) when you want to expose a whole directory.
app.static_dir("public", "/assets");Serve a frontend app
A common setup is to mount a built frontend directory.
#include <vix.hpp>
int main()
{
vix::App app;
app.static_dir("dist", "/");
app.run(8080);
return 0;
}With:
dist/
index.html
assets/
app.js
style.cssRequests can resolve to:
GET /
GET /assets/app.js
GET /assets/style.cssServe assets under /assets
#include <vix.hpp>
int main()
{
vix::App app;
app.static_dir("public/assets", "/assets");
app.get("/", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.file("public/index.html");
});
app.run(8080);
return 0;
}Example mapping:
GET /assets/logo.svgserves:
public/assets/logo.svgHEAD requests
Static file serving supports GET and HEAD.
For HEAD, Vix resolves the file but sends the response without the file body.
HEAD /assets/style.cssThis is useful for clients that only need headers.
Supported MIME types
Vix detects common MIME types from file extensions.
| Extension | Content-Type |
|---|---|
.html | text/html; charset=utf-8 |
.css | text/css; charset=utf-8 |
.js | application/javascript; charset=utf-8 |
.json | application/json; charset=utf-8 |
.png | image/png |
.jpg | image/jpeg |
.jpeg | image/jpeg |
.gif | image/gif |
.svg | image/svg+xml |
.ico | image/x-icon |
.txt | text/plain; charset=utf-8 |
.woff | font/woff |
.woff2 | font/woff2 |
Unknown extensions use:
application/octet-streamPath safety
Static file serving performs basic path traversal checks.
A request path that resolves to a path containing .. is rejected.
GET /assets/../../secret.txtThis returns a bad request response instead of serving the file.
Directory requests
When a request maps to a directory, Vix appends the configured index file.
app.static_dir("public", "/", "index.html");Request:
GET /docscan map to:
public/docs/index.htmlMissing file
When a file does not exist and fallthrough is enabled:
app.static_dir("public", "/assets", "index.html", true, "public, max-age=3600", true);Request:
GET /assets/missing.csscontinues to the default not-found handler.
When fallthrough is disabled:
app.static_dir("public", "/assets", "index.html", true, "public, max-age=3600", false);Vix returns a static-file 404.
Multiple static mounts
You can register multiple static directories.
app.static_dir("public/assets", "/assets");
app.static_dir("public/uploads", "/uploads");
app.static_dir("public/docs", "/docs");Example mappings:
GET /assets/app.css -> public/assets/app.css
GET /uploads/a.png -> public/uploads/a.png
GET /docs/index.html -> public/docs/index.htmlStatic files with middleware
Middleware can be used with mounted static paths.
app.use("/assets", [](vix::Request &req, vix::Response &res, vix::App::Next next)
{
(void)req;
res.header("X-Static", "true");
next();
});
app.static_dir("public/assets", "/assets");This can add headers before the static fallback writes the file response.
Static files with cache headers
app.static_dir(
"public/assets",
"/assets",
"index.html",
true,
"public, max-age=86400",
true);This is useful for versioned assets.
/assets/app.3f2a9.js
/assets/style.a91c2.cssStatic files without cache
app.static_dir(
"public",
"/",
"index.html",
false,
"",
true);This is useful during development.
Static files and templates
Static files and templates solve different problems.
Use static files for files that already exist on disk:
app.static_dir("public", "/assets");Use templates when a response should be rendered dynamically:
app.templates("views");
app.get("/", [](vix::Request &req, vix::Response &res)
{
(void)req;
vix::tmpl::Context ctx;
ctx.set("title", "Home");
res.render("index.html", ctx);
});Static files and API routes
You can mix API routes and static files.
#include <vix.hpp>
int main()
{
vix::App app;
app.get("/api/status", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.json({
{"status", "ok"}
});
});
app.static_dir("public", "/");
app.run(8080);
return 0;
}Example:
GET /api/statusruns the route.
GET /style.csscan serve a file from public/style.css.
Complete example
#include <vix.hpp>
int main()
{
vix::App app;
app.use([](vix::Request &req, vix::Response &res, vix::App::Next next)
{
(void)req;
res.header("X-Powered-By", "Vix.cpp");
next();
});
app.get("/api/status", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.json({
{"status", "ok"}
});
});
app.get("/download", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.file("public/files/readme.txt");
});
app.static_dir(
"public/assets",
"/assets",
"index.html",
true,
"public, max-age=3600",
true);
app.static_dir(
"public",
"/",
"index.html",
false,
"",
true);
app.run(8080);
return 0;
}Example requests:
GET /api/status
GET /download
GET /assets/style.css
GET /API summary
| API | Purpose |
|---|---|
app.static_dir(root) | Mount a static directory at /. |
app.static_dir(root, mount) | Mount a static directory at a URL prefix. |
app.static_dir(root, mount, index_file, add_cache_control, cache_control, fallthrough) | Mount static files with explicit options. |
res.file(path) | Send one file from a route handler. |
res.header(name, value) | Set response headers before sending a file. |
res.type(mime) | Set a custom content type when sending manually. |
Best practices
Use /assets for frontend assets.
app.static_dir("public/assets", "/assets");Use / for a simple public directory.
app.static_dir("public", "/");Use cache headers for versioned assets.
app.static_dir("dist/assets", "/assets", "index.html", true, "public, max-age=31536000", true);Disable cache headers during development.
app.static_dir("public", "/", "index.html", false, "", true);Use explicit routes for APIs.
app.get("/api/status", handler);Use res.file(...) for one-off downloads.
res.file("public/files/report.pdf");Avoid exposing sensitive directories.
app.static_dir("public", "/");Do not mount project roots, home directories, secrets, or build folders that contain private files.
Next steps
Read the next pages: