Skip to content

How to Set Up Odoo Behind Nginx Reverse Proxy

DeployMonkey Team · March 11, 2026 9 min read

To run Odoo behind Nginx you need two location blocks — one for the main application and one for the longpolling websocket on port 8072 — plus correct proxy headers including X-Forwarded-Proto and an increased proxy buffer size. Skipping either the websocket block or the right headers causes live chat and real-time notifications to break silently.

Why Odoo Needs a Reverse Proxy

Odoo's built-in HTTP server is not designed to be exposed directly to the internet. It lacks SSL termination, has no rate limiting, and cannot efficiently serve static files. Nginx sits in front of Odoo and handles:

  • SSL/TLS termination — Odoo sees only plain HTTP internally
  • Static file caching — Nginx serves /web/static/ files directly without hitting a Python worker
  • Gzip compression — reduces bandwidth for large JavaScript bundles
  • Websocket proxying — required for Odoo's Discuss and live chat modules
  • Request buffering — protects Odoo workers from slow clients

In multi-processing mode (workers > 0) Odoo also starts a gevent-based longpolling server on port 8072. This port must be proxied separately — it handles long-lived HTTP connections that would block regular workers if they hit port 8069.

Prerequisites

  • Odoo running on 127.0.0.1:8069 with workers > 0
  • Nginx installed: sudo apt install nginx
  • Certbot for Let's Encrypt: sudo apt install certbot python3-certbot-nginx
  • A domain pointed at your server's IP

Complete Nginx Configuration

# /etc/nginx/sites-available/odoo

upstream odoo {
server 127.0.0.1:8069;
}

upstream odoo-longpolling {
server 127.0.0.1:8072;
}

# Redirect HTTP → HTTPS
server {
listen 80;
server_name erp.example.com;
return 301 https://$host$request_uri;
}

server {
listen 443 ssl http2;
server_name erp.example.com;

# --- SSL (managed by certbot) ---
ssl_certificate     /etc/letsencrypt/live/erp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/erp.example.com/privkey.pem;
include             /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;

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

# --- Proxy timeouts ---
proxy_read_timeout    720s;
proxy_connect_timeout 720s;
proxy_send_timeout    720s;

# --- Proxy headers ---
proxy_set_header X-Forwarded-Host   $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;

# --- Gzip ---
gzip            on;
gzip_min_length 1100;
gzip_buffers    4 32k;
gzip_types      text/css text/less text/plain text/xml
                application/xml application/json application/javascript;
gzip_vary       on;

# --- Serve static files directly ---
location ~* /web/static/ {
    proxy_cache_valid 200 90d;
    proxy_buffering   on;
    expires           864000;
    proxy_pass        http://odoo;
}

# --- Longpolling / websocket ---
location /web/websocket {
    proxy_pass         http://odoo-longpolling;
    proxy_http_version 1.1;
    proxy_set_header   Upgrade    $http_upgrade;
    proxy_set_header   Connection "Upgrade";
    proxy_set_header   Host       $host;
    proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_read_timeout 86400s;
}

# Odoo 15 and older use /longpolling/poll instead of websocket
location /longpolling {
    proxy_pass http://odoo-longpolling;
}

# --- Main Odoo application ---
location / {
    proxy_redirect   off;
    proxy_pass       http://odoo;
    proxy_buffer_size          128k;
    proxy_buffers              4 256k;
    proxy_busy_buffers_size    256k;
}

# --- Security headers ---
add_header X-Content-Type-Options  nosniff;
add_header X-Frame-Options         SAMEORIGIN;
add_header X-XSS-Protection        "1; mode=block";
}

Enabling the Config

sudo ln -s /etc/nginx/sites-available/odoo /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Obtaining an SSL Certificate with Let's Encrypt

# Obtain certificate (Nginx plugin handles config automatically)
sudo certbot --nginx -d erp.example.com

# Test automatic renewal
sudo certbot renew --dry-run

# Verify the renewal timer is active
sudo systemctl status certbot.timer

Required Odoo Settings When Behind a Proxy

Tell Odoo it is behind a proxy so it generates correct HTTPS URLs:

# /etc/odoo/odoo.conf
proxy_mode = True

Without proxy_mode = True, Odoo will generate http:// links in emails and portal pages even when users are accessing it over HTTPS, because it reads the scheme from the raw socket connection (which is always plain HTTP from Nginx).

Common Mistakes and How to Fix Them

MistakeSymptomFix
Missing /web/websocket blockLive chat and Discuss do not update in real time; spinner in chatAdd the websocket location block pointing to port 8072
Missing proxy_redirect offRedirects send users to http://127.0.0.1:8069/Always set proxy_redirect off in the main location block
proxy_mode = True not setOdoo emails contain http:// links; CSRF errors on portalAdd proxy_mode = True to odoo.conf and restart Odoo
Missing X-Forwarded-Proto headerOdoo shows "Your connection is not secure" banner internallyAdd the header in the Nginx proxy config
Small proxy_buffer_size502 errors on pages with large cookie/session headersSet proxy_buffer_size 128k and proxy_buffers 4 256k

How DeployMonkey Handles Nginx Automatically

Every instance deployed through DeployMonkey gets a fully configured Nginx reverse proxy generated automatically — including the correct websocket block, gzip settings, static file caching, and all required proxy headers. You never write an Nginx config file manually.

SSL certificates are provisioned via Let's Encrypt on first deploy and renewed automatically before expiry. The platform monitors certificate expiry across all instances and sends a warning if renewal fails for any reason — a common source of unexpected downtime on self-managed servers.

When you update your instance's domain in the DeployMonkey dashboard, the platform regenerates the Nginx config and obtains a new certificate for the new domain in a single atomic operation. For custom domains on the Agency plan, wildcard certificates are also supported.

Related Articles

Frequently Asked Questions

Why does Odoo live chat not work after setting up Nginx?

The most common cause is a missing websocket proxy block. Odoo 16+ uses /web/websocket; Odoo 15 and older use /longpolling/poll. Make sure the matching location block is present in your Nginx config and points to port 8072.

Do I need to open port 8072 in the firewall?

No. Port 8072 should only be accessible from Nginx on the same server (127.0.0.1). Block it in your firewall with ufw deny 8072 — Nginx proxies to it internally. Only ports 80 and 443 should be open to the internet.

What proxy_read_timeout should I set for Odoo?

720 seconds (12 minutes) is a safe default that covers long-running reports. If your users regularly generate reports that take longer, increase it further. The longpolling location should use a much longer timeout (86400 s) because those connections are intentionally long-lived.

How do I test if Nginx is correctly proxying to Odoo?

Run curl -I https://erp.example.com and check that the response has HTTP/2 200 and a Set-Cookie: session_id header from Odoo. Then open the Odoo Discuss app and verify the chat interface updates in real time — that confirms the websocket proxy is also working.

Nginx, SSL, and Proxy Config — Handled Automatically

DeployMonkey configures Nginx and SSL for every Odoo instance on deploy. No config files, no certbot commands, no websocket debugging.

Start Free — No Credit Card Required