Skip to main content

Nginx

816 words·4 mins·

What
#

Nginx is an HTTP web server, reverse proxy, content cache, load balancer, TCP/UDP proxy server, and mail proxy server.

Why
#

  1. Serving static content
  2. Reverse proxy: Sends incoming requests to the appropriate backend server
  3. SSL/TLS termination: relieves backend server of dealing with encryption and decoding
  4. Load balancing
  5. Caching

How
#

Folder layout:

nginx
├── conf
│   ├── certs
│   │   ├── create_certs.sh
│   │   ├── dhparam.pem
│   │   ├── nginx-selfsigned.crt
│   │   ├── nginx-selfsigned.key
│   │   ├── self-signed.conf
│   │   └── ssl-params.conf
│   ├── conf.d
│   │   ├── api-proxy.conf
│   │   └── default.conf
│   ├── streams
│   │   └── reverse-ssh.conf
│   └── nginx.conf
├── test-app
|	├── www
|	│   ├── test
|	│   │   └── test.html
|	│   ├── app.js
|	│   └── index.html
|	├── app
|	│   ├── index.js
|	│   ├── package.json
|	│   └── package-lock.json
└── docker-compose-nginx.yaml 
  • docker-compose-nginx.yaml: docker compose file
  • conf/nginx.conf: general configuration
  • conf/certs/: certificates folder
  • conf/conf.d/: server configurations
  • conf/streams/: streams configurations
  • test-app/: application example

docker-compose-nginx.yaml
#

Base image: nginx

services:
  nginx:
  image: nginx:1.27.3-alpine
  container_name: nginx
  ports:
    - 80:80
    - 443:443
    - 9122:9122

  volumes:
    - ./conf/nginx.conf:/etc/nginx/nginx.conf
    - ./conf/conf.d:/etc/nginx/conf.d
    - ./conf/streams:/etc/nginx/streams
    - ./conf/certs:/etc/nginx/certs
    - ./test-app/www:/etc/nginx/www

  extra_hosts:
    - "host.docker.internal:host-gateway"

  restart: always

extra-hosts allows the container to access localhost as host.docker.internal

./conf/nginx.conf
#

General nginx configuration file:

user nginx;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;

events {
  worker_connections 1024;
}

stream {
  include /etc/nginx/streams/*.conf; # streams configuration here
}

http {
  include /etc/nginx/mime.types;
  default_type application/octet-stream;

  log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';
  access_log /var/log/nginx/access.log main;

  sendfile on;
  
  #tcp_nopush on;
  keepalive_timeout 65;
  #gzip on;
  include /etc/nginx/conf.d/*.conf; # servers configuration here
}

Specific include folders for streams and http configurations

./conf/certs
#

SSL/TLS configuration and setup

Generate (self-signed) certificates
#

to generate certificates with certbot see this certbot

Create key and certificate and store them in ./conf/certs folder

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./conf/certs/nginx-selfsigned.key -out ./conf/certs/nginx-selfsigned.crt

This command will generate the following files:

./conf/certs/nginx-selfsigned.key
./conf/certs/nginx-selfsigned.crt

Generate DH params to increase SSL/TLS security on the server:

openssl dhparam -out ./conf/certs/dhparam.pem 2048

Generate self-signed certificates
#

Create key and certificate and store them in ./conf/certs folder

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./conf/certs/nginx-selfsigned.key -out ./conf/certs/nginx-selfsigned.crt

Generate DH params to increase SSL/TLS security on the server:

openssl dhparam -out ./conf/certs/dhparam.pem 2048

Nginx configuration files
#

  • Certificates configuration: `./conf/self-signed.conf
ssl_certificate /etc/nginx/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/nginx/certs/nginx-selfsigned.key;
  • Configuration for SSL options in nginx: ./conf/ssl-params.conf
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# Disable preloading HSTS for now. You can use the commented out header line that includes

# the "preload" directive if you understand the implications.

#add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

ssl_dhparam /etc/nginx/certs/dhparam.pem;

./conf/conf.d
#

This folder hold specific servers configuration files.

Services diagram

Automatic redirection to SSL port: ./conf/conf.d/default.conf

server {
	listen 80;
	server_name localhost;
	return 301 https://$host$request_uri;
}

http://nginx-server -> https:/nginx-server

Example file: ./conf/conf.d/api-proxy.conf

server {
	listen 443 ssl;
	listen [::]:443 ssl;

	server_name localhost; #your server name here

	include /etc/nginx/certs/self-signed.conf;
	include /etc/nginx/certs/ssl-params.conf;

	location / {
		proxy_pass http://host.docker.internal:1313;
		
		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;
	}

	location /app {
		alias /etc/nginx/www; # your public folder
		index index.html index.htm;
		try_files $uri $uri/ /index.html;
	}

	location ^~ /api {
		rewrite /api/(.*) /$1 break;
		proxy_pass http://host.docker.internal:5000;
		
		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;
	}
}

location /: https://nginx-server -> http://localhost:1313 location /app: https://nginx-server/app -> file://test-app/www location ^~ /api: https://nginx-server/api -> http://locahost:5000

./conf/streams
#

This folder hold specific streams configuration files.

Example file: ./conf/streams/reverse-ssh.conf

listen 9122;

proxy_pass host.docker.internal:22;

./test-app
#

Quick setup for test application.

test-app
	├── www
	│   ├── test
	│   │   └── test.html
	│   ├── app.js
	│   └── index.html
    ├── app
	│   ├── index.js
	│   ├── package.json
	│   └── package-lock.json
  • ./www/: Frontend static resources
  • ./app/: REST API Server

Rest Api

./app/index.js

const express = require("express"); //require express
const app = express(); //create the app

app.get("/v1/products", (req, res) => {
	let products = [
	  {
	    name: "prod1",
	    price: 10
	  },
	  {
	    name: "prod2",
	    price: 20
	  },
	  {
	    name: "prod3",
	    price: 30
	  },
	]
	
	 res.send(JSON.stringify(products)); //responding to the request
});// send a response to the client

app.listen(5000, () => {
    console.log("App has started and running ");
}); // listen for connection to the app

Run server:

npm install
node index.js

Frontend

www/index.html

<html>
  <body>
    <h1 class="description">Main App UI</h1>
	<div id="template"></div>
    <div id="container">
   	  <button id="fetch">Invoke API</button>
    </div>
    <script src="app.js"></script>
  </body>
</html>

www/app.js

const myButton = document.getElementById('fetch');
myButton.addEventListener('click', fetchInfo);
function fetchInfo () {
  fetch('https://localhost/api/v1/products')
    .then(response => response.json())
    .then(jsonObj => displayUi(jsonObj))
    .catch(() => alert('API Could not be reached at this time'))
}

function displayUi (products) {
  let template = ""
  for (const p of products){
    template += `<div><b> ${p.name}</b>: ${p.price}</div>`
  }

  document.getElementById('template').innerHTML = template
}

Run frontend through nginx:

https://nginx-server/app

Run fronted from express.js:

http://localhost:5000