Files
Juan DenisandClaude Fable 5.1 bbce447e85 fix(nginx): serve /serverkit-vendor/*.mjs shims as text/javascript
Stock nginx (1.18-1.24 on Debian/Ubuntu) has no `.mjs` entry in mime.types,
so the runtime-extension vendor shims the import map points at came back as
application/octet-stream. Browsers refuse to execute an ES module with that
type, so every runtime-loaded extension (WordPress, Git, Mail, Fail2ban, ...)
failed on fresh installs with "Failed to fetch dynamically imported module:
blob:..." (Chrome) or "'application/octet-stream' is not a valid JavaScript
MIME type" (Safari).

Add a location block to the shipped vhosts (and the container frontend
config) that forces text/javascript for /serverkit-vendor/*.mjs. The regex is
anchored to that folder: a bare `\.mjs$` would also capture the
/api/v1/plugins/<slug>/assets/dist/index.mjs route and 404 every bundle.
update.sh already re-copies these vhosts, so existing installs pick it up on
their next update.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NWc84WDsWR7M55Mn7qtpie
2026-09-08 00:25:25 -04:00

113 lines
4.3 KiB
Plaintext

# ServerKit - Insecure HTTP fallback configuration
# /etc/nginx/sites-available/serverkit-insecure.conf
#
# ⚠️ WARNING: This configuration does NOT use SSL/TLS. All traffic between
# browsers and this server is unencrypted. Passwords, tokens, and any
# data sent to the panel or hosted apps can be read by anyone on the
# network path.
#
# This mode exists only as a convenience for local networks, temporary
# testing, or situations where you cannot obtain certificates. It is
# strongly recommended to switch to the secure config as soon as possible.
#
# To switch to HTTPS later:
# 1. Obtain certificates (Let's Encrypt or Cloudflare Origin CA).
# 2. Replace this file with serverkit.conf:
# sudo cp /opt/serverkit/nginx/sites-available/serverkit.conf \
# /etc/nginx/sites-available/serverkit.conf
# 3. Update the certificate paths inside that file.
# 4. sudo nginx -t && sudo systemctl reload nginx
server {
listen 80 default_server;
server_name _;
# Keep the panel out of search indexes. Defense-in-depth only — port
# scanners (Shodan/Censys) ignore this; real hiding is access control.
add_header X-Robots-Tag "noindex, nofollow, noarchive" always;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# ServerKit frontend — served directly by host nginx from the built SPA
# bundle (no container). Path tracks the /opt/serverkit symlink so blue/green
# updates swap assets atomically; the installer rewrites it for a custom
# SERVERKIT_DIR.
root /opt/serverkit/frontend/dist;
index index.html;
location /assets/ {
try_files $uri =404;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Runtime-extension vendor shims (/serverkit-vendor/*.mjs). The import map in
# index.html resolves react / react-dom / react-router-dom / i18next /
# serverkit-sdk to these, and the browser refuses to run an ES module served
# with a non-JavaScript MIME type. Stock nginx (1.18-1.24, Debian/Ubuntu
# mime.types) has no `.mjs` entry and falls back to application/octet-stream,
# which surfaces as "Failed to fetch dynamically imported module: blob:..."
# (Chrome) or "'application/octet-stream' is not a valid JavaScript MIME
# type" (Safari) on EVERY runtime-loaded extension. Force the type here
# instead of editing the system mime.types. Not hashed, so revalidate.
# Anchored to the shim folder: a bare `\.mjs$` regex would also capture
# /api/v1/plugins/<slug>/assets/dist/index.mjs (regex locations beat the
# /api prefix block) and 404 every extension bundle instead.
location ~* ^/serverkit-vendor/.+\.mjs$ {
types { }
default_type text/javascript;
try_files $uri =404;
expires -1;
}
location / {
# expires -1 → Cache-Control: no-cache — the entrypoint must
# revalidate every load or a cached index.html references hashed
# assets deleted by the next blue/green switch.
expires -1;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
location /socket.io {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
}
include /etc/nginx/serverkit-locations/*.conf;
location ~ /\. {
deny all;
}
location ~* \.(env|git|gitignore|dockerignore)$ {
deny all;
}
}
include /etc/nginx/serverkit-conf.d/*.conf;