While Odoo itself can serve web pages, it is strongly recommended to have a reverse proxy in front of it. A reverse proxy acts as an intermediary managing the traffic between the clients sending requests and the Odoo servers responding to them. Using a reverse proxy has several benefits.

On the security side, it can do the following:

And on the performance side, it can provide significant improvements:

Apache is a popular option to use as a reverse proxy. Nginx is a recent alternative with good technical arguments. Here, we will choose to use Nginx as a reverse proxy and show how it can be used to perform the security and performance side functions mentioned here.

First, we should install Nginx. We want it to listen on the default HTTP ports, so we should make sure they are not already taken by some other service. Performing this command should result in an error, as follows:

$ curl http://localhost
curl: (7) Failed to connect to localhost port 80: Connection refused

If not, you should disable or remove that service to allow Nginx to use those ports. For example, to stop an existing Apache server you should use this command:

$ sudo service apache2 stop

Or better yet, you should consider removing it from your system, or reconfigure it to listen on another port, so the HTTP and HTTPS ports (80 and 443) are free to be used by Nginx.

Now we can install Nginx, which is done in the expected way:

$ sudo apt-get install nginx

To confirm that it is working correctly, we should see a Welcome to nginx page when visiting the server address with a browser or using curl http://localhost inside our server.

Nginx configuration files follow the same approach as Apache: they are stored in /etc/nginx/available-sites/ and activated by adding a symbolic link in /etc/nginx/enabled-sites/. We should also disable the default configuration provided by the Nginx installation, as follows:

$ sudo rm /etc/nginx/sites-enabled/default
$ sudo touch /etc/nginx/sites-available/odoo
$ sudo ln -s  /etc/nginx/sites-available/odoo /etc/nginx/sites-enabled/odoo

Using an editor, such as nano or vi, we should edit our Nginx configuration file as follows:

$ sudo nano /etc/nginx/sites-available/odoo

First, we add the upstreams, and the backend servers Nginx will redirect traffic to the Odoo server in our case, which is listening on port 8069, as follows:

upstream backend-odoo { 
  server 127.0.0.1:8069; 
} 
server { 
  location / { 
    proxy_pass http://backend-odoo; 
  } 
} 

To test if the edited configuration is correct, use the following command:

$ sudo nginx -t

If you find errors, confirm the configuration file is correctly typed. Also, a common problem is for the default HTTP to be taken by another service, such as Apache or the default Nginx website. Double-check the instructions given before to make sure that this is not the case, then restart Nginx. After this, we can have Nginx to reload the new configuration as follows:

$ sudo /etc/init.d/nginx reload

We can now confirm that Nginx is redirecting traffic to the backend Odoo server:

$ curl http://localhost
<html><head><script>window.location = '/web' +  location.hash;</script></head></html>

Next, we should install a certificate to be able to use SSL. To create a self-signed certificate, follow the following steps:

$ sudo mkdir /etc/nginx/ssl && cd /etc/nginx/ssl
$ sudo openssl req -x509  -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes
$ sudo chmod a-wx *            # make files read only
$ sudo chown www-data:root *   # access only to www-data group

This creates an ssl/ directory inside the /etc/nginx/ directory and creates a passwordless self-signed SSL certificate. When running the openssl command, some additional information will be asked, and a certificate and key files are generated. Finally, the ownership of these files is given to the user www-data used to run the web server.

Now that we have an SSL certificate, we are ready to configure Nginx to use it.

To enforce HTTPS, we will redirect all HTTP traffic to it. Replace the server directive we defined previously with the following:

server { 
  listen 80; 
  add_header Strict-Transport-Security max-age=2592000; 
  rewrite ^/.*$ https://$host$request_uri? permanent; 
} 

If we reload the Nginx configuration now and access the server with a web browser, we will see that the http:// address will be converted into an https:// address.

But it won't return any content before we configure the HTTPS service properly, by adding the following server configuration:

server { 
  listen 443 default; 
  # ssl settings 
  ssl on; 
  ssl_certificate     /etc/nginx/ssl/cert.pem; 
  ssl_certificate_key /etc/nginx/ssl/key.pem; 
  keepalive_timeout 60; 
  # proxy header and settings 
  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; 
  proxy_redirect off; 
  
  location / { 
    proxy_pass http://backend-odoo; 
  } 
} 

This will listen to the HTTPS port and use the /etc/nginx/ssl/ certificate files to encrypt the traffic. We also add some information to the request header to let the Odoo backend service know it's being proxied.

For security reasons, it's important for Odoo to make sure the proxy_mode parameter is set to True. The reason for this is that, when Nginx acts as a proxy, all request will appear to come from the server itself instead of the remote IP address. Setting the X-Forwarded-For header in the proxy and enabling --proxy-mode solves that. But enabling --proxy-mode without forcing this header at the proxy level would allow anyone to spoof their remote address.

At the end, the location directive defines that all request are passed to the backend-odoo upstream.

Reload the configuration, and we should have our Odoo service working through HTTPS, as shown in the following commands:

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo service nginx reload 
* Reloading nginx configuration nginx
...done.
$ curl -k https://localhost 
<html><head><script>window.location = '/web' +  location.hash;</script></head></html>

The last output confirms that the Odoo web client is being served over HTTPS.