Give the web server its own .env, where PORT means its port

The previous commit made `pnpm --filter web start` load the repository-root
.env, which was the wrong file: Strapi reads a bare PORT as well
(apps/cms/config/server.ts uses env.int('PORT', 1337)), so one value there
would have moved the CMS onto the website's port the moment anyone exported
it - and the root .env is docker compose's file, whose WEB_PORT is a
published host port, not a listen port.

The web app now loads apps/web/.env, the way Strapi already loads
apps/cms/.env. PORT and HOST are per-application because they mean something
different to each server. apps/web/.env.example documents them together with
SITE_URL, STRAPI_URL and STRAPI_TOKEN, and warns against the VITE_ prefix,
which would inline a value into the client bundle - STRAPI_TOKEN lives in
that file.

Still no default port: with PORT unset, react-router-serve keeps its own
behaviour of taking the first free port from 3000 upwards. A real
environment variable wins over the file, so a systemd unit or a container
that injects PORT is unaffected.

.dockerignore now excludes .env at every depth, not only at the root.
`COPY apps/web ./apps/web` would otherwise have baked a developer's
apps/web/.env - STRAPI_TOKEN included - into the image.

Verified against the existing build: PORT=8140 HOST=127.0.0.1 in
apps/web/.env listens on 127.0.0.1:8140, and PORT=8141 in the environment
overrides the file while HOST still comes from it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-20 11:45:29 +02:00
co-authored by Claude Opus 5
parent e27e31bfa5
commit 2ed3a17914
5 changed files with 110 additions and 70 deletions
+47
View File
@@ -0,0 +1,47 @@
# ---------------------------------------------------------------------------
# SHARP-ER web (React Router 8) - environment template
#
# Copy to .env and fill in real values: cp .env.example .env
# Never commit .env.
#
# `pnpm --filter web start` loads THIS file (apps/web/.env), the same way the
# CMS loads apps/cms/.env. It is per-application on purpose: PORT and HOST mean
# something different to each server, and a single shared value would move
# Strapi onto the website's port. The repository-root .env is docker compose's
# file and is not read here.
#
# A real environment variable always wins over this file, so a systemd unit,
# a container or `PORT=9090 pnpm --filter web start` overrides any line below.
#
# `pnpm dev:web` does NOT read this file - the dev server is Vite on 5173, and
# it only exposes variables prefixed VITE_, by inlining them into the CLIENT
# bundle. Never use that prefix here: STRAPI_TOKEN below is a server secret.
# ---------------------------------------------------------------------------
# --- Server ----------------------------------------------------------------
# Port react-router-serve binds. Leave it unset and the server takes the first
# FREE port from 3000 upwards - 3001, 3002, ... once 3000 is busy - which a
# reverse proxy cannot be pointed at. `pnpm dev:web` ignores this and uses 5173.
PORT=3000
# Interface to bind. Unset means every interface. Set it to 127.0.0.1 when a
# reverse proxy on the same host should be the only way in.
# HOST=127.0.0.1
# --- Content ---------------------------------------------------------------
# Public origin of the site. Canonical URLs, hreflang alternates and JSON-LD
# are built from it, so it must match how visitors actually reach the site -
# https://sharper.ly in production, never the internal http://127.0.0.1:3000.
# Leave it unset in local development and the request's own host is used.
SITE_URL=http://localhost:3000
# Where the server-side loaders fetch content from. This origin is also
# prefixed onto uploaded media URLs, which the browser has to resolve too -
# see "Images from the CMS" in README.md.
STRAPI_URL=http://localhost:1337
# Optional Strapi API token. Only needed to read draft or otherwise protected
# content; published content is public. Settings -> API Tokens in the admin.
STRAPI_TOKEN=