@lopatnov/express-reverse-proxy 3.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +192 -192
- package/README.md +802 -17
- package/package.json +16 -5
- package/server-config.schema.json +321 -0
- package/server.js +284 -10
package/README.md
CHANGED
|
@@ -18,14 +18,44 @@
|
|
|
18
18
|
- [Demo](#demo)
|
|
19
19
|
- [How It Works](#how-it-works)
|
|
20
20
|
- [CLI Options](#cli-options)
|
|
21
|
+
- [--config](#--config)
|
|
22
|
+
- [--init](#--init)
|
|
23
|
+
- [--cluster](#--cluster)
|
|
24
|
+
- [--cluster-config](#--cluster-config)
|
|
21
25
|
- [Configuration](#configuration)
|
|
22
26
|
- [port](#port)
|
|
27
|
+
- [logging](#logging)
|
|
28
|
+
- [hotReload](#hotreload)
|
|
29
|
+
- [compression](#compression)
|
|
30
|
+
- [helmet](#helmet)
|
|
31
|
+
- [cors](#cors)
|
|
32
|
+
- [favicon](#favicon)
|
|
33
|
+
- [responseTime](#responsetime)
|
|
34
|
+
- [rateLimit](#ratelimit)
|
|
35
|
+
- [basicAuth](#basicauth)
|
|
36
|
+
- [healthCheck](#healthcheck)
|
|
37
|
+
- [cgi](#cgi)
|
|
38
|
+
- [upload](#upload)
|
|
23
39
|
- [headers](#headers)
|
|
40
|
+
- [redirects](#redirects)
|
|
24
41
|
- [folders](#folders)
|
|
25
42
|
- [proxy](#proxy)
|
|
26
43
|
- [unhandled](#unhandled)
|
|
27
44
|
- [host](#host)
|
|
45
|
+
- [ssl](#ssl)
|
|
28
46
|
- [Configuration Recipes](#configuration-recipes)
|
|
47
|
+
- [Static files + proxy fallback](#static-files-first-then-fall-back-to-back-end)
|
|
48
|
+
- [Static files + API on a specific path](#static-files--api-on-a-specific-path)
|
|
49
|
+
- [Hot reload dev server](#hot-reload-dev-server)
|
|
50
|
+
- [HTTPS with automatic HTTP redirect](#https-with-automatic-http-redirect)
|
|
51
|
+
- [HTTPS with a self-signed certificate](#https-with-a-self-signed-certificate-local-dev)
|
|
52
|
+
- [Production hardening](#production-hardening-helmet--cors--compression)
|
|
53
|
+
- [Protected admin area](#protected-admin-area)
|
|
54
|
+
- [URL migration](#url-migration-permanent-redirects)
|
|
55
|
+
- [Load-balanced API proxy](#load-balanced-api-proxy)
|
|
56
|
+
- [File upload server](#file-upload-server)
|
|
57
|
+
- [Health check for Docker / Kubernetes](#health-check-for-docker--kubernetes)
|
|
58
|
+
- [CORS headers + rich error responses](#cors-headers--rich-error-responses)
|
|
29
59
|
- [Docker & PM2](#docker--pm2)
|
|
30
60
|
- [Testing](#testing)
|
|
31
61
|
- [Troubleshooting](#troubleshooting)
|
|
@@ -59,7 +89,13 @@ npx @lopatnov/express-reverse-proxy
|
|
|
59
89
|
|
|
60
90
|
## Quick Start
|
|
61
91
|
|
|
62
|
-
1.
|
|
92
|
+
1. Generate a `server-config.json` interactively (or create it manually):
|
|
93
|
+
|
|
94
|
+
```shell
|
|
95
|
+
express-reverse-proxy --init
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Or create it manually:
|
|
63
99
|
|
|
64
100
|
```json
|
|
65
101
|
{
|
|
@@ -131,41 +167,111 @@ Browser
|
|
|
131
167
|
|
|
132
168
|
## How It Works
|
|
133
169
|
|
|
134
|
-
Every incoming request
|
|
170
|
+
Every incoming request passes through the middleware chain in this order:
|
|
135
171
|
|
|
136
172
|
```
|
|
137
173
|
Request
|
|
138
174
|
│
|
|
139
|
-
├─▶
|
|
175
|
+
├─▶ Logging (morgan) — log to console or file
|
|
176
|
+
├─▶ responseTime — measure and record latency
|
|
177
|
+
├─▶ cors — CORS headers + preflight OPTIONS
|
|
178
|
+
├─▶ compression — gzip/deflate response body
|
|
179
|
+
├─▶ helmet — security HTTP headers
|
|
180
|
+
├─▶ favicon — serve /favicon.ico from memory
|
|
181
|
+
│
|
|
182
|
+
├─▶ healthCheck — GET /__health__ → {status, uptime}
|
|
183
|
+
│ └─▶ Path matches → respond immediately (bypasses auth below)
|
|
184
|
+
│
|
|
185
|
+
├─▶ rateLimit — 429 if client over limit
|
|
186
|
+
├─▶ basicAuth — 401 if credentials missing/wrong
|
|
187
|
+
│
|
|
188
|
+
├─▶ Custom headers applied (headers)
|
|
189
|
+
│
|
|
190
|
+
├─▶ Redirect rules checked (redirects)
|
|
191
|
+
│ └─▶ Path matches → 301/302 redirect
|
|
140
192
|
│
|
|
141
193
|
├─▶ Static files checked (folders, in order)
|
|
142
194
|
│ └─▶ File found → serve it
|
|
143
195
|
│
|
|
196
|
+
├─▶ CGI scripts checked (cgi)
|
|
197
|
+
│ └─▶ Path + extension matches → execute script → stream response
|
|
198
|
+
│
|
|
199
|
+
├─▶ File upload handler (upload)
|
|
200
|
+
│ └─▶ POST path matches → save files, return JSON
|
|
201
|
+
│ GET path matches → serve uploaded file
|
|
202
|
+
│
|
|
144
203
|
├─▶ Reverse proxy rules checked (proxy)
|
|
145
204
|
│ └─▶ Path matches → forward to back-end → return response
|
|
205
|
+
│ (round-robin if multiple targets configured)
|
|
146
206
|
│
|
|
147
207
|
└─▶ No match → unhandled handler (by Accept header)
|
|
148
|
-
└─▶ Return status + body
|
|
208
|
+
└─▶ Return status + body
|
|
149
209
|
```
|
|
150
210
|
|
|
151
|
-
|
|
211
|
+
Options that are not configured are skipped entirely. Static files always take priority over proxy rules.
|
|
152
212
|
|
|
153
213
|
---
|
|
154
214
|
|
|
155
215
|
## CLI Options
|
|
156
216
|
|
|
217
|
+
The package installs two equivalent commands — use whichever you prefer:
|
|
218
|
+
|
|
219
|
+
```shell
|
|
220
|
+
express-reverse-proxy [options]
|
|
221
|
+
lerp [options]
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`lerp` is a short alias for **L**opatnov **E**xpress **R**everse **P**roxy.
|
|
225
|
+
|
|
157
226
|
| Option | Description |
|
|
158
227
|
| ------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
159
228
|
| `--help` | Print help and exit |
|
|
160
229
|
| `--config <file>` | Path to the JSON configuration file. Default: `server-config.json` |
|
|
230
|
+
| `--init` | Interactively create a `server-config.json` in the current directory |
|
|
161
231
|
| `--cluster [action]` | Manage the PM2 cluster. Action defaults to `start` when omitted |
|
|
162
232
|
| `--cluster-config <file>` | Path to a custom PM2 ecosystem config file. Default: `ecosystem.config.cjs` next to `server.js` |
|
|
163
233
|
|
|
164
|
-
### --
|
|
234
|
+
### --config
|
|
235
|
+
|
|
236
|
+
Specify the path to the configuration file. Accepts a file path or a directory (in which case `server-config.json` inside that directory is used).
|
|
237
|
+
|
|
238
|
+
```shell
|
|
239
|
+
express-reverse-proxy --config ./configs/prod.json
|
|
240
|
+
express-reverse-proxy --config ./configs/
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Default: `server-config.json` in the current working directory.
|
|
244
|
+
|
|
245
|
+
If the file is not found and `--config` was explicitly provided, the server exits with an error. If no `--config` is given and the default file is missing, the server starts with built-in defaults (`port: 8000`, `folders: "."`) and prints a warning.
|
|
246
|
+
|
|
247
|
+
### --init
|
|
248
|
+
|
|
249
|
+
Interactively creates a `server-config.json` in the current working directory. Asks for port, static folder, optional proxy target, and hot reload preference.
|
|
250
|
+
|
|
251
|
+
```shell
|
|
252
|
+
express-reverse-proxy --init
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Example session:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
Port [8000]: 8080
|
|
259
|
+
Static folder [.]: www
|
|
260
|
+
Proxy path (e.g. /api) [skip]: /api
|
|
261
|
+
Proxy target for /api: http://localhost:4000
|
|
262
|
+
Hot reload? [y/N]: y
|
|
263
|
+
[init] Created /your/project/server-config.json
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
If `server-config.json` already exists, the command asks before overwriting.
|
|
267
|
+
|
|
268
|
+
### --cluster
|
|
269
|
+
|
|
270
|
+
Manage the PM2 process cluster. Action defaults to `start` when omitted.
|
|
165
271
|
|
|
166
272
|
| Action | Description |
|
|
167
273
|
| --------- | ------------------------------------------------------ |
|
|
168
|
-
| `start` | Start the
|
|
274
|
+
| `start` | Start the cluster (default when action is omitted) |
|
|
169
275
|
| `stop` | Stop all cluster instances |
|
|
170
276
|
| `restart` | Restart all cluster instances |
|
|
171
277
|
| `status` | Show PM2 process status table |
|
|
@@ -173,7 +279,7 @@ Static files always take priority over proxy rules. Proxies are checked only whe
|
|
|
173
279
|
| `monitor` | Open the PM2 real-time monitor |
|
|
174
280
|
|
|
175
281
|
```shell
|
|
176
|
-
express-reverse-proxy --cluster
|
|
282
|
+
express-reverse-proxy --cluster # same as --cluster start
|
|
177
283
|
express-reverse-proxy --cluster start
|
|
178
284
|
express-reverse-proxy --cluster stop
|
|
179
285
|
express-reverse-proxy --cluster restart
|
|
@@ -182,25 +288,41 @@ express-reverse-proxy --cluster logs
|
|
|
182
288
|
express-reverse-proxy --cluster monitor
|
|
183
289
|
```
|
|
184
290
|
|
|
185
|
-
Pass a custom config to cluster workers
|
|
291
|
+
Pass `--config` to forward a custom config path to all cluster workers:
|
|
186
292
|
|
|
187
293
|
```shell
|
|
188
294
|
express-reverse-proxy --cluster start --config ./configs/prod.json
|
|
189
295
|
```
|
|
190
296
|
|
|
191
|
-
|
|
297
|
+
### --cluster-config
|
|
298
|
+
|
|
299
|
+
Override the PM2 ecosystem config file used to start the cluster. Default: `ecosystem.config.cjs` in the same directory as `server.js`.
|
|
192
300
|
|
|
193
301
|
```shell
|
|
194
302
|
express-reverse-proxy --cluster start --cluster-config ./my-ecosystem.config.cjs
|
|
195
303
|
express-reverse-proxy --cluster restart --cluster-config /etc/myapp/ecosystem.config.cjs
|
|
196
304
|
```
|
|
197
305
|
|
|
306
|
+
Useful when you need custom PM2 settings such as a different number of instances, environment variables, or log file paths. See [PM2 ecosystem documentation](https://pm2.keymetrics.io/docs/usage/application-declaration/) for all options.
|
|
307
|
+
|
|
198
308
|
---
|
|
199
309
|
|
|
200
310
|
## Configuration
|
|
201
311
|
|
|
202
312
|
All configuration lives in a single JSON file (default `server-config.json`).
|
|
203
313
|
|
|
314
|
+
### IDE autocomplete (JSON Schema)
|
|
315
|
+
|
|
316
|
+
Add a `$schema` reference to your config file to get property autocomplete, descriptions, and type checking in VS Code and other editors:
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"$schema": "https://unpkg.com/@lopatnov/express-reverse-proxy/server-config.schema.json",
|
|
321
|
+
"port": 8080,
|
|
322
|
+
"folders": "www"
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
204
326
|
### Environment variables
|
|
205
327
|
|
|
206
328
|
| Variable | Default | Description |
|
|
@@ -220,7 +342,7 @@ The port the server listens on. Defaults to `8000`. Can also be set via the `POR
|
|
|
220
342
|
|
|
221
343
|
### logging
|
|
222
344
|
|
|
223
|
-
Controls HTTP request logging (Morgan). Enabled by default. Set to `false` to silence per-request log lines — useful in production behind another proxy, or to keep console output clean.
|
|
345
|
+
Controls HTTP request logging (Morgan). Enabled by default (`dev` format). Set to `false` to silence per-request log lines — useful in production behind another proxy, or to keep console output clean.
|
|
224
346
|
|
|
225
347
|
```json
|
|
226
348
|
{
|
|
@@ -230,6 +352,23 @@ Controls HTTP request logging (Morgan). Enabled by default. Set to `false` to si
|
|
|
230
352
|
}
|
|
231
353
|
```
|
|
232
354
|
|
|
355
|
+
**Object form** — write logs to a file and/or choose a different format:
|
|
356
|
+
|
|
357
|
+
```json
|
|
358
|
+
{
|
|
359
|
+
"logging": { "format": "combined", "file": "./logs/access.log" }
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
| Option | Default | Description |
|
|
364
|
+
| -------- | ------------ | -------------------------------------------------------------- |
|
|
365
|
+
| `format` | `"combined"` | Morgan format: `combined`, `common`, `dev`, `short`, or `tiny` |
|
|
366
|
+
| `file` | none | Path to log file (relative to config file). Appended if exists |
|
|
367
|
+
|
|
368
|
+
When `file` is set, logs are written to the file only (not to the console).
|
|
369
|
+
|
|
370
|
+
> Logging is applied per-site, so each virtual host can have its own format and log file.
|
|
371
|
+
|
|
233
372
|
### hotReload
|
|
234
373
|
|
|
235
374
|
Watches the `folders` directories for file changes and automatically reloads connected browser tabs. Uses Server-Sent Events (SSE). Intended for local development only.
|
|
@@ -279,6 +418,42 @@ Add headers to every response — useful for CORS in development.
|
|
|
279
418
|
}
|
|
280
419
|
```
|
|
281
420
|
|
|
421
|
+
### redirects
|
|
422
|
+
|
|
423
|
+
Permanently or temporarily redirect URL paths to new destinations. Redirects are checked before static files and proxy rules.
|
|
424
|
+
|
|
425
|
+
**Object form** — map source paths to destinations:
|
|
426
|
+
|
|
427
|
+
```json
|
|
428
|
+
{
|
|
429
|
+
"redirects": {
|
|
430
|
+
"/old-path": "/new-path",
|
|
431
|
+
"/legacy": "https://new.example.com",
|
|
432
|
+
"/temp": { "to": "/temporary-destination", "status": 302 }
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**Array form** — explicit entries with `from`, `to`, and optional `status`:
|
|
438
|
+
|
|
439
|
+
```json
|
|
440
|
+
{
|
|
441
|
+
"redirects": [
|
|
442
|
+
{ "from": "/old", "to": "/new" },
|
|
443
|
+
{ "from": "/moved", "to": "https://example.com", "status": 301 },
|
|
444
|
+
{ "from": "/temp", "to": "/somewhere", "status": 302 }
|
|
445
|
+
]
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
| Field | Default | Description |
|
|
450
|
+
| -------- | ------- | -------------------------------------------------------- |
|
|
451
|
+
| `from` | — | Source URL path *(array form only, required)* |
|
|
452
|
+
| `to` | — | Destination path or full URL *(required)* |
|
|
453
|
+
| `status` | `301` | HTTP redirect status: `301`, `302`, `307`, or `308` |
|
|
454
|
+
|
|
455
|
+
> `301` — Moved Permanently. `302` — Found (temporary). Use `301` for permanent URL changes and `302` for temporary ones.
|
|
456
|
+
|
|
282
457
|
### folders
|
|
283
458
|
|
|
284
459
|
Serve static files. Supports three forms:
|
|
@@ -358,6 +533,18 @@ Forward requests to a back-end server. Supports three forms:
|
|
|
358
533
|
}
|
|
359
534
|
```
|
|
360
535
|
|
|
536
|
+
**Load balancing** — pass an array of targets for a path to distribute requests in round-robin:
|
|
537
|
+
|
|
538
|
+
```json
|
|
539
|
+
{
|
|
540
|
+
"proxy": {
|
|
541
|
+
"/api": ["http://backend1:3000", "http://backend2:3000", "http://backend3:3000"]
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
Requests to `/api` are forwarded to the backends in turn: `backend1`, `backend2`, `backend3`, `backend1`, …
|
|
547
|
+
|
|
361
548
|
### unhandled
|
|
362
549
|
|
|
363
550
|
Control responses when no static file or proxy rule matches. Rules are selected by the request's `Accept` header.
|
|
@@ -438,11 +625,12 @@ To use multi-site mode, make the config file an **array** instead of an object.
|
|
|
438
625
|
|
|
439
626
|
Enable HTTPS on a port by adding an `ssl` object to any site config for that port. All sites sharing the same port use the same certificate.
|
|
440
627
|
|
|
441
|
-
| Field
|
|
442
|
-
|
|
|
443
|
-
| `key`
|
|
444
|
-
| `cert`
|
|
445
|
-
| `ca`
|
|
628
|
+
| Field | Type | Description |
|
|
629
|
+
| ---------- | --------- | -------------------------------------------------------- |
|
|
630
|
+
| `key` | `string` | Path to the private key file (PEM format) |
|
|
631
|
+
| `cert` | `string` | Path to the certificate file (PEM format) |
|
|
632
|
+
| `ca` | `string` | *(optional)* Path to the CA bundle for client validation |
|
|
633
|
+
| `redirect` | `integer` | *(optional)* HTTP port to redirect (301) to HTTPS |
|
|
446
634
|
|
|
447
635
|
Paths are resolved **relative to the config file**, not the current working directory.
|
|
448
636
|
|
|
@@ -460,8 +648,363 @@ Paths are resolved **relative to the config file**, not the current working dire
|
|
|
460
648
|
}
|
|
461
649
|
```
|
|
462
650
|
|
|
651
|
+
**Automatic HTTP → HTTPS redirect** — set `redirect` to the HTTP port to also listen on plain HTTP and redirect all traffic to HTTPS:
|
|
652
|
+
|
|
653
|
+
```json
|
|
654
|
+
{
|
|
655
|
+
"port": 443,
|
|
656
|
+
"ssl": {
|
|
657
|
+
"key": "./certs/key.pem",
|
|
658
|
+
"cert": "./certs/cert.pem",
|
|
659
|
+
"redirect": 80
|
|
660
|
+
},
|
|
661
|
+
"folders": "./public"
|
|
662
|
+
}
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
This starts an HTTPS server on port `443` and a tiny redirect-only HTTP server on port `80`. All `http://` requests are permanently redirected (301) to `https://`.
|
|
666
|
+
|
|
463
667
|
> All site configs on the same port must either all have `ssl` or none — mixing is a startup error.
|
|
464
668
|
|
|
669
|
+
### compression
|
|
670
|
+
|
|
671
|
+
Enable gzip/deflate response compression. Reduces the size of HTML, CSS, JS, and JSON responses sent to the browser. Set to `true` for defaults, or pass an options object.
|
|
672
|
+
|
|
673
|
+
```json
|
|
674
|
+
{
|
|
675
|
+
"port": 8080,
|
|
676
|
+
"compression": true,
|
|
677
|
+
"folders": "www"
|
|
678
|
+
}
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
With custom options (see [compression docs](https://github.com/expressjs/compression#options)):
|
|
682
|
+
|
|
683
|
+
```json
|
|
684
|
+
{
|
|
685
|
+
"compression": { "level": 6, "threshold": 1024 }
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
> Compression is applied per-site. Assets that are already compressed (images, fonts, video) are not affected — the browser signals it accepts compressed responses via the `Accept-Encoding` header.
|
|
690
|
+
|
|
691
|
+
### helmet
|
|
692
|
+
|
|
693
|
+
Set security-related HTTP response headers. Protects against common web vulnerabilities by configuring headers such as `Content-Security-Policy`, `X-Frame-Options`, `Strict-Transport-Security`, and others.
|
|
694
|
+
|
|
695
|
+
```json
|
|
696
|
+
{
|
|
697
|
+
"port": 8080,
|
|
698
|
+
"helmet": true,
|
|
699
|
+
"folders": "www"
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
Disable a specific header (see [helmet docs](https://helmetjs.github.io/) for all options):
|
|
704
|
+
|
|
705
|
+
```json
|
|
706
|
+
{
|
|
707
|
+
"helmet": { "contentSecurityPolicy": false }
|
|
708
|
+
}
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
> When `helmet: true` is set, the default helmet configuration is applied. This may block inline scripts and cross-origin resources. Adjust `contentSecurityPolicy` or other options as needed for your project.
|
|
712
|
+
|
|
713
|
+
### cors
|
|
714
|
+
|
|
715
|
+
Enable CORS (Cross-Origin Resource Sharing) headers and handle preflight `OPTIONS` requests automatically. Useful when your front-end on one origin calls an API on a different origin.
|
|
716
|
+
|
|
717
|
+
```json
|
|
718
|
+
{
|
|
719
|
+
"port": 8080,
|
|
720
|
+
"cors": true,
|
|
721
|
+
"proxy": { "/api": "http://localhost:4000" }
|
|
722
|
+
}
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
Restrict to a specific origin (see [cors docs](https://github.com/expressjs/cors#configuration-options)):
|
|
726
|
+
|
|
727
|
+
```json
|
|
728
|
+
{
|
|
729
|
+
"cors": { "origin": "https://app.example.com" }
|
|
730
|
+
}
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
> The `cors` middleware handles `OPTIONS` preflight requests that the `headers` option cannot respond to. Use `cors` when you need to allow requests from JavaScript on a different domain — for example a React app calling this proxy's API routes.
|
|
734
|
+
|
|
735
|
+
### favicon
|
|
736
|
+
|
|
737
|
+
Serve a favicon file efficiently. The file is read into memory at startup and served from there on every `/favicon.ico` request — before static folder scanning or proxy rules run.
|
|
738
|
+
|
|
739
|
+
```json
|
|
740
|
+
{
|
|
741
|
+
"port": 8080,
|
|
742
|
+
"favicon": "./public/favicon.ico",
|
|
743
|
+
"folders": "www"
|
|
744
|
+
}
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
The path is resolved **relative to the config file**, consistent with the `ssl` option. Absolute paths are also accepted.
|
|
748
|
+
|
|
749
|
+
> If your favicon already lives inside a directory listed in `folders`, this option is not needed — `express.static` will serve it automatically.
|
|
750
|
+
|
|
751
|
+
### responseTime
|
|
752
|
+
|
|
753
|
+
Add an `X-Response-Time` header to every response, recording how long the server took to handle the request. Useful for performance monitoring and debugging.
|
|
754
|
+
|
|
755
|
+
```json
|
|
756
|
+
{
|
|
757
|
+
"port": 8080,
|
|
758
|
+
"responseTime": true,
|
|
759
|
+
"folders": "www"
|
|
760
|
+
}
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
With custom precision (see [response-time docs](https://github.com/expressjs/response-time#options)):
|
|
764
|
+
|
|
765
|
+
```json
|
|
766
|
+
{
|
|
767
|
+
"responseTime": { "digits": 0, "suffix": false }
|
|
768
|
+
}
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
### rateLimit
|
|
772
|
+
|
|
773
|
+
Limit the number of requests a client can make in a time window. Responds with `429 Too Many Requests` when the limit is exceeded. Useful when running without a dedicated reverse proxy.
|
|
774
|
+
|
|
775
|
+
```json
|
|
776
|
+
{
|
|
777
|
+
"port": 8080,
|
|
778
|
+
"rateLimit": { "windowMs": 60000, "limit": 100 },
|
|
779
|
+
"folders": "www"
|
|
780
|
+
}
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
| Option | Default | Description |
|
|
784
|
+
| ----------- | -------- | ------------------------------------------------ |
|
|
785
|
+
| `windowMs` | `60000` | Time window in milliseconds |
|
|
786
|
+
| `limit` | `5` | Maximum requests per client per window |
|
|
787
|
+
| `message` | built-in | Response body when limit is exceeded |
|
|
788
|
+
|
|
789
|
+
See [express-rate-limit docs](https://express-rate-limit.mintlify.app/reference/configuration) for all options.
|
|
790
|
+
|
|
791
|
+
> Rate limiting is applied per-site and per IP address. In production behind Nginx or Caddy, configure rate limiting there instead — it runs before Node.js and is more efficient.
|
|
792
|
+
|
|
793
|
+
### basicAuth
|
|
794
|
+
|
|
795
|
+
Protect the site with HTTP Basic Authentication. All requests must include valid credentials or the server responds with `401 Unauthorized`.
|
|
796
|
+
|
|
797
|
+
```json
|
|
798
|
+
{
|
|
799
|
+
"port": 8080,
|
|
800
|
+
"basicAuth": {
|
|
801
|
+
"users": { "admin": "s3cr3t" },
|
|
802
|
+
"challenge": true
|
|
803
|
+
},
|
|
804
|
+
"folders": "www"
|
|
805
|
+
}
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
| Option | Default | Description |
|
|
809
|
+
| ----------- | ------- | ------------------------------------------------------------ |
|
|
810
|
+
| `users` | — | Object mapping username → password *(required)* |
|
|
811
|
+
| `challenge` | `false` | Send `WWW-Authenticate` header to trigger browser login dialog |
|
|
812
|
+
| `realm` | — | Realm string shown in the browser login dialog |
|
|
813
|
+
|
|
814
|
+
See [express-basic-auth docs](https://github.com/LionC/express-basic-auth#options) for all options.
|
|
815
|
+
|
|
816
|
+
> Passwords are compared in plain text. Do not use Basic Auth over plain HTTP in production — always combine with `ssl` or put behind a TLS-terminating proxy.
|
|
817
|
+
|
|
818
|
+
### healthCheck
|
|
819
|
+
|
|
820
|
+
Expose a lightweight health check endpoint. Returns a JSON response with server status, uptime, and current timestamp. Useful for load balancers, monitoring systems, and container health checks.
|
|
821
|
+
|
|
822
|
+
```json
|
|
823
|
+
{
|
|
824
|
+
"port": 8080,
|
|
825
|
+
"healthCheck": true,
|
|
826
|
+
"folders": "www"
|
|
827
|
+
}
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
Default endpoint: `GET /__health__`
|
|
831
|
+
|
|
832
|
+
```json
|
|
833
|
+
{ "status": "ok", "uptime": 42.3, "timestamp": "2026-01-01T12:00:00.000Z" }
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
Custom path:
|
|
837
|
+
|
|
838
|
+
```json
|
|
839
|
+
{
|
|
840
|
+
"healthCheck": { "path": "/health" }
|
|
841
|
+
}
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
| Option | Default | Description |
|
|
845
|
+
| ------ | -------------- | -------------------------------- |
|
|
846
|
+
| `path` | `"/__health__"` | URL path of the health endpoint |
|
|
847
|
+
|
|
848
|
+
> The health check endpoint is placed before rate limiting and basic auth — it is always publicly accessible regardless of other authentication settings.
|
|
849
|
+
|
|
850
|
+
### cgi
|
|
851
|
+
|
|
852
|
+
Execute server-side scripts using the CGI (Common Gateway Interface) protocol. When a request matches the configured URL prefix and file extension, the script is spawned as a child process — HTTP headers become environment variables, the request body is piped to stdin, and the script's stdout is streamed back as the HTTP response.
|
|
853
|
+
|
|
854
|
+
```json
|
|
855
|
+
{
|
|
856
|
+
"port": 8080,
|
|
857
|
+
"cgi": {
|
|
858
|
+
"path": "/cgi-bin",
|
|
859
|
+
"dir": "./cgi-bin",
|
|
860
|
+
"extensions": [".cgi", ".pl", ".py", ".sh"],
|
|
861
|
+
"interpreters": {
|
|
862
|
+
".py": "python3",
|
|
863
|
+
".sh": "sh",
|
|
864
|
+
".pl": "perl"
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
| Option | Default | Description |
|
|
871
|
+
| -------------- | ------------------------------------- | -------------------------------------------------------------------- |
|
|
872
|
+
| `path` | `"/cgi-bin"` | URL prefix that triggers CGI dispatch |
|
|
873
|
+
| `dir` | `"./cgi-bin"` | Local directory containing scripts (resolved relative to config file)|
|
|
874
|
+
| `extensions` | `[".cgi", ".pl", ".py", ".sh"]` | File extensions treated as executable CGI scripts |
|
|
875
|
+
| `interpreters` | `{}` | Map of file extension → interpreter command |
|
|
876
|
+
|
|
877
|
+
Shorthand — point directly to the script directory (all defaults apply):
|
|
878
|
+
|
|
879
|
+
```json
|
|
880
|
+
{
|
|
881
|
+
"cgi": "./cgi-bin"
|
|
882
|
+
}
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
CGI environment variables set for every request:
|
|
886
|
+
|
|
887
|
+
| Variable | Value |
|
|
888
|
+
| ----------------- | -------------------------------------------------------- |
|
|
889
|
+
| `REQUEST_METHOD` | HTTP method (`GET`, `POST`, …) |
|
|
890
|
+
| `QUERY_STRING` | URL query string (without `?`) |
|
|
891
|
+
| `CONTENT_TYPE` | `Content-Type` request header |
|
|
892
|
+
| `CONTENT_LENGTH` | `Content-Length` request header |
|
|
893
|
+
| `SCRIPT_FILENAME` | Absolute path to the script file |
|
|
894
|
+
| `SCRIPT_NAME` | URL path to the script (e.g. `/cgi-bin/hello.py`) |
|
|
895
|
+
| `SERVER_NAME` | Requested hostname |
|
|
896
|
+
| `SERVER_PORT` | Server listen port |
|
|
897
|
+
| `REMOTE_ADDR` | Client IP address |
|
|
898
|
+
| `HTTP_*` | All request headers (e.g. `HTTP_ACCEPT`, `HTTP_HOST`) |
|
|
899
|
+
|
|
900
|
+
A minimal Python example (`cgi-bin/hello.py`):
|
|
901
|
+
|
|
902
|
+
```python
|
|
903
|
+
#!/usr/bin/env python3
|
|
904
|
+
print("Content-Type: text/plain")
|
|
905
|
+
print("Status: 200 OK")
|
|
906
|
+
print()
|
|
907
|
+
print("Hello from CGI!")
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
> **Unix/macOS note:** Scripts must be executable: `chmod +x cgi-bin/hello.py`. Alternatively, configure an `interpreters` entry for the extension — no executable bit required when an interpreter is specified.
|
|
911
|
+
|
|
912
|
+
> **Windows note:** Scripts are not directly executable on Windows. You must configure `interpreters` for every extension you use; otherwise the request returns a `500` spawn error.
|
|
913
|
+
|
|
914
|
+
**Array form** — multiple independent CGI directories on the same site:
|
|
915
|
+
|
|
916
|
+
```json
|
|
917
|
+
{
|
|
918
|
+
"cgi": [
|
|
919
|
+
{
|
|
920
|
+
"path": "/py-scripts",
|
|
921
|
+
"dir": "./py-scripts",
|
|
922
|
+
"extensions": [".py"],
|
|
923
|
+
"interpreters": { ".py": "python3" }
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
"path": "/node-scripts",
|
|
927
|
+
"dir": "./node-scripts",
|
|
928
|
+
"extensions": [".js"],
|
|
929
|
+
"interpreters": { ".js": "node" }
|
|
930
|
+
}
|
|
931
|
+
]
|
|
932
|
+
}
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
Each entry in the array sets up an independent CGI mount point with its own directory, URL prefix, extensions, and interpreters.
|
|
936
|
+
|
|
937
|
+
### upload
|
|
938
|
+
|
|
939
|
+
Accept file uploads via `multipart/form-data` and save them to a local directory. Uploaded files can be retrieved immediately via `GET`.
|
|
940
|
+
|
|
941
|
+
```json
|
|
942
|
+
{
|
|
943
|
+
"port": 8080,
|
|
944
|
+
"upload": {
|
|
945
|
+
"path": "/upload",
|
|
946
|
+
"dir": "./uploads",
|
|
947
|
+
"maxFileSize": 10485760,
|
|
948
|
+
"maxFiles": 10,
|
|
949
|
+
"allowedTypes": ["image/jpeg", "image/png", "application/pdf"],
|
|
950
|
+
"fieldName": "file"
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
Shorthand — directory only (all defaults apply):
|
|
956
|
+
|
|
957
|
+
```json
|
|
958
|
+
{
|
|
959
|
+
"upload": "./uploads"
|
|
960
|
+
}
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
| Option | Default | Description |
|
|
964
|
+
| --------------- | -------------- | ------------------------------------------------------------------------ |
|
|
965
|
+
| `path` | `"/upload"` | URL prefix for the upload endpoint |
|
|
966
|
+
| `dir` | `"./uploads"` | Save directory (resolved relative to the config file) |
|
|
967
|
+
| `maxFileSize` | none | Maximum file size in bytes; responds with `413` when exceeded |
|
|
968
|
+
| `maxFiles` | none | Maximum number of files per request; responds with `400` when exceeded |
|
|
969
|
+
| `allowedTypes` | none | MIME type whitelist; responds with `400` when the type is not in the list|
|
|
970
|
+
| `fieldName` | any field | Accept only files uploaded in this specific form field |
|
|
971
|
+
|
|
972
|
+
**Array form** — multiple upload endpoints on the same site:
|
|
973
|
+
|
|
974
|
+
```json
|
|
975
|
+
{
|
|
976
|
+
"upload": [
|
|
977
|
+
{ "path": "/photos", "dir": "./photos", "allowedTypes": ["image/jpeg", "image/png"] },
|
|
978
|
+
{ "path": "/docs", "dir": "./documents", "allowedTypes": ["application/pdf"], "maxFileSize": 5242880 }
|
|
979
|
+
]
|
|
980
|
+
}
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
**HTTP interface:**
|
|
984
|
+
|
|
985
|
+
| Method | URL | Description |
|
|
986
|
+
| ------ | ---------------- | ---------------------------------------- |
|
|
987
|
+
| `POST` | `<path>` | Upload files via `multipart/form-data` |
|
|
988
|
+
| `GET` | `<path>/<name>` | Retrieve a previously uploaded file |
|
|
989
|
+
|
|
990
|
+
`POST` success response (`200`):
|
|
991
|
+
|
|
992
|
+
```json
|
|
993
|
+
{
|
|
994
|
+
"files": [
|
|
995
|
+
{ "file": "photo-1700000000000-123456789.jpg", "size": 45678, "originalName": "photo.jpg" }
|
|
996
|
+
]
|
|
997
|
+
}
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
Upload with curl:
|
|
1001
|
+
|
|
1002
|
+
```shell
|
|
1003
|
+
curl -F "file=@photo.jpg" http://localhost:8080/upload
|
|
1004
|
+
```
|
|
1005
|
+
|
|
1006
|
+
> The upload directory is created automatically at startup if it does not exist. Saved filenames include a timestamp and random suffix to avoid collisions.
|
|
1007
|
+
|
|
465
1008
|
---
|
|
466
1009
|
|
|
467
1010
|
## Configuration Recipes
|
|
@@ -499,6 +1042,65 @@ Only `/api/*` requests go to the back-end; everything else stays local.
|
|
|
499
1042
|
- `GET /api/users` → proxied to `http://localhost:4000/users`
|
|
500
1043
|
- `GET /missing` → 404 Not Found
|
|
501
1044
|
|
|
1045
|
+
### Hot reload dev server
|
|
1046
|
+
|
|
1047
|
+
Local development setup: serve a front-end build folder, proxy API requests to a local back-end, and automatically reload the browser on file changes.
|
|
1048
|
+
|
|
1049
|
+
```json
|
|
1050
|
+
{
|
|
1051
|
+
"port": 3000,
|
|
1052
|
+
"hotReload": true,
|
|
1053
|
+
"folders": "./dist",
|
|
1054
|
+
"proxy": {
|
|
1055
|
+
"/api": "http://localhost:4000"
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
Add the client script to your HTML (or import it in your bundler entry point):
|
|
1061
|
+
|
|
1062
|
+
```html
|
|
1063
|
+
<script src="/__hot-reload__/client.js"></script>
|
|
1064
|
+
```
|
|
1065
|
+
|
|
1066
|
+
The browser reconnects automatically after server restarts.
|
|
1067
|
+
|
|
1068
|
+
---
|
|
1069
|
+
|
|
1070
|
+
### HTTPS with automatic HTTP redirect
|
|
1071
|
+
|
|
1072
|
+
Serve the site over HTTPS and redirect all plain-HTTP traffic (port 80) to HTTPS (port 443) with a permanent 301 redirect.
|
|
1073
|
+
|
|
1074
|
+
```shell
|
|
1075
|
+
mkdir certs
|
|
1076
|
+
openssl req -x509 -newkey rsa:2048 -keyout certs/key.pem -out certs/cert.pem \
|
|
1077
|
+
-days 365 -nodes -subj "/CN=example.com"
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
```json
|
|
1081
|
+
{
|
|
1082
|
+
"port": 443,
|
|
1083
|
+
"ssl": {
|
|
1084
|
+
"key": "./certs/key.pem",
|
|
1085
|
+
"cert": "./certs/cert.pem",
|
|
1086
|
+
"redirect": 80
|
|
1087
|
+
},
|
|
1088
|
+
"folders": "./public",
|
|
1089
|
+
"proxy": {
|
|
1090
|
+
"/api": "http://localhost:4000"
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
```
|
|
1094
|
+
|
|
1095
|
+
The server logs two listeners on startup:
|
|
1096
|
+
|
|
1097
|
+
```
|
|
1098
|
+
[listen] https://localhost:443
|
|
1099
|
+
[listen] http redirect :80 → https :443
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
---
|
|
1103
|
+
|
|
502
1104
|
### HTTPS with a self-signed certificate (local dev)
|
|
503
1105
|
|
|
504
1106
|
```shell
|
|
@@ -524,12 +1126,186 @@ openssl req -x509 -newkey rsa:2048 -keyout certs/key.pem -out certs/cert.pem \
|
|
|
524
1126
|
Start and open in browser (accept the self-signed cert warning):
|
|
525
1127
|
|
|
526
1128
|
```shell
|
|
527
|
-
|
|
1129
|
+
express-reverse-proxy --config server-config.json
|
|
528
1130
|
# [listen] https://localhost:8443
|
|
529
1131
|
```
|
|
530
1132
|
|
|
531
1133
|
---
|
|
532
1134
|
|
|
1135
|
+
### Production hardening (helmet + cors + compression)
|
|
1136
|
+
|
|
1137
|
+
Enable security headers, CORS, and response compression in one config:
|
|
1138
|
+
|
|
1139
|
+
```json
|
|
1140
|
+
{
|
|
1141
|
+
"port": 8080,
|
|
1142
|
+
"compression": true,
|
|
1143
|
+
"helmet": true,
|
|
1144
|
+
"cors": { "origin": "https://app.example.com" },
|
|
1145
|
+
"responseTime": true,
|
|
1146
|
+
"folders": "www",
|
|
1147
|
+
"proxy": {
|
|
1148
|
+
"/api": "http://localhost:4000"
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
```
|
|
1152
|
+
|
|
1153
|
+
---
|
|
1154
|
+
|
|
1155
|
+
### Protected admin area
|
|
1156
|
+
|
|
1157
|
+
Protect a site with rate limiting and HTTP Basic Auth. Useful for internal tools or staging environments.
|
|
1158
|
+
|
|
1159
|
+
```json
|
|
1160
|
+
{
|
|
1161
|
+
"port": 8080,
|
|
1162
|
+
"rateLimit": { "windowMs": 60000, "limit": 30 },
|
|
1163
|
+
"basicAuth": {
|
|
1164
|
+
"users": { "admin": "s3cr3t", "viewer": "readonly" },
|
|
1165
|
+
"challenge": true
|
|
1166
|
+
},
|
|
1167
|
+
"folders": "./admin",
|
|
1168
|
+
"proxy": {
|
|
1169
|
+
"/api": "http://localhost:4000"
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
```
|
|
1173
|
+
|
|
1174
|
+
- Requests without valid credentials → `401 Unauthorized` (browser shows login dialog)
|
|
1175
|
+
- More than 30 requests per minute from the same IP → `429 Too Many Requests`
|
|
1176
|
+
|
|
1177
|
+
> Always combine Basic Auth with `ssl` in production — credentials are transmitted in plain text otherwise.
|
|
1178
|
+
|
|
1179
|
+
---
|
|
1180
|
+
|
|
1181
|
+
### URL migration (permanent redirects)
|
|
1182
|
+
|
|
1183
|
+
Redirect old URLs to new ones after a site restructure, without breaking existing links or SEO rankings.
|
|
1184
|
+
|
|
1185
|
+
```json
|
|
1186
|
+
{
|
|
1187
|
+
"port": 8080,
|
|
1188
|
+
"redirects": [
|
|
1189
|
+
{ "from": "/about.html", "to": "/about", "status": 301 },
|
|
1190
|
+
{ "from": "/products.html", "to": "/products", "status": 301 },
|
|
1191
|
+
{ "from": "/blog/:slug", "to": "/posts/:slug", "status": 301 }
|
|
1192
|
+
],
|
|
1193
|
+
"folders": "./public"
|
|
1194
|
+
}
|
|
1195
|
+
```
|
|
1196
|
+
|
|
1197
|
+
Or as an object map for simple path-to-path redirects:
|
|
1198
|
+
|
|
1199
|
+
```json
|
|
1200
|
+
{
|
|
1201
|
+
"redirects": {
|
|
1202
|
+
"/old-home": "/",
|
|
1203
|
+
"/old-about": "/about",
|
|
1204
|
+
"/legacy-api": "https://api.example.com"
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
```
|
|
1208
|
+
|
|
1209
|
+
---
|
|
1210
|
+
|
|
1211
|
+
### Load-balanced API proxy
|
|
1212
|
+
|
|
1213
|
+
Distribute API traffic across multiple back-end instances using round-robin load balancing. No external load balancer required.
|
|
1214
|
+
|
|
1215
|
+
```json
|
|
1216
|
+
{
|
|
1217
|
+
"port": 8080,
|
|
1218
|
+
"folders": "./public",
|
|
1219
|
+
"proxy": {
|
|
1220
|
+
"/api": [
|
|
1221
|
+
"http://backend-1:3000",
|
|
1222
|
+
"http://backend-2:3000",
|
|
1223
|
+
"http://backend-3:3000"
|
|
1224
|
+
]
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
```
|
|
1228
|
+
|
|
1229
|
+
Requests to `/api/*` are forwarded to the three back-ends in turn. If a back-end is down, its slot in the rotation still receives requests — add a health check at the application level or use a dedicated load balancer for automatic failover.
|
|
1230
|
+
|
|
1231
|
+
---
|
|
1232
|
+
|
|
1233
|
+
### File upload server
|
|
1234
|
+
|
|
1235
|
+
Accept file uploads from a web form or API client and serve them back over HTTP.
|
|
1236
|
+
|
|
1237
|
+
```json
|
|
1238
|
+
{
|
|
1239
|
+
"port": 8080,
|
|
1240
|
+
"upload": [
|
|
1241
|
+
{
|
|
1242
|
+
"path": "/photos",
|
|
1243
|
+
"dir": "./storage/photos",
|
|
1244
|
+
"maxFileSize": 5242880,
|
|
1245
|
+
"allowedTypes": ["image/jpeg", "image/png", "image/webp"]
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
"path": "/documents",
|
|
1249
|
+
"dir": "./storage/docs",
|
|
1250
|
+
"maxFileSize": 10485760,
|
|
1251
|
+
"allowedTypes": ["application/pdf"]
|
|
1252
|
+
}
|
|
1253
|
+
]
|
|
1254
|
+
}
|
|
1255
|
+
```
|
|
1256
|
+
|
|
1257
|
+
Upload a photo:
|
|
1258
|
+
|
|
1259
|
+
```shell
|
|
1260
|
+
curl -F "file=@photo.jpg" http://localhost:8080/photos
|
|
1261
|
+
# {"files":[{"file":"photo-1700000000000-123456789.jpg","size":45678,"originalName":"photo.jpg"}]}
|
|
1262
|
+
```
|
|
1263
|
+
|
|
1264
|
+
Retrieve it:
|
|
1265
|
+
|
|
1266
|
+
```shell
|
|
1267
|
+
curl http://localhost:8080/photos/photo-1700000000000-123456789.jpg
|
|
1268
|
+
```
|
|
1269
|
+
|
|
1270
|
+
---
|
|
1271
|
+
|
|
1272
|
+
### Health check for Docker / Kubernetes
|
|
1273
|
+
|
|
1274
|
+
Add a health check endpoint and write access logs to a file — a common pattern for containerized deployments.
|
|
1275
|
+
|
|
1276
|
+
```json
|
|
1277
|
+
{
|
|
1278
|
+
"port": 8080,
|
|
1279
|
+
"healthCheck": { "path": "/health" },
|
|
1280
|
+
"logging": { "format": "combined", "file": "/var/log/app/access.log" },
|
|
1281
|
+
"compression": true,
|
|
1282
|
+
"folders": "./public",
|
|
1283
|
+
"proxy": {
|
|
1284
|
+
"/api": "http://backend:3000"
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
```
|
|
1288
|
+
|
|
1289
|
+
Docker `HEALTHCHECK`:
|
|
1290
|
+
|
|
1291
|
+
```dockerfile
|
|
1292
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
|
|
1293
|
+
CMD curl -f http://localhost:8080/health || exit 1
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
Kubernetes liveness/readiness probe:
|
|
1297
|
+
|
|
1298
|
+
```yaml
|
|
1299
|
+
livenessProbe:
|
|
1300
|
+
httpGet:
|
|
1301
|
+
path: /health
|
|
1302
|
+
port: 8080
|
|
1303
|
+
initialDelaySeconds: 10
|
|
1304
|
+
periodSeconds: 30
|
|
1305
|
+
```
|
|
1306
|
+
|
|
1307
|
+
---
|
|
1308
|
+
|
|
533
1309
|
### CORS headers + rich error responses
|
|
534
1310
|
|
|
535
1311
|
```json
|
|
@@ -807,6 +1583,15 @@ Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) before
|
|
|
807
1583
|
- [Express](https://expressjs.com/) — HTTP server framework
|
|
808
1584
|
- [express-http-proxy](https://github.com/villadora/express-http-proxy) — reverse proxy middleware
|
|
809
1585
|
- [Morgan](https://github.com/expressjs/morgan) — HTTP request logger
|
|
1586
|
+
- [compression](https://github.com/expressjs/compression) — gzip/deflate response compression
|
|
1587
|
+
- [helmet](https://helmetjs.github.io/) — security HTTP headers
|
|
1588
|
+
- [cors](https://github.com/expressjs/cors) — CORS headers and preflight handling
|
|
1589
|
+
- [serve-favicon](https://github.com/expressjs/serve-favicon) — efficient favicon serving
|
|
1590
|
+
- [response-time](https://github.com/expressjs/response-time) — X-Response-Time header
|
|
1591
|
+
- [express-rate-limit](https://express-rate-limit.mintlify.app/) — request rate limiting
|
|
1592
|
+
- [express-basic-auth](https://github.com/LionC/express-basic-auth) — HTTP Basic Authentication
|
|
1593
|
+
- CGI support — built on Node.js `child_process.spawn` (no external dependency)
|
|
1594
|
+
- [multer](https://github.com/expressjs/multer) — multipart/form-data file upload handling
|
|
810
1595
|
- [PM2](https://pm2.keymetrics.io/) — production process manager with clustering
|
|
811
1596
|
- [Biome](https://biomejs.dev/) — fast linter and formatter (Rust-based)
|
|
812
1597
|
- [Cypress](https://www.cypress.io/) — E2E testing framework
|