Tour: Static Files
Any file in the routes/ directory that isn't a .rex file and doesn't
start with _ is served as a static asset with automatic content-type detection.
How It Works
This page's CSS is served from routes/style.css.
The server detected text/css from the file extension and set the content-type header.
No Rex code needed.
Verify: /style.css
Resolution Priority
When a request arrives, the server checks in this order:
.rexhandler — highest priority (dynamic)- Static file — exact path match
index.rex— if the path is a directory- 404
Middleware Still Runs
Even for static files, the middleware chain executes. That's why /style.css
gets the X-Request-Id and X-Content-Type-Options: nosniff headers
added by the global middleware. Try it:
Try it: inspect static file headers
curl -I http://localhost:4000/style.css
You'll see the security headers alongside the CSS content-type.
Tagged Template Literals
This page is written using Rex's tagged template literals.
The html tag auto-escapes all interpolated values, preventing XSS.
Static HTML passes through unchanged, and quotes don't need escaping.
Instead of string concatenation with escaped quotes:
body = body + "<a href=" + url + ">" + title + "</a>"
You write:
body = html`<a href="${url}">${title}</a>`
Private Directories
Directories and files starting with _ are private — never served to
browsers, but readable by Rex handlers via fs.read(). This app uses:
_layouts/— HTML templates_content/— markdown articles
Try requesting /_layouts/page.html directly — you'll get a 404.
This Page's Source
/* Tour Stop 1: Static Files This page demonstrates tagged template literals — the html tag auto-escapes interpolated values, preventing XSS. */ res.headers.content-type = "text/html; charset=utf-8" layout = fs.read("routes/_layouts/page.html") unless layout do status = 500 return "layout not found" end self-source = fs.read("routes/tour/static-files.rex") highlighted = when self-source do html.raw(html.highlight(self-source)) end body = html`<h1>Tour: Static Files</h1> <p class="source-link"><a href="/tour/routing">Next: Routing →</a></p> <p>Any file in the <code>routes/</code> directory that isn't a <code>.rex</code> file and doesn't start with <code>_</code> is served as a static asset with automatic content-type detection.</p> <h2>How It Works</h2> <div class="card"> <p><strong>This page's CSS</strong> is served from <code>routes/style.css</code>. The server detected <code>text/css</code> from the file extension and set the content-type header. No Rex code needed.</p> <p>Verify: <a href="/style.css">/style.css</a></p> </div> <h2>Resolution Priority</h2> <p>When a request arrives, the server checks in this order:</p> <ol> <li><strong><code>.rex</code> handler</strong> — highest priority (dynamic)</li> <li><strong>Static file</strong> — exact path match</li> <li><strong><code>index.rex</code></strong> — if the path is a directory</li> <li><strong>404</strong></li> </ol> <h2>Middleware Still Runs</h2> <p>Even for static files, the middleware chain executes. That's why <code>/style.css</code> gets the <code>X-Request-Id</code> and <code>X-Content-Type-Options: nosniff</code> headers added by the global middleware. Try it:</p> <details class="try-it"> <summary>Try it: inspect static file headers</summary> <pre>curl -I http://localhost:4000/style.css</pre> <p>You'll see the security headers alongside the CSS content-type.</p> </details> <h2>Tagged Template Literals</h2> <p>This page is written using Rex's <strong>tagged template literals</strong>. The <code>html</code> tag auto-escapes all interpolated values, preventing XSS. Static HTML passes through unchanged, and quotes don't need escaping.</p>` /* Show the before/after comparison using highlighted Rex snippets */ old-way = 'body = body + "<a href=" + url + ">" + title + "</a>"' new-way = 'body = html`<a href="${url}">${title}</a>`' body = body + "<p>Instead of string concatenation with escaped quotes:</p>" body = body + "<pre>" + html.highlight(old-way) + "</pre>" body = body + "<p>You write:</p>" body = body + "<pre>" + html.highlight(new-way) + "</pre>" body = body + html`<h2>Private Directories</h2> <p>Directories and files starting with <code>_</code> are private — never served to browsers, but readable by Rex handlers via <code>fs.read()</code>. This app uses:</p> <ul> <li><code>_layouts/</code> — HTML templates</li> <li><code>_content/</code> — markdown articles</li> </ul> <p>Try requesting <code>/_layouts/page.html</code> directly — you'll get a 404.</p> <h2>This Page's Source</h2> <pre>${highlighted}</pre>` template.render(layout, { title: "Static Files" body: body footer: "<a href='/'>← Home</a> · <a href='/tour/routing'>Routing →</a>" })