[object Object]

← back to Wallco Ai

wallco.ai · gzip compression middleware — 86% bandwidth reduction on /designs

486986eb859a72591c14c406580afb345436f1d1 · 2026-05-12 06:41:34 -0700 · SteveStudio2

Was uncompressed. Adds the standard `compression` npm middleware first in the
chain. /designs HTML drops from 164,851 → 23,637 bytes (86%); JSON-LD-heavy
/design/:id pages see similar. PNG room mockups already deflated so the
middleware's content-type filter skips them — net win is on HTML/JS/CSS/JSON.

Threshold 1024B avoids overhead for tiny responses (robots.txt stays plain).
Bypass via X-No-Compress: 1 header for debug. Vary: Accept-Encoding emitted
so CDN caches don't poison clients that can't decode.

Reversible: 1 npm dep, 9-line middleware block, no env/config changes.

Files touched

Diff

commit 486986eb859a72591c14c406580afb345436f1d1
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Tue May 12 06:41:34 2026 -0700

    wallco.ai · gzip compression middleware — 86% bandwidth reduction on /designs
    
    Was uncompressed. Adds the standard `compression` npm middleware first in the
    chain. /designs HTML drops from 164,851 → 23,637 bytes (86%); JSON-LD-heavy
    /design/:id pages see similar. PNG room mockups already deflated so the
    middleware's content-type filter skips them — net win is on HTML/JS/CSS/JSON.
    
    Threshold 1024B avoids overhead for tiny responses (robots.txt stays plain).
    Bypass via X-No-Compress: 1 header for debug. Vary: Accept-Encoding emitted
    so CDN caches don't poison clients that can't decode.
    
    Reversible: 1 npm dep, 9-line middleware block, no env/config changes.
---
 package-lock.json | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 package.json      |  7 +++---
 server.js         | 12 +++++++++++
 3 files changed, 80 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 09f946e..09baf00 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
       "license": "ISC",
       "dependencies": {
         "@dw/inline-editor": "file:../inline-editor",
+        "compression": "^1.8.1",
         "dotenv": "^17.4.2",
         "express": "^5.2.1",
         "jsonwebtoken": "^9.0.3"
@@ -112,6 +113,60 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/compressible": {
+      "version": "2.0.18",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": ">= 1.43.0 < 2"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/compression": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
+      "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "compressible": "~2.0.18",
+        "debug": "2.6.9",
+        "negotiator": "~0.6.4",
+        "on-headers": "~1.1.0",
+        "safe-buffer": "5.2.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/compression/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/compression/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "license": "MIT"
+    },
+    "node_modules/compression/node_modules/negotiator": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
+      "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/content-disposition": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
@@ -673,6 +728,15 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/on-headers": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
+      "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
diff --git a/package.json b/package.json
index 6d2e21b..8a60518 100644
--- a/package.json
+++ b/package.json
@@ -11,9 +11,10 @@
   "license": "ISC",
   "description": "",
   "dependencies": {
+    "@dw/inline-editor": "file:../inline-editor",
+    "compression": "^1.8.1",
     "dotenv": "^17.4.2",
     "express": "^5.2.1",
-    "jsonwebtoken": "^9.0.3",
-    "@dw/inline-editor": "file:../inline-editor"
+    "jsonwebtoken": "^9.0.3"
   }
-}
\ No newline at end of file
+}
diff --git a/server.js b/server.js
index 8c58703..e6d9211 100644
--- a/server.js
+++ b/server.js
@@ -72,6 +72,18 @@ function isRoomsAdmin(req) {
       || (req.query && req.query.admin === ADMIN_TOKEN);
 }
 
+// gzip + brotli on everything compressible. PNGs are already deflated so the
+// middleware filter skips them; HTML/JS/CSS/JSON drops ~70%. Threshold 1KB
+// avoids overhead for tiny responses. Bypass-able via X-No-Compress header.
+const compression = require('compression');
+app.use(compression({
+  threshold: 1024,
+  filter: (req, res) => {
+    if (req.headers['x-no-compress']) return false;
+    return compression.filter(req, res);
+  }
+}));
+
 app.use(express.json({ limit: '25mb' }));  // 25mb to accept full-res baked PNGs from /api/design/:id/bake
 app.use(express.urlencoded({ extended: false }));
 

← 70b4570 security — close path traversal + shell injection in /api/st  ·  back to Wallco Ai  ·  security — prototype-pollution + pin-spam DoS guards in src/ 0772bf0 →