Configure Instance on a different domain

How to configure a single Remark42 instance for multiple domains

What doesn't work so far?

Unless discussion #1139 has a marked answer, authorisation using oAuth like GitHub or Google is impossible on domains other than the original one. Telegram, Email and anonymous auth work on allowed HTTPS embedding domains when AUTH_SEND_JWT_HEADER=true.

Setup

Set ALLOWED_HOSTS="'self',https://example1.org,https://example2.org" with your domain names, and AUTH_SEND_JWT_HEADER=true.

AUTH_SEND_JWT_HEADER is what keeps a reader signed in across a page reload on Safari, in Chrome Incognito, and in any browser configured to block third-party cookies. Read its security considerations before enabling it: it puts the token in a cookie JavaScript can read, which costs XSS exposure that a server-set HttpOnly cookie does not.

AUTH_SAME_SITE=none is not needed alongside it, which reverses what this page recommended for years, so it is worth showing the measurement instead of asserting it. Signing in anonymously from an embedded widget over https and dumping the browser's cookie jar gives, with the setting:

namehttpOnlypartition keywritten by
JWTyesnonethe server
XSRF-TOKENnononethe server
JWTnothe embedding sitethe widget
XSRF-TOKENnothe embedding sitethe widget

and without it, only the widget's own partitioned pair. Sign-in, posting and the reload all work either way, in a permissive browser and in one blocking third-party cookies. What the setting adds is the unpartitioned HttpOnly JWT in the first row, delivered as a third-party cookie to every listed domain wherever the browser still permits that. Nothing needs it, so leaving it at the default is the smaller exposure.

Keep AUTH_SAME_SITE=none if you are not setting AUTH_SEND_JWT_HEADER. Then the server's cookies are the only ones there are, and this is what allows them to be set off-domain at all, for as long as the reader's browser still accepts unpartitioned third-party cookies.

The 'self' in ALLOWED_HOSTS value means "domain where Remark42 is installed on" and needed if you want remark42.example.com/web/ to work in case you want to test something with it.

Technical details

ALLOWED_HOSTS sets CSP frame-ancestors, which, once enabled, limits the domains where Remark42 would work. The default value is * so that it would work on any domain.

AUTH_SAME_SITE sets the SameSite attribute on the cookies the server sets. none lets those cookies be set on any domain where Remark42 is shown.

The default setting does not mean Lax. It means Remark42 emits no SameSite attribute at all and leaves the choice to the browser, and browsers differ: Chromium treats a missing attribute as Lax and so refuses the cookie cross-site, while Firefox accepts it. That difference is not a detail, because it decides which of the two cookie pairs below a reader actually ends up with.

SameSite=None is not sufficient on its own, and with AUTH_SEND_JWT_HEADER it is not necessary either. A browser that blocks third-party cookies drops a cookie set by Remark42 for a reader on another domain no matter what its SameSite value is, unless the cookie is explicitly marked Partitioned, and the server-set cookies are not. AUTH_SEND_JWT_HEADER=true is what closes that: the token comes back in an X-JWT response header and the widget stores it in its own cookie, written from inside the embedded frame and marked SameSite=None; Secure; Partitioned, so the browser keeps it for that embedding site and sends it back after a reload. That cookie is the widget's own doing and owes nothing to AUTH_SAME_SITE, which reaches only the pair the server sets.

A browser that refuses a cross-site Set-Cookie lacking SameSite=None refuses it outright, so with the setting left at its default the server's pair is absent from the jar, not present with a stricter attribute.

Note that this applies to Email, Telegram and anonymous authorisation, which the widget performs from inside the frame. It does not rescue oAuth, which completes in a popup that is a top-level page of its own, so the cookie set there belongs to the Remark42 domain and the embedded frame never sees it.

What each browser actually does

Measured on real domains over real certificates, with Remark42 on one registrable domain and the
host page on another, signing in and then reloading. Every "blocked" column below was verified with
a control cookie: an ordinary third-party cookie written from inside the widget frame has to be
dropped, or the run is not blocking anything and proves nothing.

configurationChrome, defaultChrome, third-party cookies blockedFirefox, defaultFirefox, "block all third-party"Safari
AUTH_SEND_JWT_HEADER onlyworksworksworksfailsworks
AUTH_SAME_SITE=none onlyworksfailsworksfailsfails
bothworksworksworksfailsworks

Three things in that table are worth spelling out.

Safari needs no configuring to break the old recipe. It blocks third-party cookies out of the
box, so AUTH_SAME_SITE=none on its own has already stopped working there for every reader. This
is not a future deprecation to plan for.

Firefox reaches "works" by a different route, and a weaker one. Chrome and Safari refuse the
server's cookie when it carries no SameSite attribute, which leaves the field clear for the
widget to write its own partitioned pair. Firefox accepts that cookie, and because the server's
JWT is HttpOnly, the browser then refuses to let the widget's script overwrite it: a cookie set
by JavaScript may not replace an HttpOnly one of the same name. So on Firefox the session rides
on an ordinary unpartitioned third-party cookie even with the header flag on, and it disappears the
moment the reader blocks those.

No configuration survives Firefox's "block all third-party cookies" setting. That mode discards
partitioned cookies too, so the Partitioned escape hatch does not apply. A reader who has turned
it on cannot stay signed in on an embedded widget, and nothing in Remark42 can change that.

Here are all possible combinations of these two:

  • Default setup with unaltered variables: comments are shown on any domain, but the authorisation wouldn't work anywhere, except on the same domain Remark42 is installed on and subdomains of it.
  • ALLOWED_HOSTS set to a set of domains: comments are shown only on listed domains, and authorisation wouldn't work anywhere, except on the same domain Remark42 is installed on and subdomains of it.
  • AUTH_SAME_SITE set to None: comments are shown on any domain. Authorisation works on browsers that still permit third-party cookies, and stops working on the ones that block them.
  • ALLOWED_HOSTS set to a set of domains and AUTH_SAME_SITE set to None: comments are shown on listed domains, with the same authorisation caveat.
  • ALLOWED_HOSTS and AUTH_SEND_JWT_HEADER=true, with AUTH_SAME_SITE left alone: comments are shown on listed domains, and Email, Telegram and anonymous authorisation survives a reload whatever the browser's third-party cookie policy. This is the recommended arrangement. Adding AUTH_SAME_SITE=none on top changes nothing about whether a reader stays signed in; it only adds the server's unpartitioned cookies where the browser still takes them.