Compare commits
1
Commits
fe2deec5bb
...
9e1e0c26e1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e1e0c26e1 |
@@ -86,7 +86,9 @@ either one is broken. We provide:
|
||||
- `script/frontend-fmt-check` — check prettier formatting (read-only)
|
||||
- `script/frontend-viewport-test` — responsive-layout verification of the built
|
||||
frontend in a containerised headless Chrome (see
|
||||
[test/viewport/README.md](test/viewport/README.md)). Not part of
|
||||
[test/viewport/README.md](test/viewport/README.md)). Before the browser
|
||||
starts, it checks that the page, and a path that falls back to it, are served
|
||||
with `Cache-Control: no-cache` and `Content-Security-Policy`. Not part of
|
||||
`script/check`: it takes minutes.
|
||||
- `script/docker` — build the image from `Dockerfile` without the build cache,
|
||||
tagged `netwatch` via `script/projectname`
|
||||
@@ -435,7 +437,10 @@ address nginx passes on and no other. The image:
|
||||
each entry with `netwatch-server check-cidr`, which parses it as the backend
|
||||
parses its own `TRUSTED_PROXIES`
|
||||
- Sends access logs to stdout
|
||||
- Caches static assets with immutable headers
|
||||
- Sends the page with `Cache-Control: no-cache`, so a browser that keeps a copy
|
||||
checks with the server that it is current before using it again; only the
|
||||
files under `/assets/`, whose names change whenever their contents do, are
|
||||
cached as immutable
|
||||
- Sends the security headers `REPO_POLICIES.md` requires on every response, as
|
||||
`configs/security-headers.conf` sets them, in place of the backend's own
|
||||
- Stores reports in `DATA_DIR`, `/data/reports` by default, on the `/data`
|
||||
|
||||
@@ -21,6 +21,18 @@ Run `make frontend-viewport-test` in CI as its own step; it is not part of
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-10-18: the image's nginx sends the page with `Cache-Control: no-cache`
|
||||
([#141](https://git.eeqj.de/sneak/netwatch/issues/141)), so a browser that
|
||||
keeps a copy checks with the server that it is current before using it again.
|
||||
The page went out with no `Cache-Control`, so a browser could keep it after a
|
||||
deploy and ask for files under `/assets/` that the new build no longer has,
|
||||
and show an empty page. A path with no file of its own falls back to the page
|
||||
and gets the same header. `location /` now includes the security headers
|
||||
again, as `/assets/` does, since its own `add_header` drops the server's.
|
||||
`/assets/` keeps its immutable caching. `make frontend-viewport-test` fails
|
||||
unless `/` and a path that falls back to the page carry
|
||||
`Cache-Control: no-cache` and `Content-Security-Policy`, which comes from the
|
||||
same file as the other security headers
|
||||
- 2026-10-18: a browser that cannot format the header's clocks still probes
|
||||
([#149](https://git.eeqj.de/sneak/netwatch/issues/149)). The clocks name the
|
||||
local time zone through `Intl.DateTimeFormat`'s `shortGeneric` option, which
|
||||
|
||||
@@ -30,8 +30,13 @@ server {
|
||||
access_log /dev/stdout combined;
|
||||
error_log /dev/stderr warn;
|
||||
|
||||
# With no-cache, a browser never uses a copy of the page that names
|
||||
# assets a newer build no longer has without first checking with the
|
||||
# server that it is current.
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cache-Control no-cache;
|
||||
include /etc/nginx/security-headers.conf;
|
||||
}
|
||||
|
||||
# Cache static assets aggressively
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
# headless Chrome at it over CDP, and asserts on computed layout at every
|
||||
# viewport width derived from the app's own CSS. See
|
||||
# test/viewport/README.md for what this covers and what it cannot.
|
||||
# Before the browser starts, it checks that the page is served with
|
||||
# Cache-Control: no-cache and Content-Security-Policy.
|
||||
#
|
||||
# Deliberately not part of script/check: it needs Docker and takes far
|
||||
# longer than the 20s budget make test has to stay inside.
|
||||
@@ -75,6 +77,25 @@ main() {
|
||||
-v "$ROOT/configs/security-headers.conf:/etc/nginx/security-headers.conf:ro" \
|
||||
"$SERVER_IMAGE" > /dev/null
|
||||
|
||||
# The page, and a path with no file of its own, which falls back to
|
||||
# it, must go out with Cache-Control: no-cache. That add_header in
|
||||
# its location drops the server's own unless the location includes
|
||||
# the security headers again; only Content-Security-Policy is
|
||||
# checked, as it comes from the same file as the rest. curl tries up
|
||||
# to ten more times, a second apart, while nginx starts.
|
||||
for path in / /no-such-page; do
|
||||
headers="$(docker exec "$SERVER" curl -sS -o /dev/null -D - \
|
||||
--retry 10 --retry-connrefused --retry-delay 1 \
|
||||
"http://127.0.0.1:8080$path" | tr -d '\r')"
|
||||
if ! printf '%s\n' "$headers" | grep -qix 'Cache-Control: no-cache' ||
|
||||
! printf '%s\n' "$headers" | grep -qi '^Content-Security-Policy:'; then
|
||||
echo "frontend-viewport-test: $path needs" \
|
||||
"Cache-Control: no-cache and Content-Security-Policy; got:" >&2
|
||||
printf '%s\n' "$headers" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# The image's own entrypoint already exposes CDP on 9222 and passes
|
||||
# --no-sandbox, so only extra flags belong here; re-specifying the
|
||||
# debugging port collides with it and leaves the endpoint bound to
|
||||
|
||||
Reference in New Issue
Block a user