Public URL requirements for realtime push (administrator)
Realtime push needs one thing from your side that scheduled sync does not: a stable, public, HTTPS address that the email provider can call from the internet. This page explains exactly what that requirement is, why Google Pub/Sub and Microsoft Graph both insist on it, and how to verify it yourself before you run the setup assistant.
Realtime push needs one thing from your side that scheduled sync does not: a stable, public, HTTPS address that the email provider can call from the internet. This page explains exactly what that requirement is, why Google Pub/Sub and Microsoft Graph both insist on it, and how to verify it yourself before you run the setup assistant.
Available in: Pro. Realtime push (Gmail Pub/Sub and Outlook Graph) is part of the MailDesk Pro module. Basic always uses scheduled background sync and never needs a public inbound URL. The requirement on this page applies only when you enable realtime push.
This only affects realtime, never normal mail
Scheduled sync polls outbound — MailDesk reaches out to the provider, so it works behind a firewall with no public address at all. The public URL is needed only so the provider can push inbound to your webhook. If the public URL is wrong, realtime push cannot register or deliver, but scheduled sync (Gmail fallback polling every 2 minutes) keeps delivering new mail. No mail is ever lost.
What it does
Both providers deliver realtime notifications by making an HTTPS POST to a webhook on your own server:
- Gmail uses Google Cloud Pub/Sub. The push subscription you create in your Google
Cloud project posts to
<your-url>/maildesk/push/gmail. - Outlook uses Microsoft Graph subscriptions, which post change notifications to
<your-url>/maildesk/push/outlook.
For either provider to reach those routes, MailDesk has to know your public address. It reads
it from the Odoo system parameter web.base.url, and builds the webhook URL as
<web.base.url>/maildesk/push/gmail (or .../outlook). If web.base.url is unset, points
at http://, or points somewhere the provider cannot reach, push registration fails at the
preflight or validation step.
Why it matters
- Providers refuse plain HTTP. Google Pub/Sub and Microsoft Graph both reject non-HTTPS
push endpoints outright. A push subscription pointed at an
http://URL will not be accepted. - Self-signed certificates are rejected. The certificate on your public URL must be signed by a trusted public CA. Self-signed certificates fail the TLS handshake, and Pub/Sub then drops every delivery with a TLS error.
- The audience must match. For Gmail, Pub/Sub signs each push with an OIDC token whose
audclaim must equal the exact URL the message is delivered to. MailDesk computes that audience fromweb.base.url. If the saved audience and the computed webhook URL diverge, validation fails with an audience mismatch. - Stability matters. If your public address changes (a new tunnel, a different host,
web.base.urlrewritten at the next login), the provider keeps pushing to the old address and notifications silently stop. The URL must be one that stays the same.
The exact requirement
To enable realtime push, web.base.url must satisfy all of the following:
| Requirement | Why | If it fails |
|---|---|---|
| Set (not empty) | MailDesk cannot build a webhook URL without it | Error code WEBHOOK_URL_UNSET |
HTTPS (https://…, not http://) |
Providers refuse plain-HTTP push endpoints | Error code WEBHOOK_URL_NOT_HTTPS |
| Valid public-CA certificate | Self-signed / expired / wrong-host certs fail the TLS handshake | Error code WEBHOOK_UNREACHABLE_TLS |
| DNS resolvable from the internet | The provider must look up the hostname | Error code WEBHOOK_UNREACHABLE_DNS |
| Reachable (responds promptly) | A down tunnel or dropping firewall makes push undeliverable | Error code WEBHOOK_UNREACHABLE_TIMEOUT |
| Stable | A changing address means the provider pushes to a dead endpoint | (silence — push stops arriving) |
Set the public URL and freeze it
- Go to Settings → Technical → Parameters → System Parameters.
- Set
web.base.urlto your stable public HTTPS URL, for examplehttps://mail.example.com(no trailing path, no trailing slash). - Set
web.base.url.freezetoTrue.
Why web.base.url.freeze = True matters
By default Odoo overwrites web.base.url with whatever host the next administrator
logs in from. If an admin signs in over an internal hostname, web.base.url quietly
becomes that internal address — and every provider push then targets a URL the internet
cannot reach. Setting web.base.url.freeze = True pins the value so it survives
logins and stays the public HTTPS address you configured.
A valid public certificate
The hostname in web.base.url must present a certificate signed by a trusted public CA —
Let's Encrypt, Cloudflare, or any commercial CA all work. The certificate must cover the
exact hostname the provider connects to. Self-signed certificates will be rejected by
Pub/Sub. If you terminate TLS at a reverse proxy or CDN, make sure that layer serves the
valid certificate and forwards /maildesk/push/gmail (and /maildesk/push/outlook) to Odoo
untouched — no caching, no rewriting, no redirects.
Verify it externally
The MailDesk setup assistant probes the webhook for you during preflight, but you can — and should — confirm reachability yourself from an external machine (not from the Odoo server itself), so you are testing the same path the provider will use.
Run:
curl -sI https://your-url/maildesk/push/gmail
The expected response is:
HTTP/1.1 405 Method Not Allowed
Why 405 is the success signal
The webhook route only accepts POST requests. A plain GET (which is what curl -I
sends) is therefore answered with HTTP 405 Method Not Allowed — and that 405 proves
the route exists, is served by the right MailDesk controller, and is reachable over HTTPS.
It is the correct result, not an error.
What the other responses mean:
405 Method Not Allowed— correct. The webhook is wired up and reachable.- The request hangs / times out — your public URL is pointed somewhere wrong, the tunnel
is down, or a firewall is dropping packets. Fix routing first (
WEBHOOK_UNREACHABLE_TIMEOUT). - A TLS / certificate error — the certificate is expired, self-signed, or for the wrong
hostname (
WEBHOOK_UNREACHABLE_TLS). - Any other status (200, 302, 404, …) — something else is bound to that path: a CDN
cache, a redirect, or the wrong module. Make sure your reverse proxy passes
/maildesk/push/gmailstraight to Odoo (WEBHOOK_UNEXPECTED_STATUS).
If the hostname does not resolve at all, check DNS first:
nslookup your-host
If you use split-brain DNS (the internal name differs from the external one), make sure the
Odoo server resolves the same name external clients see (WEBHOOK_UNREACHABLE_DNS).
Development tunnel caveat
Tunnels such as trycloudflare.com are convenient for local development and QA, but they
are ephemeral: a quick-tunnel URL is generated fresh each time and dies when the origin
restarts. When the tunnel URL changes:
- the provider keeps pushing to the old, now-dead address, so realtime notifications stop;
web.base.urlno longer matches the live endpoint, so the OIDC audience no longer matches (AUDIENCE_MISMATCH) and you must re-run the assistant to re-align it.
For this reason the assistant offers a Developer / Test mode for short-lived tunnel
setups — use it for QA only. For any real deployment, use a stable public hostname with a
persistent certificate, and freeze web.base.url.
Push never blocks normal sync
Even while you are sorting out the public URL, mail keeps flowing. Every webhook error code on this page blocks only realtime registration or delivery — Gmail fallback polling keeps delivering new mail every 2 minutes, and Outlook falls back to scheduled sync, until realtime is restored. Nothing to babysit.
Error codes that point here
If the setup assistant shows one of these codes, this page is the reference for the fix:
| Code | Meaning | Fix |
|---|---|---|
WEBHOOK_URL_UNSET |
web.base.url is empty |
Set it (and freeze it) as above |
WEBHOOK_URL_NOT_HTTPS |
URL is http://, not https:// |
Switch to https:// with a valid certificate |
WEBHOOK_UNREACHABLE_DNS |
Hostname does not resolve | Check DNS with nslookup; reconcile split-brain DNS |
WEBHOOK_UNREACHABLE_TIMEOUT |
No response within 5 seconds | Fix routing / tunnel / firewall; expect 405 on curl -sI |
WEBHOOK_UNREACHABLE_TLS |
TLS handshake failed | Replace with a public-CA certificate for the exact hostname |
WEBHOOK_UNEXPECTED_STATUS |
Something other than 405 answered |
Pass /maildesk/push/gmail straight to Odoo, no cache/redirect |
Related
- Gmail realtime push setup — the guided assistant
- Outlook realtime push setup
- Realtime troubleshooting — emails missing or delayed
- Realtime & synchronization architecture — how push, sync, and prefetch fit together