I'm experimenting with Nuxt 3, and was wondering if it was possible to override a particular part of Nitro's runtime behaviour. The Nuxt docs suggest extending Nitro's runtime behaviour is possible through server plugins, it's just a little thin on the details at the moment.
Nuxt will automatically read any files in the ~/server/plugins directory and register them as Nitro plugins. This allows extending Nitro's runtime behavior and hooking into lifecycle events.
What I'm trying to achieve:
When a request is made for a static asset, Nitro attempts to serve it from <app root>/public/
. If the asset isn't found, Nitro returns a 404. I'd like to have Nitro attempt to serve the same static asset from a different sub directory of <app root>/public/
, return it if found there, throw the 404 otherwise.
How this could work:
After running a build doing some digging, I found the code block responsible for serving assets seems to live in .output/server/chunks/node-server.mjs
, as follows:
const _f4b49z = eventHandler((event) => {
if (event.req.method && !METHODS.includes(event.req.method)) {
return;
}
let id = decodeURIComponent(withLeadingSlash(withoutTrailingSlash(parseURL(event.req.url).pathname)));
let asset;
const encodingHeader = String(event.req.headers["accept-encoding"] || "");
const encodings = encodingHeader.split(",").map((e) => EncodingMap[e.trim()]).filter(Boolean).sort().concat([""]);
if (encodings.length > 1) {
event.res.setHeader("Vary", "Accept-Encoding");
}
for (const encoding of encodings) {
for (const _id of [id + encoding, joinURL(id, "index.html" + encoding)]) {
const _asset = getAsset(_id);
if (_asset) {
asset = _asset;
id = _id;
break;
}
}
}
if (!asset) {
if (isPublicAssetURL(id)) {
throw createError({
statusMessage: "Cannot find static asset " + id,
statusCode: 404
});
}
return;
}
const ifNotMatch = event.req.headers["if-none-match"] === asset.etag;
if (ifNotMatch) {
event.res.statusCode = 304;
event.res.end();
return;
}
const ifModifiedSinceH = event.req.headers["if-modified-since"];
if (ifModifiedSinceH && asset.mtime) {
if (new Date(ifModifiedSinceH) >= new Date(asset.mtime)) {
event.res.statusCode = 304;
event.res.end();
return;
}
}
if (asset.type && !event.res.getHeader("Content-Type")) {
event.res.setHeader("Content-Type", asset.type);
}
if (asset.etag && !event.res.getHeader("ETag")) {
event.res.setHeader("ETag", asset.etag);
}
if (asset.mtime && !event.res.getHeader("Last-Modified")) {
event.res.setHeader("Last-Modified", asset.mtime);
}
if (asset.encoding && !event.res.getHeader("Content-Encoding")) {
event.res.setHeader("Content-Encoding", asset.encoding);
}
if (asset.size && !event.res.getHeader("Content-Length")) {
event.res.setHeader("Content-Length", asset.size);
}
return readAsset(id);
});
Is it possible to override that specific method? I'd basically like to change:
for (const encoding of encodings) {
for (const _id of [id + encoding, joinURL(id, "index.html" + encoding)]) {
const _asset = getAsset(_id);
if (_asset) {
asset = _asset;
id = _id;
break;
}
}
}
In to:
for (const encoding of encodings) {
for (let _id of [id + encoding, joinURL(id, "index.html" + encoding)]) {
let _asset = getAsset(_id);
// if the asset wasn't found at 'public/', try 'public/subDir'
if (!_asset) {
_asset = assets['/subDir' + _id]
_id = '/subDir' + _id
}
if (_asset) {
asset = _asset;
id = _id;
break;
}
}
}
I'm exploring how serving multiple sites from a single Nuxt app might work, without having to change anything outside of the code base. I've managed to get along so far, but I need a solution for static assets (robots.txt, well-known URI, etc).
After investigating, it looks like this isn't possible from within the Nuxt app. While I wanted to achieve this within the app itself, I was able to get this to work using nginx. I'll post my solution here in case it's helpful to anyone else in future.
Within my nginx config, I mapped the
$host
variable to a new$static_asset_directory
variable. The value of$static_asset_directory
then depends on which site the request comes in from, example-1.com or example-2.comI can then use the value of
$static_asset_directory
to define a location block for the static asset I want the Nuxt app to serve.This allows me to structure my Nuxt app as follows, to serve different static assets (with the same names) from the root of the domain. "example-1.com/robots.txt" will serve the file in /public/example-1/robots.txt, while "example-2.com/robots.txt" will serve /public/example-2/robots.txt.