Security
H3 security utilities.
#Authentication
#basicAuth(opts)
Create a basic authentication middleware.
Example:
import { H3, serve, basicAuth } from "h3";
const auth = basicAuth({ password: "test" });
app.get("/", (event) => `Hello ${event.context.basicAuth?.username}!`, [auth]);
serve(app, { port: 3000 });#requireBasicAuth(event, opts)
Apply basic authentication for current request.
Example:
import { defineHandler, requireBasicAuth } from "h3";
export default defineHandler(async (event) => {
await requireBasicAuth(event, { password: "test" });
return `Hello, ${event.context.basicAuth.username}!`;
});#Session
#clearSession(event, config)
Clear the session data for the current request.
#getSession(event, config)
Get the session for the current request.
#sealSession(event, config)
Encrypt and sign the session data for the current request.
#unsealSession(_event, config, sealed)
Decrypt and verify the session data for the current request.
#updateSession(event, config, update?)
Update the session data for the current request.
#useSession(event, config)
Create a session manager for the current request.
#Fingerprint
#getRequestFingerprint(event, opts)
Get a unique fingerprint for the incoming request.
#CORS
#appendCorsHeaders(event, options)
Append CORS headers to the response.
#appendCorsPreflightHeaders(event, options)
Append CORS preflight headers to the response.
#handleCors(event, options)
Handle CORS for the incoming request.
If the incoming request is a CORS preflight request, it will append the CORS preflight headers and send a 204 response.
If return value is not false, the request is handled and no further action is needed.
Example:
const app = new H3();
app.all("/", async (event) => {
const corsRes = handleCors(event, {
origin: "*",
preflight: {
statusCode: 204,
},
methods: "*",
});
if (corsRes !== false) {
return corsRes;
}
// Your code here
});#isCorsOriginAllowed(origin, options)
Check if the origin is allowed.
#isPreflightRequest(event)
Check if the incoming request is a CORS preflight request.
#Path
#isCanonicalPath(path, opts?)
Whether path is already canonical under opts — i.e. {@link resolveDotSegments} would return it unchanged. Exact in both directions: true if and only if resolveDotSegments(path, opts) === path.
This is the resolver's own fast-path guard, exported so a caller that canonicalizes on a hot path (per-request scope or rule matching) can skip the call — and any work derived from it — without keeping its own copy of what the resolver decodes. Such a copy goes stale silently, and a missed canonicalization in a scope check is a bypass, not a perf bug.
Pass the same options as the later {@link resolveDotSegments} call, or stricter ones: decodeSlashes/mergeSlashes only add triggers, so true with both enabled implies true in every mode. Checking one mode and resolving in another voids the guarantee.
Takes a bare pathname. Like the resolver, it has no notion of a query or hash and scans one as if it were path, so /a?next=/../b is reported non-canonical (and would resolve to /b).
#resolveDotSegments(path, opts?)
Resolve . and .. segments in a path, without ever escaping above the root /. The result is always an absolute path with a single leading /, so it can never be protocol-relative (//host).
Also decodes percent-encoded dot segments at any %25-nesting depth (%2e, %252e, ...) and normalizes \ to /, so encoded or backslash-based traversal (e.g. %2e%2e/, ..\..\) is caught the same way as a literal ../.
%2f/%5c (encoded path separators) are left untouched by default — see {@link ResolveDotSegmentsOptions.decodeSlashes}.
Only ./.. resolution and the decodes above alter the string; every other percent-encoding (%20, non-ASCII, %3A, and any %2e not forming a whole segment) is left intact, so the result stays in the same representation as an un-decoded event.url.pathname and matches routes/rules consistently. A trailing ./.. resolves to a directory and keeps its trailing slash (/a/b/.. -> /a/, /a/. -> /a/), per RFC 3986 §5.2.4 and matching what a WHATWG/nginx downstream resolves — so a scope check sees the directory form, not its file-form sibling. Interior empty segments are preserved (/a//b stays /a//b) — like WHATWG, this never merges slashes, so empty segments survive rather than collapsing. The one exception is a leading run: it is always clamped to a single / (WHATWG would keep //host), so only the leading slash is guaranteed single and a consumer doing exact prefix matching should normalize its allowlist the same way. To collapse interior runs too (the reading a slash-merging downstream resolves), see {@link ResolveDotSegmentsOptions.mergeSlashes}.