- What the website module provides and how it differs from the portal
- The
ir.websitemodel and website context - How multi-website works and how routes are website-aware
- Built-in SEO fields and how to use
website.seo.metadata - Key modules that build on website: eCommerce, Blog, Forum, Live Chat
What is the Website Module?
The website module (odoo/addons/website/) turns Odoo into a CMS. It provides:
- Page editor — drag-and-drop building blocks (snippets) for non-developers
- Website-aware routes — controllers can be restricted to a specific website instance
- SEO management — per-page title, description, sitemap inclusion, structured data
- Multi-website — run multiple websites from one Odoo instance with different domains
- ir.website — the database model representing a website instance
Unlike the portal (/my/), website pages are publicly accessible at /. You build public-facing marketing and product pages here, not authenticated customer areas.
The ir.website Model
ir.website stores configuration for each website: domain, language, default language, favicon, header/footer theme, and social media links. From Python you access the current website via request.website:
from odoo import http
from odoo.http import request
class MyController(http.Controller):
@http.route('/about', type='http', auth='public', website=True)
def about_page(self, **kw):
website = request.website # ir.website record
company = website.company_id # linked res.company
return request.render('my_module.about_page_template', {
'website': website,
'company': company,
})
The website=True parameter on @http.route is what activates website context — it makes request.website available and enables multi-website routing.
Website vs Portal
| Feature | website module | portal module |
|---|---|---|
| URL prefix | / (public pages) | /my/ (authenticated area) |
| Authentication | Public (no login required) | Portal user login required |
| Target audience | Anonymous visitors | Existing customers |
| Page editor | Yes — drag-and-drop snippets | No |
| SEO tools | Built-in | Minimal |
| Dependency | web, mail | web |
Multi-Website Architecture
One Odoo instance can host multiple websites (e.g., brand-a.com and brand-b.com) simultaneously. Each website has its own ir.website record. Routes with website=True are automatically scoped to the current website by matching request.httprequest.environ['HTTP_HOST'] against the website's domain.
To restrict a page to a specific website, use request.website.id as a domain filter in your controllers:
products = request.env['product.template'].search([
('website_id', 'in', [False, request.website.id]),
('website_published', '=', True),
])
Records with website_id = False are visible on all websites. Records with a specific website_id are only visible on that website.
SEO Metadata – website.seo.metadata
Add the website.seo.metadata mixin to your model to get built-in SEO fields managed from the backend or the website editor:
class LibraryBook(models.Model):
_name = 'library.book'
_inherit = ['website.seo.metadata', 'website.published.mixin']
_description = 'Library Book'
name = fields.Char(string='Title', required=True)
author = fields.Char(string='Author')
description = fields.Html(string='Description')
The website.seo.metadata mixin adds: website_meta_title, website_meta_description, website_meta_keywords, and website_meta_og_img fields. These are editable in the website editor's SEO panel.
The website.published.mixin adds is_published and website_published fields plus publish/unpublish actions.
Website Module Ecosystem
Several Odoo apps build directly on the website module:
| Module | What it adds |
|---|---|
| website_sale | eCommerce: product pages, cart, checkout, payment |
| website_blog | Blog with posts, tags, and comments |
| website_forum | Q&A forum with voting |
| website_slides | eLearning: slide channels and presentations |
| website_event | Event registration and ticketing |
| website_livechat | Live chat widget integration |
Each of these follows the same pattern: website=True routes, QWeb templates inheriting from website.layout, and models that mix in website.seo.metadata.
website=Trueon@http.routeactivates website context and makesrequest.websiteavailableir.websiterepresents a website instance — one Odoo can run many- Website pages are public (
/); portal pages are authenticated (/my/) - Add
website.seo.metadataandwebsite.published.mixinto make records CMS-manageable
Frequently Asked Questions
Do I need the website module to build public pages in Odoo?
No — you can build public HTTP controllers with just web as a dependency. But without the website module you lose the page editor, SEO tools, sitemap generation, multi-website, and the website.layout template. For most public-facing sites, installing website is the right choice.
What's the difference between website=True and type='http'?
type='http' means the route handles a standard HTTP request (not JSON-RPC). website=True is a separate flag that enables website context — it sets up request.website, handles multi-website routing, and activates the theme layer. You can have type='http' without website=True for non-website HTTP routes.
Can I use the website editor on pages I create in a custom module?
Yes. Register your page URL as an ir.ui.view with the appropriate key and inherit from website.layout. Odoo's website editor will pick it up and allow drag-and-drop editing of snippet areas you mark with oe_structure.
How does multi-language work with the website module?
The website module adds URL-prefixed language routing (e.g., /fr/about for French). Each ir.website record has a default language and a list of supported languages. request.website.get_current_pricelist() and request.lang are set automatically based on the URL prefix or browser preference.