summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-01 22:34:19 +0100
committersalmonik <salmonik@tuta.io>2026-11-01 22:34:19 +0100
commitcd3f93bcc2d8e4a5934a19b13f7fdf30ee4e130c (patch)
treec40914124410c0e5a407a14ba284774dbfbff03f /web
parentv4.4.0: stable installers that follow the newest published build (diff)
downloadzagan-bet-cd3f93bcc2d8e4a5934a19b13f7fdf30ee4e130c.tar.gz
zagan-bet-cd3f93bcc2d8e4a5934a19b13f7fdf30ee4e130c.zip
web: serve latest.php through PHP and refuse stray PHP files
nginx example now routes index.php and latest.php to php-fpm, sends the newest-version feed with no-store, keeps the stable installers revalidated, and returns 404 for every other .php request so releases.php cannot be downloaded as source. Documents Debian and Arch pool sockets.
Diffstat (limited to 'web')
-rw-r--r--web/README.md38
-rw-r--r--web/nginx.conf.example39
2 files changed, 58 insertions, 19 deletions
diff --git a/web/README.md b/web/README.md
index 13f5f85..81346fd 100644
--- a/web/README.md
+++ b/web/README.md
@@ -71,34 +71,46 @@ Cargo itself cannot produce these names: it always writes
71## nginx 71## nginx
72 72
73`web/nginx.conf.example` is a working server block for a subdomain with an 73`web/nginx.conf.example` is a working server block for a subdomain with an
74existing certificate. On Arch-based systems: 74existing certificate. Debian/Ubuntu and Arch install it the same way:
75 75
76```sh 76```sh
77# Debian / Ubuntu
78sudo apt install nginx php-fpm
79sudo install -d -o "$USER" -g www-data -m 0755 /srv/zagan.bet
80
81# Arch
77sudo pacman -S nginx php-fpm 82sudo pacman -S nginx php-fpm
78sudo install -d -o "$USER" -g http -m 0755 /srv/zagan.bet 83sudo install -d -o "$USER" -g http -m 0755 /srv/zagan.bet
84
79sudo cp web/nginx.conf.example /etc/nginx/conf.d/zagan.bet.conf 85sudo cp web/nginx.conf.example /etc/nginx/conf.d/zagan.bet.conf
80# edit server_name, root and the certificate paths, then: 86# edit server_name, root, socket and certificate paths, then:
81sudo nginx -t && sudo systemctl enable --now nginx php-fpm 87sudo nginx -t && sudo systemctl enable --now nginx php-fpm
82``` 88```
83 89
84The pool socket and user are Arch defaults (`http`, 90The example uses Debian/Ubuntu's pool socket (`/run/php/php-fpm.sock`); Arch
85`/run/php-fpm/php-fpm.sock`). Check them and adjust `fastcgi_pass` if your 91uses `/run/php-fpm/php-fpm.sock`. Check the pool's user and socket and adjust
86distribution differs: 92`fastcgi_pass` if your distribution differs:
87 93
88```sh 94```sh
89grep -E '^(user|group|listen)' /etc/php/php-fpm.d/www.conf 95grep -E '^(user|group|listen)' /etc/php/*/fpm/pool.d/www.conf /etc/php-fpm.d/www.conf 2>/dev/null
90``` 96```
91 97
92`rsync` publishes files as `0755`/`0644`, so the `http` worker and the php-fpm 98`rsync` publishes files as `0755`/`0644`, so the PHP worker can read them
93pool can read them without extra group juggling. `index.php` is the only 99without extra group juggling. `index.php` and `latest.php` are the only
94script; any other `.php` request 404s instead of executing. 100scripts; `releases.php` is an include, and the example returns 404 for every
101other `.php` request instead of serving it as source.
102
103The example sends `/latest.php` with `no-store` and the two installers with
104`no-cache`, so a new release is picked up immediately without giving up the
105immutable caching on `/bin/`.
95 106
96PHP's opcache is enabled by default and revalidates timestamps every two 107PHP's opcache is enabled by default and revalidates timestamps every two
97seconds, so a deployed `index.php` takes effect without reloading php-fpm. If 108seconds, so a deployed `index.php` or `latest.php` takes effect without
98you ever set `opcache.validate_timestamps=0`, reload php-fpm after a deploy. 109reloading php-fpm. If you ever set `opcache.validate_timestamps=0`, reload
110php-fpm after a deploy.
99 111
100The php-fpm unit on Arch uses `PrivateTmp=`, so the hash cache in `/tmp` lives 112The php-fpm unit often uses `PrivateTmp=`, so the hash cache in `/tmp` lives in
101in the service's private tmp. It still works; point `ZAGAN_HASH_CACHE` at a 113the service's private tmp. It still works; point `ZAGAN_HASH_CACHE` at a
102writable path such as `/var/cache/zagan-bet-hashes.json` if you want it to 114writable path such as `/var/cache/zagan-bet-hashes.json` if you want it to
103survive a service restart. 115survive a service restart.
104 116
diff --git a/web/nginx.conf.example b/web/nginx.conf.example
index ad001d1..9fc942f 100644
--- a/web/nginx.conf.example
+++ b/web/nginx.conf.example
@@ -1,11 +1,12 @@
1# zagan.bet download page — nginx + php-fpm example. 1# zagan.bet download page — nginx + php-fpm example.
2# 2#
3# Install at /etc/nginx/conf.d/zagan.bet.conf (the packaged nginx.conf includes 3# Install at /etc/nginx/conf.d/zagan.bet.conf (the packaged nginx.conf includes
4# conf.d/*.conf). Replace the server name, root and certificate paths. 4# conf.d/*.conf). Replace the server name, root, socket and certificate paths.
5# 5#
6# Only index.php is PHP; everything under /bin/ is served as static files. 6# Only index.php and latest.php are PHP. Everything else under the web root is
7# The binaries are versioned, so they can be cached forever — which means you 7# static: /bin/ is immutable and versioned, while the installers are stable and
8# must bump the version (Cargo.toml) instead of overwriting a published file. 8# revalidated. Any other .php request is refused, so releases.php (an include)
9# and stray scripts cannot be downloaded as source.
9 10
10server { 11server {
11 listen 80; 12 listen 80;
@@ -17,7 +18,7 @@ server {
17server { 18server {
18 listen 443 ssl; 19 listen 443 ssl;
19 listen [::]:443 ssl; 20 listen [::]:443 ssl;
20 http2 on; 21 http2 on; # nginx < 1.25.1: listen 443 ssl http2;
21 server_name dl.example.com; 22 server_name dl.example.com;
22 23
23 root /srv/zagan.bet; 24 root /srv/zagan.bet;
@@ -39,15 +40,41 @@ server {
39 # The page rescans bin/ on every request, so don't cache it. 40 # The page rescans bin/ on every request, so don't cache it.
40 location = /index.php { 41 location = /index.php {
41 include fastcgi_params; 42 include fastcgi_params;
42 fastcgi_pass unix:/run/php-fpm/php-fpm.sock; 43 # Debian/Ubuntu: /run/php/php-fpm.sock
44 # Arch: /run/php-fpm/php-fpm.sock
45 fastcgi_pass unix:/run/php/php-fpm.sock;
43 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 46 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
44 add_header Cache-Control "no-cache"; 47 add_header Cache-Control "no-cache";
45 } 48 }
46 49
50 # The newest-version feed read by install.sh and install.ps1.
51 location = /latest.php {
52 include fastcgi_params;
53 fastcgi_pass unix:/run/php/php-fpm.sock;
54 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
55 add_header Cache-Control "no-store";
56 }
57
58 # Stable installers: always revalidate so a fix reaches the next download.
59 location = /install.sh {
60 default_type text/plain;
61 add_header Cache-Control "no-cache";
62 }
63
64 location = /install.ps1 {
65 default_type text/plain;
66 add_header Cache-Control "no-cache";
67 }
68
47 location / { 69 location / {
48 try_files $uri $uri/ =404; 70 try_files $uri $uri/ =404;
49 } 71 }
50 72
73 # releases.php is an include only; no other script is meant to be served.
74 location ~ \.php$ {
75 return 404;
76 }
77
51 # Never serve dotfiles (.gitignore, editor leftovers). 78 # Never serve dotfiles (.gitignore, editor leftovers).
52 location ~ /\. { 79 location ~ /\. {
53 deny all; 80 deny all;