Skip to content
Christian Santamaría (Solvos) edited this page Aug 25, 2025 · 20 revisions

Install & Configure Nginx

Here we'll do a basic Nginx configuration to use Port 80.

Install Nginx

sudo -i
apt install nginx

Create Site Config

First, remove the default archive (with Webmin or by command), and create one called odoo-80:

rm /etc/nginx/sites-available/default
nano /etc/nginx/sites-available/odoo-80

Paste the following (you must change server_name with the IP or VM name):

# odoo server
upstream odoo {
    server 127.0.0.1:8169;
}
upstream odoochat {
    server 127.0.0.1:8172;
}
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    server_name $ip_or_vm;
    proxy_read_timeout 720s;
    proxy_connect_timeout 720s;
    proxy_send_timeout 720s;
    client_max_body_size 128M;
    large_client_header_buffers 16 32k;

    access_log /var/log/nginx/odoo.access.log;
    error_log /var/log/nginx/odoo.error.log;

    location /websocket {
        proxy_pass http://odoochat;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
    }

    location / {
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_redirect off;
        proxy_pass http://odoo;

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
    }

    location /.well-known/acme-challenge/ {
	    alias /var/www/acme/;
	    try_files $uri =404;
    }

    gzip_types text/css text/scss text/plain text/xml application/xml application/json application/javascript;
    gzip on;
}

Enable the Site

Again, remove the default archive and create a symbolic link with the one created before:

rm /etc/nginx/sites-enabled/default
ln -s /etc/nginx/sites-available/odoo-80 /etc/nginx/sites-enabled/odoo-80

Set Proxy Mode

Now it's time to end the .env configuration we talked about here:

nano .env
PROXY_MODE=True

Restart Odoo and Nginx

sudo docker-compose down
sudo docker-compose up -d
systemctl restart nginx.service

If Nginx fails, check:

systemctl status nginx.service

Note: If you follow Odoo's configuration system and cookie_flags fails, check first if Nginx version is 1.18 and remove the following line (requires Nginx 1.19+):

proxy_cookie_flags session_id samesite=lax secure;  # requires nginx 1.19.8

Allow Port 80 (UFW)

After all configuration, it's time to check if it works on port 80 by putting the server name on your browser and see if Odoo is active. If it doesn’t, check ufw and allow 80/tcp:

sudo ufw status
sudo ufw allow 80/tcp

Let’s Encrypt

For this to work, we need to ensure the destination route exists and then we can restart nginx:

mkdir /var/www/acme
systemctl restart nginx

Next, the structure that will host the certificates and other items is created:

mkdir /etc/nginx/ssl/
chmod 700 /etc/nginx/ssl/
cd /etc/nginx/ssl/

At this point, you can create the different structures:

Certificate for the account we will "open" in Let's Encrypt

openssl genrsa 4096 > acct.key

Domain private key

openssl genrsa 4096 > $domain.key

Fault the domain certificate request (the following line would fail), only if necessary

dd if=/dev/urandom of=/root/.rnd bs=256 count=1

Request for the domain certificate domain

openssl req -new -sha256 -key $domain.es.key -subj "/CN=$domain.es" > $domain.es.csr

You now have the private key and the certificate request, among other things. From there, we download the script that allows us to create and recreate the final certificate:

Download the signing tool

wget -O - "https://raw.githubusercontent.com/diafygi/acme-tiny/master/acme_tiny.py" > acme_tiny.py

Generate the signed certificate against Let's Encrypt

python3 acme_tiny.py --account-key ./acct.key --csr ./$domain.es.csr --acme-dir /var/www/acme/ > ./signed.crt

Create the Nginx-compatible certificate (pem)

wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem
cat signed.crt intermediate.pem > $domain.es.pem
#odoo server
upstream odoo {
  server 127.0.0.1:8169;
}
upstream odoochat {
  server 127.0.0.1:8172;
}
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

# http -> https
server {
  listen 80;
  server_name $domain.es;
  rewrite ^(.*) https://$host$1 permanent;
}

server {
  listen 443 ssl;
  server_name $domain;
  proxy_read_timeout 720s;
  proxy_connect_timeout 720s;
  proxy_send_timeout 720s;

  # SSL parameters
  ssl_certificate /etc/nginx/ssl/$domain.pem;
  ssl_certificate_key /etc/nginx/ssl/$domain.key;
  ssl_session_timeout 30m;
  ssl_protocols TLSv1.2;
  ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
  ssl_prefer_server_ciphers off;

  # log
  access_log /var/log/nginx/odoo.access.log;
  error_log /var/log/nginx/odoo.error.log;

  # Redirect websocket requests to odoo gevent port
  location /websocket {
    proxy_pass http://odoochat;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
  }

  # Redirect requests to odoo backend server
  location / {
    # Add Headers for odoo proxy mode
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_redirect off;
    proxy_pass http://odoo;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
  }

  # common gzip
  gzip_types text/css text/scss text/plain text/xml application/xml application/json application/javascript;
  gzip on;
}

Clone this wiki locally