# All You Need to Know About Database Filtering in Odoo

> How Odoo's dbfilter picks which databases a request can reach, the regex traps to avoid, and how OCA's dbfilter_from_header lets Nginx map custom domains to databases.

**Date:** 2020-03-26
**Source:** <https://trobz.com/insights/odoo-dbfilter/>

---


Odoo is a multi-tenant system: a single Odoo server can run and serve several databases. The `dbfilter` option decides which of those databases a given request can reach.

The [official deployment documentation](https://www.odoo.com/documentation/19.0/administration/on_premise/deploy.html#dbfilter) has a good introduction to database filtering. This article goes into how it works in practice, the traps to avoid, and what to do when domain names and database names do not line up.

## How dbfilter Works

`dbfilter` is a regular expression built dynamically for each request. You can use the variables `%d` and `%h` to adapt it to the `Host` header of the HTTP request:

- `%d` is replaced by the first subdomain after `www`:
  - `https://demo.example.com`: `%d` = `demo`
  - `https://www.demo.example.com`: `%d` = `demo`
- `%h` is replaced by the full domain, without the port if there is one:
  - `https://demo.example.com`: `%h` = `demo.example.com`
  - `https://demo.example.com:8080`: `%h` = `demo.example.com`

So if you define `dbfilter = ^%d.*\Z`, a user visiting `https://demo.example.com` can access the databases matching `^demo.*\Z`, for example:

- `demo`
- `demo_backup_20180404`

## Beware of Permissive Patterns

Odoo filters the database list with `re.match`:

```python
dbs = [i for i in dbs if re.match(r, i)]
```

`re.match` anchors the pattern at the start of the name but not at the end, so it is easy to write a regex that is more permissive than intended. If you define `dbfilter = demo_*`, for example, it also matches `demoinefromage_prod`.

## When Domains and Database Names Do Not Match: dbfilter_from_header

You cannot always derive database names from domain names.

For example, a single Odoo instance might serve two databases:

- `customer1_prod`
- `customer2_prod`

But each customer wants its own custom domain:

- `https://back.customer1.com`
- `https://app.customer2.com`

To support this case, you can use OCA's [`dbfilter_from_header`](https://github.com/OCA/server-tools/tree/9.0/dbfilter_from_header) module.

### Installation

```bash
git clone https://github.com/OCA/server-tools -b 9.0 --no-checkout --single-branch --depth 1
cd server-tools
git config core.sparsecheckout true
echo "dbfilter_from_header" > .git/info/sparse-checkout
git read-tree -mu HEAD
```

### Configuration

On the Nginx side, define a dedicated database filter for each virtual host. In our example, for `https://back.customer1.com`:

```nginx
server_name back.customer1.com;

(...)

location / {

    (...)

    proxy_set_header X-Odoo-dbfilter ^customer1_.*\Z;
}
```

Two things to note:

- before passing the request to Odoo, Nginx takes all HTTP headers, lowercases them and converts dashes to underscores;
- we use `\Z` instead of `$` to match the end of the string, because Nginx cannot escape `$`.

## Two Levels of Filtering

`dbfilter_from_header` respects the `dbfilter` defined at Odoo level, so you can set up two levels of filtering. In the Odoo configuration:

```ini
dbfilter = ^.+prod.*$
```

And in Nginx:

```nginx
proxy_set_header X-Odoo-dbfilter ^.*customer1.*\Z;
```

