Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nginx Web Server with HTTPS, SSL & Reverse Proxy

A production-like Nginx setup on Linux featuring:

  • Static website hosted from /var/www/secure-app
  • HTTPS using a self-signed SSL certificate (OpenSSL, 365 days)
  • HTTP → HTTPS automatic redirect
  • Reverse proxy to a Node.js backend on port 3000

Part 1 — Basic Setup

1. Install Nginx and OpenSSL

sudo apt update
sudo apt install -y nginx openssl

Install nginx and openssl

2. Create the web root and deploy the static page

sudo mkdir -p /var/www/secure-app
sudo vi /var/www/secure-app/index.html
sudo chown -R www-data:www-data /var/www/secure-app
sudo chmod -R 755 /var/www/secure-app

Web root and static page

The page shows “Secure Server Running via Nginx” once Nginx is serving it.

Static page served on port 80


Part 2 — Self-Signed SSL (OpenSSL, 365 days)

1. Create the SSL directory

sudo mkdir -p /etc/nginx/ssl

SSL directory created under /etc/nginx

2. Generate a self-signed certificate valid for 365 days

sudo openssl req -x509 -nodes -days 365 \
  -newkey rsa:2048 \
  -keyout /etc/nginx/ssl/selfsigned.key \
  -out    /etc/nginx/ssl/selfsigned.crt \
  -subj   "/C=IN/ST=State/L=City/O=SecureApp/OU=Dev/CN=localhost"

Self-signed certificate generated

3. Lock down the private key

sudo chmod 600 /etc/nginx/ssl/selfsigned.key

Private key locked to 0600

Files produced:

  • /etc/nginx/ssl/selfsigned.crt
  • /etc/nginx/ssl/selfsigned.key

Part 3 — Nginx Configuration

1. Deploy the site config

sudo vi /etc/nginx/sites-available/secure-app
sudo ln -sf /etc/nginx/sites-available/secure-app /etc/nginx/sites-enabled/secure-app
sudo rm -f /etc/nginx/sites-enabled/default

2. Contents of nginx/secure-app

# /etc/nginx/sites-available/secure-app
# Nginx config: HTTPS with self-signed SSL + reverse proxy to backend on :3000

# ---------- HTTP: redirect everything to HTTPS ----------
server {
    listen 80;
    listen [::]:80;
    server_name _;

    return 301 https://$host$request_uri;
}

# ---------- HTTPS: serve static site + reverse proxy ----------
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name _;

    ssl_certificate     /etc/nginx/ssl/selfsigned.crt;
    ssl_certificate_key /etc/nginx/ssl/selfsigned.key;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    root  /var/www/secure-app;
    index index.html;

    # Static site
    location / {
        try_files $uri $uri/ =404;
    }

    # Reverse proxy to backend on port 3000
    location /api/ {
        proxy_pass         http://127.0.0.1:3000/;
        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;
    }
}

Deployed secure-app.conf

Key points:

  • Port 80301 redirect to https://$host$request_uri
  • Port 443 → SSL using ssl_certificate + ssl_certificate_key
  • root /var/www/secure-app and index index.html
  • /api/ is reverse-proxied to http://127.0.0.1:3000/

Part 4 — Reverse Proxy to Backend (port 3000)

1. Install Node.js (if not already present)

sudo apt install -y nodejs

Install Node.js

2. Deploy the backend

sudo mkdir -p /var/www/backend
sudo nano /var/www/backend/server.js
sudo chown -R ubuntu:ubuntu /var/www/backend

Backend directory created and owned

3. backend/server.js

const http = require("http");

const PORT = 3000;

const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(
    JSON.stringify(
      {
        message: "Hello from backend!",
        path: req.url,
        host: req.headers["host"],
        realIp: req.headers["x-real-ip"] || null,
        forwardedFor: req.headers["x-forwarded-for"] || null,
        timestamp: new Date().toISOString(),
      },
      null,
      2,
    ),
  );
});

server.listen(PORT, "127.0.0.1", () => {
  console.log(`Backend listening on http://127.0.0.1:${PORT}`);
});

The backend echoes back the Host and X-Real-IP headers so you can verify Nginx is correctly passing them through.

4. Run the backend as a systemd service

Deploy the unit file and enable it:

sudo vi /etc/systemd/system/backend.service
sudo systemctl daemon-reload
sudo systemctl enable --now backend
sudo systemctl status backend --no-pager

Unit contents (backend/backend.service):

[Unit]
Description=Secure App Backend (Node.js on port 3000)
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/var/www/backend
ExecStart=/usr/bin/node /var/www/backend/server.js
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Deployed backend.service unit

After systemctl enable --now backend, the service is active and Node.js is listening on 127.0.0.1:3000:

backend.service active and listening on :3000

5. Nginx proxy settings (already in secure-app.conf)

location /api/ {
    proxy_pass         http://127.0.0.1:3000/;
    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;
}

Part 5 — Testing

1. Validate the Nginx configuration and reload

sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status nginx --no-pager

Expected:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

nginx -t, reload, and active status

2. Verify HTTP → HTTPS redirect

curl -I http://localhost

Expected (truncated):

HTTP/1.1 301 Moved Permanently
Location: https://localhost/

HTTP 301 → HTTPS redirect

3. Verify HTTPS is serving the static page

In a browser, open https://<server-ip>/. Because the certificate is self-signed the browser shows Not Secure — that is expected — but the connection is HTTPS and the page renders:

HTTPS serving the static page in the browser

4. Verify backend is reachable through Nginx

curl -k https://localhost/api/

Expected (example):

{
  "message": "Hello from backend!",
  "path": "/",
  "host": "localhost",
  "realIp": "::1",
  "forwardedFor": "::1",
  "timestamp": "2026-04-21T17:39:10.708Z"
}

The non-null realIp field confirms Nginx is passing X-Real-IP through to the backend.

All three proofs: 301 redirect, HTTPS 200, and /api/ JSON with forwarded headers

One-Shot Deploy (summary)

# Packages
sudo apt update && sudo apt install -y nginx openssl nodejs

# Web root + static page
sudo mkdir -p /var/www/secure-app
sudo nano /var/www/secure-app/index.html

# SSL (365 days)
sudo mkdir -p /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout /etc/nginx/ssl/selfsigned.key \
  -out    /etc/nginx/ssl/selfsigned.crt \
  -subj   "/C=IN/ST=State/L=City/O=SecureApp/OU=Dev/CN=localhost"
sudo chmod 600 /etc/nginx/ssl/selfsigned.key

# Nginx site
sudo vi /etc/nginx/sites-available/secure-app
sudo ln -sf /etc/nginx/sites-available/secure-app /etc/nginx/sites-enabled/secure-app
sudo rm -f /etc/nginx/sites-enabled/default

# Backend
sudo mkdir -p /var/www/backend
sudo vi /var/www/backend/server.js
sudo vi /etc/systemd/system/backend.service
sudo systemctl daemon-reload
sudo systemctl enable --now backend

# Test + reload
sudo nginx -t
sudo systemctl reload nginx

Latest screenshot of the site Live static secure site

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages