---
title: "Locking the Front Door on Modern Web Apps: Why Your Production Container Should Never Serve From the Project Root (Plus Our 8-Point Pre-Launch Gate) | GreenTree Field Notes"
url: https://www.greentreehosting.com/field-notes/docker-static-asset-isolation-and-8-point-pre-launch-security-gate/
description: "By Gene Salvatore: Why production Node/Express and Docker containers must serve exclusively from path.resolve(dist), return HTTP 404 on dotfiles/scanner probes, strip .map files, and pass our 8-point negative-testing security gate."
format: text/markdown
generated_from: prerendered HTML (build time)
---

[ALL SYSADMIN FIELD NOTES & RUNBOOKS](https://www.greentreehosting.com/field-notes)

FN-2026-06 // WEB APP SECURITYWeb App Security & Linux Ops · 7 min read

# Locking the Front Door on Modern Web Apps: Why Your Production Container Should Never Serve From the Project Root (Plus Our 8-Point Pre-Launch Gate)

Within minutes of pointing a domain at a new server, automated scanners probe /.env, /.git/config, /backup.sql, and /server.mjs. Here is how we isolate static asset serving inside every production container and verify 8 mandatory security checks before launch.

Published: 2026-10-25•Last tested: 2026-10-25•Tested on Node.js 22 LTS, Express 4.x/5.x, Vite 6/8 & Docker Engine 27.x on Ubuntu 24.04 LTS

01 // THE SYMPTOM

Automated vulnerability scanners or search crawlers access sensitive configuration files (/.env, /package.json, /.git/HEAD, or .map source maps), or spam bots flood contact forms.

02 // THE ROOT CAUSE

Serving static files relative to the application root directory instead of a strictly confined dist/ build folder, leaving production source maps enabled, or shipping forms without server-side rate limiting and honeypots.

03 // THE PERMANENT FIX

Enforce strict path.resolve(DIST_DIR) confinement with explicit 403/404 dotfile blocking, disable production .map files, keep secrets strictly server-side, and run an 8-point negative-testing gate before launch.

STATIC ROOT CONFINEMENT

DIST_DIR Only

Static file handler resolves strictly inside /app/dist — never the project root directory

CLIENT BUNDLE AUDIT

Server-Only Keys

Pre-launch grep checks compiled JS bundles for accidentally embedded VITE_ or private API keys

FORM & WEBHOOK SHIELD

7 Defense Layers

Honeypot traps, interaction timer, IP rate limiting, origin check & database-first persistence

PRE-LAUNCH VERIFICATION

8-Point Gate

Every new application on our platform passes all 8 negative-security checks before DNS cutover

**Sysadmin Safety Notice:**Test path confinement and negative-security probes against a staging container or local build before exposing a new application container to public traffic.

INTERACTIVE SYSADMIN RUNBOOK · SAVED IN YOUR BROWSER

## Interactive 8-Point Pre-Launch Web Application Security Checklist

Run through all 8 checks before putting any Node.js, React, or PHP web application into production:

PROGRESS: 0 OF 8 STEPS VERIFIED (0%)CLICK ANY STEP TO CHECK OFF

1. Confine static file serving strictly to the compiled dist/ directory

Use path.resolve(DIST_DIR, ...) and verify that the resolved path starts with DIST_DIR + path.sep so ../ path traversal or root-directory requests (/server.mjs, /package.json) can never be read.

2. Block dotfiles and common scanner probe paths at the edge (403 / 404)

Immediately reject requests matching /.env, /.git, /.svn, /wp-config.php, /phpinfo.php, or *.sql /*.bak / *.log before touching the filesystem.

3. Audit compiled client JavaScript bundles for leaked VITE_ or NEXT_PUBLIC_ secrets

Any variable prefixed with VITE_ or NEXT_PUBLIC_ is compiled directly into public browser JavaScript. Keep all SMTP, Stripe secret, and database credentials strictly on the server.

4. Disable production source maps (.map files) in vite.config.ts / next.config.js

Leaving .js.map files in dist/assets/ allows anyone to reconstruct your unminified TypeScript source code and internal comments.

5. Protect every form and API endpoint with honeypot fields, timing checks, and IP rate limits

Combine hidden honeypot inputs, a minimum human page-interaction timer, and a sliding-window per-IP rate limiter.

6. Use timing-safe comparison (crypto.timingSafeEqual) on admin/portal authentication

Never compare admin tokens or passwords with standard === string equality where response timing can leak character matches.

7. Enforce TLS 1.3, HSTS, X-Content-Type-Options: nosniff, and X-Frame-Options

Ensure every HTTP response carries strict transport and framing headers.

8. Enroll the live URL and form endpoint in 60-second outside monitoring

Verify from outside the server via LogBot.app and DNSBot.app that the site answers HTTP 200 and SSL renewal is tracked.

FIELD NOTE 01 // THE PROJECT ROOT STATIC FILE MISTAKE

## 1. Why Static File Servers Must Be Locked Inside dist/—Never the Application Root

When developers move a custom Node.js, Vite, or Python web application from local development to a production Linux server, one of the most dangerous mistakes is configuring the static file middleware to serve files relative to the project root directory (`.`) instead of the compiled `/dist` subdirectory.

If your static file handler resolves paths against the project root without strict directory confinement, an attacker who requests `https://yourdomain.com/server.mjs`, `https://yourdomain.com/package.json`, or `https://yourdomain.com/.env` may receive your raw backend source code or environment secrets!

In our production container standard, the static file handler resolves every incoming request strictly inside `DIST_DIR = path.resolve(__dirname, "dist")`, verifies that the normalized path begins with `DIST_DIR`, and explicitly rejects any URL containing hidden dot-segments (`/.`) or sensitive extensions (`.sql`, `.bak`, `.env`, `.map`).

| Probe Vector | Unconfined Root Static Handler | GreenTree Confined DIST_DIR + 8-Point Gate |
| --- | --- | --- |
| GET /.env or /.git/config | Risk of leaking environment variables or Git metadata | Blocked immediately at edge & request router (HTTP 403/404) |
| GET /server.mjs or /package.json | Exposed if static middleware points at project root | Physically outside DIST_DIR; impossible to serve as static asset |
| GET /assets/index-*.js.map | Exposes full original TypeScript source tree to scrapers | build.sourcemap = false enforced in production builds |
| POST /api/forms/submit (Bot Flood) | Floods inbox with thousands of automated spam submissions | 7-layer shield: honeypot + interaction timer + IP rate limit |

FIELD NOTE 02 // AUTOMATED PRE-LAUNCH NEGATIVE TESTING

## 2. Testing What Should Be Blocked (Not Just What Should Load)

Most people test a new website by opening the homepage in a browser and making sure it looks right. A systems engineer tests a new website by requesting the things that **must fail**.

Before any site on our high-availability platform goes live, we run an automated negative-testing check against the production container to prove that dotfile probes, backend script filenames, path-traversal attempts, and unauthenticated admin requests are all rejected cleanly while legitimate prerendered pages return `HTTP 200` with strict security headers.

Quick Pre-Launch Security & Negative-Probe Verification Script

```
# Verify that sensitive paths return 403/404 (and NEVER 200 OK with file contents)
for probe in "/.env" "/.git/config" "/server.mjs" "/package.json" "/wp-config.php" "/backup.sql"; do
  status=$(curl -s -o /dev/null -w "%{http_code}" "https://www.yourdomain.com${probe}")
  echo "Probe ${probe} -> HTTP ${status}"
done

# Verify that compiled client JS bundles in dist/assets/ contain zero private keys or .map files
ls -la dist/assets/*.map 2>/dev/null || echo "PASS: Zero production .map files present"
grep -rnE "sk_live_|PRIVATE_KEY|POSTGRES_PASSWORD|SMTP_PASS" dist/assets/ || echo "PASS: Zero server secrets in client bundle"
```

⚠ Test on a non-production system first and verify backups before running administrative commands.

SYSADMIN Q&A

## Common Questions on Container Static Isolation & 8-Point Security Gate

### Why do I see thousands of requests for /wp-login.php and /.env in my logs even on a React/Node site?

Automated botnets continuously scan every IPv4 address and newly issued SSL certificate on the internet, firing a standard list of 500+ URL probes (/.env, /.git/config, /wp-admin, /phpmyadmin) regardless of what software you actually run. Blocking those paths cleanly at the edge and confining static files to dist/ ensures they never touch your application logic.

### Is hiding a secret key in a VITE_ environment variable safe if my Git repo is private?

No. Even if your GitHub repository is private, Vite embeds every VITE_* variable directly into the compiled JavaScript bundle served to every visitor’s browser. Any secret API key, database service-role key, or SMTP password must live only in server-side environment variables.

### How does your 7-layer form shield stop spam without annoying customers with visual puzzles?

Instead of forcing prospective customers to click traffic lights or fire hydrants, our form shield combines invisible honeypot fields, page-load interaction timing (rejecting instant sub-second bot POSTs), cryptographic payload checks, and per-IP sliding-window rate limiting.

COMPANION CORNERSTONE REFERENCE GUIDE (/LIBRARY)

### You Built the Prototype. Here’s How We Engineer the Rest: Turning a Lovable, Famous.ai, Bolt, or Replit App Into a Production Web Application

Read the full 4,200+ word guide on closing the 4 security doors (VITE_ secret leaks, CVE-2025-48757 RLS exposure, unthrottled webhooks, and .map source maps).

[Read Full Engineering Guide](https://www.greentreehosting.com/library/from-ai-prototype-to-production-web-application)

PRODUCTION HARDENING & GRADUATION

### Built an App in Lovable, Famous.ai, Bolt, Replit, or React That Needs Production Hardening?

We take prototypes and custom web applications, lock the 4 security doors, isolate the database and static build pipeline, prerender every route for Google and AI search, and host them on our monitored high-availability Linux cloud.

[Read Our AI Prototype Graduation Guide](https://www.greentreehosting.com/library/from-ai-prototype-to-production-web-application)[Explore Custom Web Application Engineering](https://www.greentreehosting.com/web-applications)

RELATED FIELD NOTES & RUNBOOKS

## Related Notes From the Sysadmin Desk

[View All 8 Field Notes](https://www.greentreehosting.com/field-notes)

[Server Transfers & Cutovers9 min readMoving Off Usage-Metered Cloud (Netlify Credits & Non-Pausing Supabase Compute): Our Production Cutover Runbook to Flat-Rate Docker & PostgreSQL 16Export the Supabase PostgreSQL schema and data via pg_dump, replace client-side VITE_SUPABASE_ANON_KEY calls with server-side Express + pg pool endpoints, translate netlify.toml redirects/headers into server.mjs, and deploy inside an isolated Docker container.Open Field Note & Checklist](https://www.greentreehosting.com/field-notes/migrating-off-metered-netlify-and-supabase-to-flat-rate-docker-and-postgresql)[DNS & Linux Infrastructure8 min readWritable Standby PostgreSQL Failback Merges: Why ON CONFLICT (id) DO NOTHING Silently Drops Rows on Sequential IDs (And How to Fix It With UUIDs or Split Sequence Ranges)Audit every table in the failback merge list: use UUID primary keys (DEFAULT gen_random_uuid()) on append-only lead/submission tables, or configure non-overlapping odd/even sequence steps (INCREMENT BY 2) dynamically above MAX(id)—and check secondary unique constraints and payload hashes before skipping.Open Field Note & Checklist](https://www.greentreehosting.com/field-notes/postgres-standby-failback-merge-id-collision-and-uuid-vs-sequence-ranges)[Server Transfers & Cutovers9 min readThe Zero-Downtime Server Transfer Runbook: How We Move Live Websites, Databases, and Mailboxes Without Losing a Single Order or EmailExecute a 5-stage cutover: lower DNS TTLs to 60 seconds 48 hours early, stage and test via local hosts-file override, pre-issue TLS certificates, run a final sub-second rsync + database delta sync, and enforce a 5-source 301 redirect map.Open Field Note & Checklist](https://www.greentreehosting.com/field-notes/zero-downtime-server-migration-and-cutover-runbook)
