Chapter 10 / 51
Volume 2 · Configuration & Assembly

The four static-layer knobs and traversal defense

What takes over after XS_FALLBACK is the static layer. It is safe by default and fast by default — four host-level knobs cover most static-site needs, and its traversal defense is guarded by eight test vectors.

Overview

Positioning: the static layer is the back half of the "script first, static fallback" combination. This chapter covers its behavioral boundaries and four knobs; security-minded readers should focus on 10.4's eight vectors.

10.1 Intro

The bulk of a web app's assets is usually static: frontend bundles, images, fonts. Writing code for static assets is waste; an external static server breaks "single-file deployment". XServer's answer is a built-in static layer: GET/HEAD the script didn't handle lands here and resolves files under the host's path — without writing a line of code you already own a secure static server.

10.2 Static-layer behavior

  • Methods: GET returns content, HEAD headers only; other methods are outside static-layer semantics
  • Paths: percent-decoded then normalized; directory requests fall through the index array (default index.html)
  • Transfer: SendFile in 512KB chunks; under TLS a 64KB plaintext pump (with AGAIN/reentry coalescing) — large-file backpressure has a 4MiB multi-round real case in func_test
  • MIME: 18 built-in type mappings
  • Error pages: the built-in minimal page when no custom page is configured

10.3 The four knobs (host-level Custom)

Static-layer knob example
"host_default": {
    "name": "app",
    "path": "wwwroot",
    "static": {
        "headers": {                       /* ≤8 fields; 8 reserved headers forbidden */
            "X-Frame-Options": "DENY",
            "Cache-Control": "public, max-age=3600"
        },
        "index": ["index.html", "home.html"],  /* fallback in order */
        "error_pages": { "404": "404.html", "500": "5xx.html" },
        "deny_dotfiles": true              /* default true: dot-files always denied */
    }
}
KnobConstraintEffect
static.headers≤8 fields; Transfer-Encoding / Connection and 6 other reserved headers rejected (fail-fast)Attached to every static response. Pre-rendered at assembly into a field array — the request path is a pure memcpy with zero string processing
static.indexstring arrayDirectory index candidates, falling through in order
static.error_pageskeys 400–599; values relative to pathUses your custom error page for that status (effective only if the file exists)
static.deny_dotfilesbool · trueDot-files always denied — even on an otherwise legitimate path

10.4 The eight traversal vectors

The static layer's red line: a URL never escapes path. func_test guards the line with eight vectors, all of which must yield 403/404 rather than file content:

GET /../secret → denied GET /..%2Fsecret → denied GET /%2e%2e/secret → denied GET /%2E%2E/secret → denied /* percent-case */ GET //../secret → denied GET /..%5Csecret → denied /* backslash */ GET /%00secret → denied symlink out of root → denied

Implementation notes: percent-decoding runs before path normalization, so encoding bypasses don't exist; deny_dotfiles is an additional layer (traversal attempts with dot-files hit twice); symlinks out of the root are rejected by root anchoring.

10.5 Contract

  • Boundary: the static layer serves only GET/HEAD, only inside the host path, and denies dot-files by default.
  • Performance: headers are pre-rendered at assembly — zero string processing on the request path, the key design that lets it survive high concurrency.
  • Fallback: a custom error page missing its file falls back to the built-in page, never a 500.

Common mistakes

Putting Content-Length into headers
Symptom
Startup fails: static headers contains a reserved header.
Cause
Content-Length / Transfer-Encoding / Connection and 5 others belong to the protocol layer; overriding them from config would corrupt messages — hence fail-fast, not runtime override.
Writing "404.html" as an error_pages key
Symptom
Startup fails: error_pages key out of range.
Cause
The key is a status code (a numeric string 400–599); the value is the page path. Reversed or non-status keys fail fast.

Exercises

BasicA custom 404

Give the demo a 404.html, register it in error_pages and verify with a nonexistent path.

Acceptance: a 404 status with your page's content; removing the file reverts to the built-in page.
AdvancedRun all eight vectors

Place a secret.txt outside wwwroot and try to read it with each of 10.4's eight vectors.

Acceptance: no vector returns the file's content; record each status code.
ChallengeA security-header baseline

Configure a set of security response headers for a static site (CSP, X-Content-Type-Options, Referrer-Policy…) and verify the zero-overhead pre-rendered path (load-test deltas against a headers-free config should be negligible).

Acceptance: every static response carries the headers; the RPS difference between the two configs is < 2%.

Summary

PointConclusion
TriggerXS_FALLBACK GET/HEAD lands in the static layer
Four knobsheaders (pre-rendered) / index / error_pages / deny_dotfiles
DefenseDecode before normalization; eight guard vectors; symlink anchoring
TransferSendFile in 512KB chunks; 64KB TLS plaintext pump; 18 MIME types
API references in this chapter
xrt further reading