Créer un site web avec le Website Builder natif d'Odoo 19 CE
Ce guide s'adresse à toute personne souhaitant créer ou restructurer un site web avec Odoo 19 Community Edition en utilisant les outils natifs du Website Builder : site vitrine, homepage d'une instance existante, ou site livré à un client.
Il ne couvre pas les développements sur mesure (modules custom, thèmes spécifiques, injection de code HTML brut) — ces cas relèvent d'une prestation de développement.
Principe directeur
Une seule règle gouverne toute la méthode, et elle conditionne la pérennité du site livré.
Construire avec les outils natifs d'Odoo, pas contre eux.
Un site construit avec les snippets natifs est : éditable par le client lui-même ; maintenable (compatible mises à jour Odoo) ; cohérent avec l'écosystème (blog, boutique, formulaires s'intègrent naturellement) ; transférable (un autre intégrateur ou le client peut reprendre le travail).
Règles permanentes de design et de contenu
Ces règles s'appliquent d'office à tous les sites construits avec cette méthode. Elles ne sont pas des options — elles constituent le socle de qualité.
Règle D1 — Proportions guidées par le nombre d'or et Fibonacci
Le nombre d'or (φ ≈ 1,618) et la suite de Fibonacci (8, 13, 21, 34, 55, 89…) fixent toutes les proportions du site : colonnes, blocs, espacements et typographie.
Mise en page deux colonnes — ratio 38,2 % / 61,8 %, jamais 50/50. Avec Bootstrap 12 colonnes : col-lg-5 (texte) + col-lg-7 (image/grille). La colonne secondaire est la plus grande.
| Usage | Colonnes Bootstrap |
|---|---|
| Texte (intro) + Image ou grille | col-lg-5 + col-lg-7 |
| Image + Texte (humain, portrait gauche) | col-lg-5 + col-lg-7 |
Ratios de blocs : hero, cartes, images vedettes → 1:1,618 ou 1,618:1. Hauteur du hero ≈ 62 % du viewport (min-height: 62vh).
Espacements — échelle Fibonacci, portée par les variables CSS du thème :
--space-1: 8px; /* Fibonacci : 8 */
--space-2: 13px; /* Fibonacci : 13 */
--space-3: 21px; /* Fibonacci : 21 */
--space-4: 34px; /* Fibonacci : 34 */
--space-5: 55px; /* Fibonacci : 55 */
--space-6: 89px; /* Fibonacci : 89 */
Utiliser ces variables pour padding, gap, margin. Ne pas coder de valeurs arbitraires (32px, 40px, 60px…).
Hiérarchie typographique — ratios φ, base 16 px :
h1 : 42 px (16 × φ²)
h2 : 26 px (16 × φ)
h3 : 20 px (intermédiaire)
body: 16 px
small: 10 px (16 ÷ φ)
Règle D2 — Fidélité éditoriale
Quand un brief pointe vers une maquette de référence ou un catalogue validé, les textes éditoriaux doivent en être repris textuellement, pas reformulés. Toute réécriture — même légère — introduit une dérive de message.
- Identifier la source de vérité éditoriale (maquette, catalogue, brief client).
- Copier les textes tels quels dans les snippets.
- Si une formulation ne tient pas : signaler et proposer une réduction (troncature signalée avec
[…]) — pas une réécriture spontanée. - Ne jamais reformuler sans accord explicite du client ou du donneur d'ordre.
Prérequis
Avant d'ouvrir l'éditeur, vérifier que les éléments suivants sont en place. Un chantier lancé sans contenu ni identité visuelle définie s'enlise systématiquement.
| Prérequis | Détail |
|---|---|
| Odoo 19 CE installé | Module Website activé |
| Accès administrateur | Odoo Admin → Site Web |
| Nom de domaine configuré | DNS pointant vers le serveur, SSL actif |
| Contenu textuel préparé | Textes, argumentaire, tarifs — avant de commencer |
| Images disponibles | Logo, photos — formats web (JPEG/PNG/SVG, <500 ko) |
| Identité visuelle définie | Couleurs primaire/secondaire, police si personnalisée |
Processus de création
Le processus se déroule en sept étapes, de la préparation du contenu jusqu'au SEO. L'ordre compte : le contenu précède toujours la mise en forme.
Étape 1 — Préparer le contenu (avant d'ouvrir l'éditeur)
C'est l'étape la plus importante et la plus souvent négligée.
Textes : titre H1 hero (1 ligne) · sous-titre hero (1-2 phrases) · proposition de valeur (2-3 phrases) · description de chaque offre (3-5 lignes/carte) · bio/présentation équipe (4-6 lignes) · processus client (3 étapes max) · CTA final (1 titre + 1 sous-titre) · meta description SEO (150-160 caractères).
Images : logo (SVG ou PNG fond transparent) · photo hero (optionnelle, fond sombre si texte blanc) · portrait(s) humain(s) (JPEG, ratio portrait) · favicons (gérés par Odoo).
Règle : ne jamais rédiger dans l'éditeur Odoo. Rédiger dans un document texte, valider avec le client, puis copier-coller. L'éditeur est pour la mise en forme, pas la rédaction.
Étape 2 — Configurer l'identité visuelle
La palette et le logo se règlent une fois pour toutes avant de poser le premier snippet.
- Odoo Admin → Site Web → Configuration → Couleurs (ou Website Builder → icône pinceau).
- Définir Couleur 1 (primaire), Couleur 2 (secondaire). Les sections utilisent les niveaux
o_cc1ào_cc5(de clair à foncé). - Configurer le logo : Site Web → Configuration → Logo.
- Configurer le nom du site et la meta description par défaut : Site Web → Configuration → Informations générales.
Note : si la palette Odoo ne suffit pas, une micro-customisation CSS ciblée est possible via Site Web → Thème → CSS personnalisé (option avancée, à documenter dans le livrable).
Étape 3 — Construire la homepage avec les snippets
Odoo Admin → Site Web → homepage → Modifier. Les sections s'ajoutent par glisser-déposer puis se personnalisent en cliquant dessus.
Structure type d'une homepage vitrine (ordre recommandé) :
| # | Section | Snippet | Rôle |
|---|---|---|---|
| 1 | Hero | s_cover | Accroche principale, bouton CTA |
| 2 | Réassurance | s_text_block | Proposition de valeur, ton |
| 3 | Offres / services | s_features | Grille de cartes descriptives |
| 4 | Offre vedette 1 | s_text_image | Détail + tarif de l'offre principale |
| 5 | Offre vedette 2 | s_text_image | Idem pour une 2e offre si pertinent |
| 6 | Présentation humaine | s_text_image | Portrait + bio |
| 7 | Processus | s_features | 3 étapes max |
| 8 | CTA final | s_call_to_action | Bouton de conversion |
Cette structure n'est pas obligatoire — elle est issue de l'expérience terrain, à adapter au métier.
Étape 4 — Catalogue des snippets natifs utiles
Dix snippets natifs suffisent à construire la quasi-totalité d'un site vitrine. Chacun se glisse depuis la barre de l'éditeur.
s_cover— Hero pleine hauteur. Image/fond de couleur (o_cc5pour fond foncé), texte centré H1 + sous-titre + 1-2 CTA, parallax activable.s_text_block— Bloc texte simple (réassurance, intro, citation).text-centerpour centrer.s_features— Grille de cartes (offres, avantages, étapes). 3 colonnes natives, extensible (col-lg-4→col). Icônes Font Awesome natives.s_text_image— Deux colonnes texte + image. Idéal grilles de prix (remplacer l'image par une listedans l'éditeur HTML). CTA intégré.s_call_to_action— Bande CTA, layout 9/3.o_cc5fond foncé,o_cc1fond clair.s_comparisons— Grille de plans tarifaires (3 offres). Requiertdata-vxml="001"etdata-vcss="001".s_pricelist_boxed— Liste de prix encadrée, style « menu restaurant ».s_quotes_carousel— Carrousel de témoignages. Requiertdata-vxml="001"etdata-vcss="002".s_numbers— Métriques / chiffres clés (classes_number, animés à l'entrée viewport).s_website_form— Formulaire de contact (voir étape 5).
Étape 5 — Configurer le formulaire de contact
Le formulaire natif s_website_form est le moyen recommandé. Configuration minimale :
| Paramètre | Valeur recommandée |
|---|---|
| Modèle cible | mail.mail (envoi d'email) |
| Route | /website/form/ (action du ) |
| Email destinataire | Champ caché email_to → adresse de réception |
| Redirection après envoi | data-success-mode="redirect" + data-success-page="/" |
Champs utiles : email_from (modèle), subject (modèle), name (custom → corps du mail), description (custom → corps).
Piège connu : la route Odoo 19 est /website/form/ (avec préfixe website/) — pas /website_form/. Le JS construit l'URL action + data-model_name ; l'action doit donc être /website/form/ uniquement (voir le piège N001).
Prérequis livraison : configurer le SMTP sortant (Paramètres → Email → Serveurs sortants). Sans SMTP, les messages sont créés en base mais non livrés.
Étape 6 — Images et médias
La médiathèque Odoo doit rester la source unique des images du site : elle garantit des URLs propres et des fichiers retrouvables.
Recommandé : uploader via le Website Builder (clic image → « Remplacer ») ou Médiathèque. Odoo stocke en ir.attachment et génère des URLs propres (/web/image/ir.attachment/ID/raw). Éviter : les data URI base64 dans le HTML (alourdissent les vues, hors médiathèque).
Optimisation : JPEG (photos), PNG (transparence), SVG (logos/icônes). Max 200 ko contenu, 50 ko icônes.
Étape 7 — SEO de base
Chaque page publiée reçoit un titre, une meta description et une URL personnalisés directement dans le Website Builder.
| Élément | Où le configurer |
|---|---|
Titre de la page () | Website Builder → icône SEO → « Titre » |
| Meta description | Website Builder → icône SEO → « Description » |
| Meta description par défaut | Site Web → Config → Infos générales |
Titre visible | Directement dans le snippet hero |
| URL de la page | Website Builder → icône engrenage → « URL » |
Règles : titre 50-60 caractères (mot-clé en tête) ; meta description 150-160 caractères ; H1 unique par page.
Critères de validation (checklist de sortie)
Un site n'est livrable que lorsque les quatre familles de critères sont vérifiées, chacune de bout en bout.
- Contenu : sections présentes · textes relus/validés client · tarifs à jour · aucun placeholder « Lorem ipsum » / « Bientôt ».
- Technique : affichage mobile correct (responsive Bootstrap) · liens CTA corrects · formulaire testé (un envoi arrive à l'adresse cible) · SMTP configuré et vérifié · SSL valide.
- SEO : titre HTML personnalisé · meta description personnalisée · attributs
altsur les images. - Éditabilité : le client peut ouvrir le Website Builder et modifier les textes · client formé aux opérations de base.
Multi-website Odoo 19 — plusieurs domaines sur une instance
Odoo 19 CE supporte nativement plusieurs sites web sur une même instance. Chaque site est un enregistrement website avec son propre domaine, sa palette, ses pages et son menu.
Argument commercial : pour plusieurs marques ou sites sectoriels, le multi-website évite de déployer une instance supplémentaire — réduction des coûts d'infrastructure, de maintenance et de sauvegardes. « Plusieurs sites pour plusieurs marques, tous gérés depuis le même backoffice, avec les mêmes clients et commandes. »
Création d'un 2e site (ou Nème)
Un site supplémentaire se crée en une commande dans le shell Odoo.
website2 = env['website'].create({
'name': 'Nom du site',
'domain': 'exemple2.fr', # sans https:// ni slash final
'company_id': env.company.id,
'default_lang_id': env.ref('base.lang_fr').id,
})
env.cr.commit()
# → website_id attribué automatiquement (ex. 2)
Partage de res.company : par défaut, tous les sites partagent la même société. Si le multi-website représente des entités juridiques distinctes, créer une société séparée par site avant de créer les sites.
Mapping domaine → website_id
Odoo détermine le site servi en comparant le header Host: HTTP au champ website.domain :
Host: entrant | website.domain | Site servi |
|---|---|---|
exemple1.fr | exemple1.fr | website_id=1 |
exemple2.fr | exemple2.fr | website_id=2 |
| Tout autre | (aucun match) | Premier site (website_id=1 par défaut) |
Important : domain ne doit pas contenir https:// ni www. — Odoo compare le hostname nu.
Configuration Nginx — un vhost par domaine
Chaque domaine additionnel a son vhost. Tous proxy-passent vers le même Odoo (127.0.0.1:8069) — Odoo résout le site via le Host:.
server {
server_name exemple.fr www.exemple.fr;
location / {
proxy_pass http://127.0.0.1:8069;
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_set_header Host $host; # ← critique : Odoo lit ce header
proxy_buffering off;
proxy_read_timeout 720s;
}
location ~* /web/static/ {
proxy_cache_valid 200 90d;
proxy_buffering on;
expires 864000;
proxy_pass http://127.0.0.1:8069;
proxy_set_header Host $host;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/exemple.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/exemple.fr/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
if ($host = www.exemple.fr) { return 301 https://exemple.fr$request_uri; }
if ($host = exemple.fr) { return 301 https://exemple.fr$request_uri; }
listen 80;
server_name exemple.fr www.exemple.fr;
return 404;
}
ln -s /etc/nginx/sites-available/exemple.fr.conf /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
certbot --nginx -d exemple.fr -d www.exemple.fr --non-interactive --agree-tos \
--redirect -m contact@exemple.fr
Pages et menus spécifiques à un site
La séparation par site s'appuie sur le champ website_id, présent sur les pages, les vues QWeb et les menus.
- Pages : une
website.pageest liée viawebsite_id.website_id=2→ visible que sur le site 2 ; sanswebsite_id→ partagée. - Vues QWeb : une
ir.ui.viewavecwebsite_id=2override la vue globale pour le site 2. - Menus :
website.menulié àwebsite_id; créer un menu racine par site.
Clés de vues — convention :
website.NOMSITE_homepage
website.NOMSITE_palette
website.NOMSITE_mentions_legales
website.NOMSITE_footer_legal
website.NOMSITE_hide_footer_copyright
Pièges spécifiques : le multi-website concentre une série de pièges validés terrain (mauvais site servi, CSS de palette jamais appliqué, vues d'extension inopérantes…). Ils sont documentés avec symptôme, cause et solution sur la page Pièges Odoo 19, références N009 à N017.
Cache à connaître : les changements de vues (ir.ui.view) peuvent tarder (cache de page/QWeb des workers) — patienter ou systemctl restart odoo. custom_code_head, website.logo et website.rewrite se reflètent vite.
Analytics — Plausible CE (optionnel recommandé)
Option recommandée — non obligatoire, mais conseillée pour tout site livré à un client soucieux de ses données. À activer si le VPS client le permet.
Plausible Analytics CE est une solution d'analyse de trafic open source (AGPL v3), auto-hébergée sur le VPS du client — chaque client reste maître de ses données.
Pourquoi le recommander
- Cohérent avec une promesse « informatique libre et éthique ».
- Sans cookies → pas de bandeau de consentement obligatoire (RGPD).
- Exempt CNIL (5 critères : données anonymisées, auto-hébergé, pas de suivi inter-sites, pas de fingerprinting, pas de partage tiers).
- Données hébergées chez le client, pas sur un tiers.
Déploiement Plausible CE sur un VPS Ubuntu
À refaire si le VPS est réinstallé — Plausible tourne dans des containers Docker qui ne font pas partie des sauvegardes système standard.
Étape A — Installer Docker Engine
sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update && sudo apt-get install -y \
docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo docker --version && sudo docker compose version
Étape B — Déployer la stack Plausible CE
sudo mkdir -p /opt/plausible/community-edition
cd /opt/plausible/community-edition
sudo curl -fsSL \
https://raw.githubusercontent.com/plausible/community-edition/v2.1.5/compose.yml \
-o compose.yml
sudo mkdir -p clickhouse
# logs.xml — niveau warning uniquement
sudo tee clickhouse/logs.xml > /dev/null << 'XEOF'
warning true
XEOF
# ipv4-only.xml — IPv4 uniquement (Docker bridge)
sudo tee clickhouse/ipv4-only.xml > /dev/null << 'XEOF'
0.0.0.0
XEOF
# low-resources.xml — NE PAS réduire background_pool_size (conflit MergeTree)
sudo tee clickhouse/low-resources.xml > /dev/null << 'XEOF'
0.5
XEOF
# .env — variables d'environnement
SECRET=$(openssl rand -hex 64)
sudo tee .env > /dev/null << XEOF
BASE_URL=https://analytics.CLIENT.fr
SECRET_KEY_BASE=${SECRET}
HTTP_PORT=8000
DISABLE_REGISTRATION=invite_only
MAILER_ADAPTER=Bamboo.SMTPAdapter
MAILER_EMAIL=contact@CLIENT.fr
SMTP_HOST_ADDR=smtp.exemple.fr
SMTP_HOST_PORT=465
SMTP_USER_NAME=contact@CLIENT.fr
SMTP_USER_PWD=MOT_DE_PASSE_SMTP
SMTP_HOST_SSL_ENABLED=true
XEOF
# compose.override.yml — exposer le port 8000 en localhost uniquement
sudo tee compose.override.yml > /dev/null << 'XEOF'
services:
plausible:
ports:
- 127.0.0.1:8000:8000
XEOF
sudo docker compose up -d
sudo docker ps --format "table {{.Names}}\t{{.Status}}" # attendre ~30s
Étape C — Créer le compte admin et les sites
sudo docker cp /tmp/setup.exs community-edition-plausible-1:/tmp/
sudo docker exec community-edition-plausible-1 \
/app/bin/plausible rpc 'Code.eval_file("/tmp/setup.exs")'
Contenu de /tmp/setup.exs :
alias Plausible.{Repo, Auth, Sites, Site}
import Ecto.Changeset
{:ok, admin} = Auth.create_user("Nom Admin", "admin@CLIENT.fr", "MOT_DE_PASSE")
for domain <- ["CLIENT.fr"] do
{:ok, %{site: site}} = Sites.create(admin, %{domain: domain, timezone: "Europe/Paris"})
slug = :crypto.strong_rand_bytes(16) |> Base.url_encode64(padding: false)
cs = Site.SharedLink.changeset(%Site.SharedLink{}, %{name: "Odoo Dashboard", slug: slug})
|> put_change(:site_id, site.id)
{:ok, link} = Repo.insert(cs)
IO.puts("#{domain} → slug=#{link.slug}")
end
Note Plausible v2.x : le slug du SharedLink est required (à générer). Sites.create/2 retourne {:ok, %{site: site}}. L'admin doit être créé AVANT les sites.
Étape D — Vhost Nginx + SSL
sudo tee /etc/nginx/sites-available/analytics.CLIENT.fr.conf > /dev/null << 'XEOF'
server {
server_name analytics.CLIENT.fr;
location / {
proxy_pass http://127.0.0.1:8000;
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_set_header Host $host;
proxy_buffering off;
}
listen 80;
}
XEOF
sudo ln -sf /etc/nginx/sites-available/analytics.CLIENT.fr.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d analytics.CLIENT.fr --non-interactive --agree-tos \
--redirect -m contact@CLIENT.fr
Mise en service côté Odoo
Une fois la stack en place, deux réglages relient Odoo à Plausible : les paramètres système (une fois par instance) et la configuration de chaque site.
- Paramètres système Odoo (une fois par instance) :
ICP = env['ir.config_parameter'].sudo() ICP.set_param('website.plausible_server', 'https://analytics.CLIENT.fr') ICP.set_param('website.plausible_script', 'https://analytics.CLIENT.fr/js/script.js') - Configurer le site Odoo :
→ Odoo injecte le script dans leenv['website'].browse(1).write({ 'plausible_site': 'CLIENT.fr', 'plausible_shared_key': '', }) de toutes les pages et le rapport devient visible dans le backoffice (Site Web → tableau de bord). - Vérifier l'injection unique :
curl -s https://CLIENT.fr | grep -c "plausible"→ 1. Si le compteur retourne 2 ou plus, voir le piège N008 (double injection).
Champs Odoo 19 CE (référence)
| Champ | Modèle | Valeur |
|---|---|---|
plausible_site | website | domaine (ex : client.fr) |
plausible_shared_key | website | slug auth uniquement (pas l'URL complète) |
website.plausible_server | ir.config_parameter | https://analytics.CLIENT.fr |
website.plausible_script | ir.config_parameter | https://analytics.CLIENT.fr/js/script.js |
_onchange_shared_key accepte aussi l'URL complète dans l'UI (extrait le slug). En script, passer le slug directement.
Plausible per-website
Les champs plausible_site / plausible_shared_key sont au niveau de chaque website → configurables par site. Les ir.config_parameter (website.plausible_server / website.plausible_script) sont globaux (une valeur par instance).
env['website'].browse(2).write({
'plausible_site': 'exemple2.fr',
'plausible_shared_key': '',
})
Mentions légales / politique de confidentialité
Si Plausible est activé, mentionner dans la politique de confidentialité : outil de mesure d'audience auto-hébergé, sans cookies, exempté CNIL.
Pièges connus de la méthode native
Les erreurs récurrentes du Website Builder (route de formulaire en 404, mauvais modèle cible, snippets sans styles, multi-website servi par le mauvais site…) sont classifiées sous les références N001 à N019, chacune avec symptôme, cause et solution vérifiés sur Odoo 19 CE. Elles sont regroupées sur la page dédiée : Pièges Odoo 19 — index des erreurs classifiées.
Méthode IA&TIC — © 2026 IA&TIC — Freddy Maillard & Fabrice-Loïc Barbier — CC BY-SA 4.0