[object Object]

← back to Blockprintedwallpaper

add: clean-URL routes for /history /vocabulary /sourcing /care /trade + 404 guard

da1fc9a603896a581df86471ddb55bfdfffdbaaa · 2026-05-25 20:51:14 -0700 · SteveStudio2

Every page template's local nav links to extension-less URLs (/history,
/vocabulary, etc.) but Express's static middleware does not auto-strip
.html, so cross-page nav was 404-ing. Mount one explicit route per page.

Also add a terminal 404 middleware — JSON for /api/* probes, friendly
HTML "return home" for everything else. Stops drive-by URL probes from
showing a blank Cannot GET line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit da1fc9a603896a581df86471ddb55bfdfffdbaaa
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date:   Mon May 25 20:51:14 2026 -0700

    add: clean-URL routes for /history /vocabulary /sourcing /care /trade + 404 guard
    
    Every page template's local nav links to extension-less URLs (/history,
    /vocabulary, etc.) but Express's static middleware does not auto-strip
    .html, so cross-page nav was 404-ing. Mount one explicit route per page.
    
    Also add a terminal 404 middleware — JSON for /api/* probes, friendly
    HTML "return home" for everything else. Stops drive-by URL probes from
    showing a blank Cannot GET line.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
 server.js | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/server.js b/server.js
index f46bb51..4acf837 100644
--- a/server.js
+++ b/server.js
@@ -153,6 +153,14 @@ app.get('/api/health', (req, res) => res.json({ status: 'ok', count: PRODUCTS_NI
 // /about — the brand history page (history.html). Nav links to /about; serve it here.
 app.get('/about', (req, res) => res.sendFile(path.join(__dirname, 'public', 'history.html')));
 
+// Clean-URL routes for extension-less nav links present across all page
+// templates (history/vocabulary/sourcing/care/trade). Without these,
+// express.static would not auto-strip .html and every cross-page nav 404'd.
+const CLEAN_PAGES = ['history', 'vocabulary', 'sourcing', 'care', 'trade'];
+for (const slug of CLEAN_PAGES) {
+  app.get('/' + slug, (req, res) => res.sendFile(path.join(__dirname, 'public', slug + '.html')));
+}
+
 app.get('/sample/:handle', (req, res) => {
   const p = PRODUCTS_NICHE.find(x => x.handle === req.params.handle || x.sku === req.params.handle);
   if (!p) return res.status(404).send('Not found');
@@ -178,6 +186,22 @@ ${urls.map(u => `  <url><loc>https://blockprintedwallpaper.com${u}</loc><changef
 // Admin catalog CRUD — /admin/catalog (basic-auth) + /api/admin/* REST.
 if (catalog) catalog.mount(app, { siteSlug: SITE_SLUG, rails: SITE_RAILS });
 
+// 404 guard — JSON for /api/*, friendly HTML redirect to home for everything
+// else. Keeps drive-by URL probes from getting a stack trace or blank page.
+app.use((req, res) => {
+  if (req.path.startsWith('/api/')) {
+    return res.status(404).json({ error: 'not_found', path: req.path });
+  }
+  res.status(404).type('html').send(
+    '<!doctype html><meta charset=utf-8><title>Not found · blockprintedwallpaper</title>' +
+    '<meta http-equiv="refresh" content="0; url=/">' +
+    '<style>body{font-family:Inter,system-ui,sans-serif;background:#FAFAFA;color:#000;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;text-align:center}a{color:#b85c40;text-decoration:none;border-bottom:1px solid #b85c40;padding-bottom:2px}</style>' +
+    '<main><p style="font-size:11px;letter-spacing:0.32em;text-transform:uppercase;font-weight:700;color:#8B7866;margin-bottom:16px">404</p>' +
+    '<p style="font-size:18px;margin-bottom:24px">That page isn’t here.</p>' +
+    '<a href="/">Return home</a></main>'
+  );
+});
+
 // Load the catalog. When admin is enabled, prefer dw_unified; else static JSON.
 (async () => {
   let loaded = false;

← 9ef46b6 fix: add noreferrer to footer Facebook target=_blank  ·  back to Blockprintedwallpaper  ·  redact vendor names from customer-facing /api (close fleet l 36416e5 →