Exercises
Objectives
By the end of this lesson, you should be able to:
- Build a reverse proxy that both load balances and forwards real client information
- Write an Nginx configuration combining routing, load balancing, and HTTPS
- Explain the complete path a request takes from a client to an application, through both layers
⚠️ A note on verification: the Node.js portion of this lesson was actually run. The Nginx and Let’s Encrypt portion reflects stable, current, documented behavior, this course’s sandboxed tooling has no access to install Nginx or obtain a real certificate, follow those steps yourself, against a real domain.
Exercise: A Load-Balancing Reverse Proxy in Node.js
a) Two backend instances, identical code, different ports:
// lb-backend1.js
const express = require('express');
const app = express();
app.get('/api/v1/health', (req, res) => res.json({ instance: 1, forwardedFor: req.headers['x-forwarded-for'] }));
app.listen(7101, () => console.log('Instance 1 on 7101'));
(lb-backend2.js identical, instance: 2, port 7102.)
b) A reverse proxy that both load balances and forwards headers:
const http = require('http');
const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer({});
const targets = ['http://localhost:7101', 'http://localhost:7102'];
let i = 0;
const server = http.createServer((req, res) => {
const target = targets[i % targets.length];
i++;
proxy.web(req, res, { target, xfwd: true });
});
server.listen(7100, () => console.log('Load-balancing reverse proxy on 7100'));
c) Confirm both properties at once:
Request 1 -> { instance: 1, forwardedFor: '::ffff:127.0.0.1' }
Request 2 -> { instance: 2, forwardedFor: '::ffff:127.0.0.1' }
Request 3 -> { instance: 1, forwardedFor: '::ffff:127.0.0.1' }
Request 4 -> { instance: 2, forwardedFor: '::ffff:127.0.0.1' }
Requests alternate between both instances, and every single one carries the real client’s address, forwarded correctly, exactly what a production reverse proxy needs to do simultaneously, distribute load, and preserve information the backend genuinely needs.
d) Write the equivalent Nginx configuration, combining Lesson 2’s upstream block with Lesson 3’s HTTPS termination:
upstream notes_api {
server localhost:7101;
server localhost:7102;
}
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location / {
proxy_pass http://notes_api;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
server {
listen 80;
server_name api.example.com;
return 301 https://$host$request_uri;
}
e) Trace the complete path. Write out, in your own words, every step a request takes from a browser sending https://api.example.com/api/v1/health, to one of the two backend instances actually handling it, naming where encryption is terminated, where load balancing happens, and what headers the backend ultimately sees.
f) Reflect. Explain, in one or two sentences, why the Node.js exercise here (b and c) is useful for understanding the mechanism, even though a real deployment would very likely use Nginx (d) instead of a hand-rolled proxy in production.
Recap
This module built the layer that sits between every real client and the application this track has been building: a reverse proxy forwarding requests and adding trustworthy client information, Nginx as the production-grade standard most real deployments actually run, load balancing across multiple instances, and HTTPS termination protecting every credential and token in transit, exactly what Course 3’s authentication depends on being genuinely safe to use over a real network.
Next module: caching, reducing load on a database for data that doesn’t need to be fetched fresh on every single request.