Phần trước Odoo đã chạy đa tiến trình, nhưng nó vẫn nghe trên cổng nội bộ 8069, không TLS, không nén. Đưa thẳng cổng này ra Internet là sai lầm: không HTTPS, không nén, và Odoo phải tự phục vụ mọi file tĩnh. Chuẩn production là đặt nginx làm reverse proxy phía trước — nginx lo TLS, nén, định tuyến, còn Odoo chỉ lo nghiệp vụ. Bài này dựng cấu hình đó và đo bằng header thật để thấy nginx thêm gì.

Trước và sau nginx — nhìn header thật

Cách rõ nhất để hiểu vai trò của nginx là so header của cùng một loại request: một lần gõ thẳng vào Odoo, một lần qua nginx.

Ảnh chụp terminal nền tối hai phần so header thật. Phần 1 Odoo trực tiếp upstream cổng 8069 chưa qua nginx. Lệnh curl sI http 127.0.0.1 8069 web login. Kết quả HTTP 1.1 303 SEE OTHER, Location web database selector, X-Content-Type-Options nosniff chú thích header do chính Odoo gửi. Chú thích không TLS không nén không Server nginx đây là backend trần. Phần 2 qua nginx blog thật coffeecode.vn nginx thêm gì. Lệnh curl sI H Accept-Encoding gzip https coffeecode.vn. Kết quả HTTP 1.1 200, Server nginx 1.24.0 Ubuntu chú thích nginx đứng trước, Content-Encoding gzip chú thích nginx nén gzip, Vary Accept-Encoding, Strict-Transport-Security max-age 31536000 includeSubDomains, Content-Security-Policy default-src self, X-Frame-Options SAMEORIGIN. Chú thích TLS và gzip do nginx còn HSTS CSP do chính ứng dụng gửi đi xuyên qua

Hình 1: Thật, đọc bằng curl -sI. Trên: Odoo trần ở cổng 8069 trả 303 với header tối thiểu (X-Content-Type-Options do chính Odoo gửi) — không TLS, không nén, không Server: nginx. Dưới: một trang qua nginx trả 200 kèm Server: nginx/1.24.0, và Content-Encoding: gzip — nginx đã nén. TLS và nén là việc của nginx; còn Strict-Transport-Security/Content-Security-Policy ở đây do chính ứng dụng gửi và đi xuyên qua nginx (nguyên tắc: đặt security header ở một chỗ, tránh trùng).

Cấu hình nginx cho Odoo

Điểm đặc biệt của Odoo: nó có hai cổng — cổng HTTP thường (8069) và cổng gevent/longpolling (8072) cho chat, thông báo real-time. nginx phải định tuyến đúng:

Ảnh chụp cấu hình nginx reverse proxy cho Odoo nền tối. upstream odoo server 127.0.0.1 8069 chú thích worker HTTP. upstream odoochat server 127.0.0.1 8072 chú thích gevent longpolling. Khối server listen 443 ssl, server_name shop.example.com, ssl_certificate fullchain.pem, ssl_certificate_key privkey.pem. client_max_body_size 100M chú thích cho upload tệp lớn. gzip on gzip_types text css application javascript. location websocket proxy_pass http odoochat, proxy_set_header Upgrade http_upgrade, proxy_set_header Connection upgrade, proxy_read_timeout 720s chú thích giữ kết nối lâu. location gạch chéo proxy_pass http odoo, proxy_set_header Host host bắt buộc, 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 chú thích http vs https. Khối server listen 80 return 301 https chú thích 80 sang 443

Hình 2: Cấu hình nginx. Hai upstream: odoo (8069) và odoochat (8072). location /websocket trỏ sang odoochat với Upgrade/Connection "upgrade" (bắt buộc cho WebSocket) và proxy_read_timeout dài (giữ kết nối lâu). location / trỏ sang odoo. Bốn proxy_set_header là bắt buộc. Cuối cùng, khối listen 80 chuyển hướng mọi HTTP sang HTTPS.

Bốn header proxy bắt buộc — và proxy_mode

Đây là phần hay bị bỏ sót và gây bug khó hiểu. Khi nginx chuyển tiếp request, Odoo mất thông tin gốc: mọi khách trông như đến từ 127.0.0.1 (chính nginx), và Odoo tưởng request là http chứ không https. Bốn header sửa điều đó:

  • Host — tên miền khách gõ, để Odoo sinh URL đúng.
  • X-Real-IP / X-Forwarded-For — IP thật của khách, để giới hạn tần suất và log đúng.
  • X-Forwarded-Proto — https hay http, để Odoo biết sinh link https và đặt cookie Secure.

Nhưng gửi header thôi chưa đủ: Odoo chỉ tin chúng khi bật proxy_mode = True trong odoo.conf. Thiếu nó, Odoo bỏ qua các header này để chống giả mạo — và bạn sẽ thấy link sinh ra sai giao thức, cookie không Secure, rate-limit đếm sai. Bật proxy_mode là điều kiện đi kèm bắt buộc của việc chạy sau proxy.

Còn gì nữa nginx lo

  • TLS termination: nginx giữ chứng chỉ (ssl_certificate), giải mã HTTPS, nói HTTP thường với Odoo ở nội bộ. Chứng chỉ Let's Encrypt gia hạn ở tầng nginx.
  • Nén gzip: đã thấy Content-Encoding: gzip thật — nén CSS/JS/HTML giảm băng thông.
  • client_max_body_size: mặc định nginx chặn body >1MB; upload tệp lớn (ảnh sản phẩm, tài liệu) cần tăng giá trị này, nếu không nhận 413 trước cả khi request tới Odoo.
  • Chuyển hướng 80→443: mọi HTTP tự động lên HTTPS.

Vài lưu ý

  • Chỉ đặt security header ở MỘT nơi. Nếu ứng dụng đã gửi HSTS/CSP (như blog này), đừng thêm lần nữa ở nginx — hai giá trị cùng header rất khó gỡ. Đã xác nhận thật: HSTS/CSP trên coffeecode.vn do ứng dụng gửi, nginx chỉ thêm Server và gzip.
  • proxy_read_timeout cho longpolling phải dài (vài trăm giây) vì kết nối được giữ mở chờ sự kiện; để mặc định ngắn thì chat/thông báo bị ngắt liên tục.
  • Đừng để hở cổng 8069 ra Internet. Bind Odoo vào 127.0.0.1 để chỉ nginx (cùng máy) gọi được; mọi khách phải đi qua nginx.

Ba ý mang về

  1. nginx đứng trước Odoo lo TLS, nén, định tuyến — đã đo thật: Odoo trần trả 303 không nén, còn qua nginx là 200 với Server: nginx/1.24.0 và Content-Encoding: gzip.
  2. Odoo có hai cổng: HTTP (8069) và gevent/longpolling (8072). location /websocket phải trỏ sang cổng gevent với Upgrade/Connection "upgrade" và timeout dài.
  3. Bốn proxy_set_header (Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto) là bắt buộc, và Odoo chỉ tin chúng khi proxy_mode = True — thiếu là link sai giao thức, cookie không Secure, rate-limit đếm sai.

Odoo giờ đã chạy đa tiến trình sau nginx với HTTPS — sẵn sàng production. Mảnh ghép vận hành cuối cùng là giữ an toàn cho dữ liệu. Phần sau nói về sao lưu, phục hồi và script migration — cách backup CSDL + filestore đúng cách, phục hồi khi sự cố, và viết migration script để nâng cấp schema an toàn.