Ad – 728×90
💻 Website Development

Odoo Website Module – Overview and Architecture

The Odoo website module is a full-featured CMS and web framework built on top of Odoo's controller layer. It adds a drag-and-drop page editor, SEO tools, website-specific routes, and a multi-website architecture — all deeply integrated with your backend data models.

⏱️ 20 min 🎯 Intermediate 📅 Updated 2026
What you'll learn:
  • What the website module provides and how it differs from the portal
  • The ir.website model 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:

Python
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

Featurewebsite moduleportal module
URL prefix/ (public pages)/my/ (authenticated area)
AuthenticationPublic (no login required)Portal user login required
Target audienceAnonymous visitorsExisting customers
Page editorYes — drag-and-drop snippetsNo
SEO toolsBuilt-inMinimal
Dependencyweb, mailweb

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:

Python
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:

Python
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.

Ad – 728×90

Website Module Ecosystem

Several Odoo apps build directly on the website module:

ModuleWhat it adds
website_saleeCommerce: product pages, cart, checkout, payment
website_blogBlog with posts, tags, and comments
website_forumQ&A forum with voting
website_slideseLearning: slide channels and presentations
website_eventEvent registration and ticketing
website_livechatLive 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.

Key takeaways:
  • website=True on @http.route activates website context and makes request.website available
  • ir.website represents a website instance — one Odoo can run many
  • Website pages are public (/); portal pages are authenticated (/my/)
  • Add website.seo.metadata and website.published.mixin to 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.