@lopatnov/express-reverse-proxy 3.0.0 → 4.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.
Files changed (3) hide show
  1. package/README.md +144 -0
  2. package/package.json +9 -3
  3. package/server.js +29 -0
package/README.md CHANGED
@@ -20,11 +20,19 @@
20
20
  - [CLI Options](#cli-options)
21
21
  - [Configuration](#configuration)
22
22
  - [port](#port)
23
+ - [logging](#logging)
24
+ - [hotReload](#hotreload)
25
+ - [compression](#compression)
26
+ - [helmet](#helmet)
27
+ - [cors](#cors)
28
+ - [favicon](#favicon)
29
+ - [responseTime](#responsetime)
23
30
  - [headers](#headers)
24
31
  - [folders](#folders)
25
32
  - [proxy](#proxy)
26
33
  - [unhandled](#unhandled)
27
34
  - [host](#host)
35
+ - [ssl](#ssl)
28
36
  - [Configuration Recipes](#configuration-recipes)
29
37
  - [Docker & PM2](#docker--pm2)
30
38
  - [Testing](#testing)
@@ -154,6 +162,15 @@ Static files always take priority over proxy rules. Proxies are checked only whe
154
162
 
155
163
  ## CLI Options
156
164
 
165
+ The package installs two equivalent commands — use whichever you prefer:
166
+
167
+ ```shell
168
+ express-reverse-proxy [options]
169
+ lerp [options]
170
+ ```
171
+
172
+ `lerp` is a short alias for **L**opatnov **E**xpress **R**everse **P**roxy.
173
+
157
174
  | Option | Description |
158
175
  | ------------------------- | ----------------------------------------------------------------------------------------------- |
159
176
  | `--help` | Print help and exit |
@@ -462,6 +479,108 @@ Paths are resolved **relative to the config file**, not the current working dire
462
479
 
463
480
  > All site configs on the same port must either all have `ssl` or none — mixing is a startup error.
464
481
 
482
+ ### compression
483
+
484
+ 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.
485
+
486
+ ```json
487
+ {
488
+ "port": 8080,
489
+ "compression": true,
490
+ "folders": "www"
491
+ }
492
+ ```
493
+
494
+ With custom options (see [compression docs](https://github.com/expressjs/compression#options)):
495
+
496
+ ```json
497
+ {
498
+ "compression": { "level": 6, "threshold": 1024 }
499
+ }
500
+ ```
501
+
502
+ > 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.
503
+
504
+ ### helmet
505
+
506
+ 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.
507
+
508
+ ```json
509
+ {
510
+ "port": 8080,
511
+ "helmet": true,
512
+ "folders": "www"
513
+ }
514
+ ```
515
+
516
+ Disable a specific header (see [helmet docs](https://helmetjs.github.io/) for all options):
517
+
518
+ ```json
519
+ {
520
+ "helmet": { "contentSecurityPolicy": false }
521
+ }
522
+ ```
523
+
524
+ > 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.
525
+
526
+ ### cors
527
+
528
+ 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.
529
+
530
+ ```json
531
+ {
532
+ "port": 8080,
533
+ "cors": true,
534
+ "proxy": { "/api": "http://localhost:4000" }
535
+ }
536
+ ```
537
+
538
+ Restrict to a specific origin (see [cors docs](https://github.com/expressjs/cors#configuration-options)):
539
+
540
+ ```json
541
+ {
542
+ "cors": { "origin": "https://app.example.com" }
543
+ }
544
+ ```
545
+
546
+ > 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.
547
+
548
+ ### favicon
549
+
550
+ 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.
551
+
552
+ ```json
553
+ {
554
+ "port": 8080,
555
+ "favicon": "./public/favicon.ico",
556
+ "folders": "www"
557
+ }
558
+ ```
559
+
560
+ The path is resolved **relative to the config file**, consistent with the `ssl` option. Absolute paths are also accepted.
561
+
562
+ > If your favicon already lives inside a directory listed in `folders`, this option is not needed — `express.static` will serve it automatically.
563
+
564
+ ### responseTime
565
+
566
+ 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.
567
+
568
+ ```json
569
+ {
570
+ "port": 8080,
571
+ "responseTime": true,
572
+ "folders": "www"
573
+ }
574
+ ```
575
+
576
+ With custom precision (see [response-time docs](https://github.com/expressjs/response-time#options)):
577
+
578
+ ```json
579
+ {
580
+ "responseTime": { "digits": 0, "suffix": false }
581
+ }
582
+ ```
583
+
465
584
  ---
466
585
 
467
586
  ## Configuration Recipes
@@ -530,6 +649,26 @@ node server.js --config server-config.json
530
649
 
531
650
  ---
532
651
 
652
+ ### Production hardening (helmet + cors + compression)
653
+
654
+ Enable security headers, CORS, and response compression in one config:
655
+
656
+ ```json
657
+ {
658
+ "port": 8080,
659
+ "compression": true,
660
+ "helmet": true,
661
+ "cors": { "origin": "https://app.example.com" },
662
+ "responseTime": true,
663
+ "folders": "www",
664
+ "proxy": {
665
+ "/api": "http://localhost:4000"
666
+ }
667
+ }
668
+ ```
669
+
670
+ ---
671
+
533
672
  ### CORS headers + rich error responses
534
673
 
535
674
  ```json
@@ -807,6 +946,11 @@ Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) before
807
946
  - [Express](https://expressjs.com/) — HTTP server framework
808
947
  - [express-http-proxy](https://github.com/villadora/express-http-proxy) — reverse proxy middleware
809
948
  - [Morgan](https://github.com/expressjs/morgan) — HTTP request logger
949
+ - [compression](https://github.com/expressjs/compression) — gzip/deflate response compression
950
+ - [helmet](https://helmetjs.github.io/) — security HTTP headers
951
+ - [cors](https://github.com/expressjs/cors) — CORS headers and preflight handling
952
+ - [serve-favicon](https://github.com/expressjs/serve-favicon) — efficient favicon serving
953
+ - [response-time](https://github.com/expressjs/response-time) — X-Response-Time header
810
954
  - [PM2](https://pm2.keymetrics.io/) — production process manager with clustering
811
955
  - [Biome](https://biomejs.dev/) — fast linter and formatter (Rust-based)
812
956
  - [Cypress](https://www.cypress.io/) — E2E testing framework
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lopatnov/express-reverse-proxy",
3
3
  "email": "oleksandr@lopatnov.cv.ua",
4
- "version": "3.0.0",
4
+ "version": "4.0.0",
5
5
  "description": "Node.js CLI tool to serve static front-end files with a reverse proxy for back-end APIs",
6
6
  "type": "module",
7
7
  "author": "lopatnov",
@@ -11,7 +11,8 @@
11
11
  "./hot-reload-client": "./hot-reload-client.js"
12
12
  },
13
13
  "bin": {
14
- "express-reverse-proxy": "./server.js"
14
+ "express-reverse-proxy": "./server.js",
15
+ "lerp": "./server.js"
15
16
  },
16
17
  "engines": {
17
18
  "node": ">=18.0.0"
@@ -67,10 +68,15 @@
67
68
  "access": "public"
68
69
  },
69
70
  "dependencies": {
71
+ "compression": "^1.7.5",
72
+ "cors": "^2.8.5",
70
73
  "express": "^5.2.1",
71
74
  "express-http-proxy": "^2.1.2",
75
+ "helmet": "^8.0.0",
72
76
  "morgan": "^1.10.1",
73
- "pm2": "^6.0.14"
77
+ "pm2": "^6.0.14",
78
+ "response-time": "^2.3.2",
79
+ "serve-favicon": "^2.5.0"
74
80
  },
75
81
  "devDependencies": {
76
82
  "@biomejs/biome": "^2.4.2",
package/server.js CHANGED
@@ -4,9 +4,14 @@ import fs from 'node:fs';
4
4
  import https from 'node:https';
5
5
  import path from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
+ import compression from 'compression';
8
+ import cors from 'cors';
7
9
  import express from 'express';
8
10
  import proxy from 'express-http-proxy';
11
+ import helmet from 'helmet';
9
12
  import morgan from 'morgan';
13
+ import responseTime from 'response-time';
14
+ import favicon from 'serve-favicon';
10
15
 
11
16
  const __filename = fileURLToPath(import.meta.url);
12
17
  const __dirname = path.dirname(__filename);
@@ -361,6 +366,30 @@ configsByPort.forEach((portConfigs, p) => {
361
366
 
362
367
  console.log(`[host] ${siteHost} → :${p}`);
363
368
 
369
+ if (siteConfig.responseTime) {
370
+ const opts = typeof siteConfig.responseTime === 'object' ? siteConfig.responseTime : {};
371
+ router.use(responseTime(opts));
372
+ }
373
+
374
+ if (siteConfig.cors) {
375
+ const opts = typeof siteConfig.cors === 'object' ? siteConfig.cors : {};
376
+ router.use(cors(opts));
377
+ }
378
+
379
+ if (siteConfig.compression) {
380
+ const opts = typeof siteConfig.compression === 'object' ? siteConfig.compression : {};
381
+ router.use(compression(opts));
382
+ }
383
+
384
+ if (siteConfig.helmet) {
385
+ const opts = typeof siteConfig.helmet === 'object' ? siteConfig.helmet : {};
386
+ router.use(helmet(opts));
387
+ }
388
+
389
+ if (siteConfig.favicon) {
390
+ router.use(favicon(path.resolve(configDir, siteConfig.favicon)));
391
+ }
392
+
364
393
  if (siteConfig.headers) {
365
394
  router.use((_req, res, next) => {
366
395
  for (const h of Object.keys(siteConfig.headers)) res.setHeader(h, siteConfig.headers[h]);