---
title: "Domain per Language plugin"
---

# Domain per Language plugin

## Troubleshooting

## Nothing happens at all

 Check, in this order: the plugin is enabled, at least one domain row is filled in and saved, Search Engine Friendly URLs are on, and the **System - Language Filter** plugin is enabled. The settings screen warns about the last two. Then check that the host you are testing is exactly the host you configured, including or excluding `www`.

 
## The language code is still in the URL

 The code only disappears for the language that owns that domain. On the English domain `/en/` goes away; `/de/` stays, because German lives on the default domain or on its own. If a code you expect to disappear stays, the row for that language is missing or points at another host.

 
## I end up in a redirect loop

 Almost always a second redirect outside Joomla that fights this one: a server rule that forces `www` while the plugin is configured without it, or the other way round. Make the server rule and the domain row agree. Browsers cache permanent redirects, so test in a private window after changing something.

 
## A visitor sees the wrong language on a domain

 That is what this plugin prevents: on a configured domain the language comes from the domain and not from a cookie or the browser. If it still happens, another plugin is changing the router order; disable other system plugins one by one to find it.

 
## I see /_dpl/ in a URL

 That is the internal marker for a link to another domain. Inside pages, feeds and JSON it is replaced automatically, and a marker URL that is opened anyway redirects to the right domain, so nothing breaks. If you see it in output your own code produced, resolve it with `Domainperlanguage::resolveUrl()`; see the article about links in other extensions.

 
## robots.txt shows the Joomla default (PRO)

 There is a `robots.txt` file in the root of your site; the web server serves that file itself. Rename or remove it. The plugin warns about it on its settings screen. If there is no file, check that your server sends missing files to `index.php`.

 
## The language suggestion never appears (PRO)

 Walk through these: the setting is on; the page is associated with a page in the other language; *Add Alternate Meta Tags* is on in the language filter; your browser really asks for that other language first; and you did not close the bar earlier, which is remembered for 30 days in a cookie on that domain. Test in a private window with the browser language changed.

 
## Staying logged in does not work (PRO)

 Check that the setting is on, that both domains are in the list, and that the visitor is logged in on the front end - the administrator is never included. Logging out everywhere needs sessions in the database with session metadata on; the health check tells you whether that is the case. A user with multi-factor authentication is asked for the second factor on the other domain, which is intended.

 
## The health check says a domain is not reachable from the server (PRO)

 Some hosting setups cannot reach their own public address. Open the link in the message in your own browser: a 64-character code means the domain does reach this site and only the server cannot see it. Your visitors are not affected. Ask your host about hairpin NAT or split DNS if you want the check to go green.

 
## Still stuck

 Run the domain health check and keep its result at hand: it names the domain, the failing check and the reason in one line, which is usually enough to see where the problem sits - DNS, certificate, web server or Joomla.


[Read more...](https://www.joomill-extensions.com/domainperlanguage/troubleshooting.md)

## Frequently asked questions

## Do I need a domain for every language?

 No. A language without a domain keeps its language code and is served on the domain of the default site language. You can start with one domain and add others later.

 
## Can two languages share one domain?

 Not as their own domain: one domain belongs to one language. Languages without a domain do live together on the default domain, each with its own language code.

 
## Can I use subdomains?

 Yes. A domain here is simply a host name, so `nl.example.com` works exactly like `www.example.nl`. Search engines treat a country domain as a stronger local signal, but that is an SEO choice, not a technical one.

 
## Do I need a separate Joomla installation per domain?

 No, and that is the point. One installation, one administrator, one set of extensions, several domains.

 
## Is this duplicate content?

 No. Every page exists on exactly one URL, the language code of a domain's own language redirects away permanently, and the hreflang links, the x-default and the canonical all point at the domain of each language.

 
## What happens on my staging domain?

 Nothing at all. A host that is not in your list is left untouched, so staging, `localhost` and an IP address keep working like an ordinary multilingual Joomla site with language codes.

 
## Does it work with the page cache?

 Yes. The URLs are already correct before a page is cached, and Joomla caches per full URL including the host.

 
## Can one language have two domains?

 One language has one domain in the plugin. An extra domain you own - an old name, another extension - is best pointed at the right domain with a permanent redirect in your web server.

 
## What does the free edition include?

 Everything about routing: the language per domain, the redirects, the links between domains, hreflang and the canonical correction. PRO adds a robots.txt per domain, the language suggestion, the domain health check and staying logged in across domains.

 
## Do I need a certificate for every domain?

 Yes, every domain and every www variant needs a valid certificate. The PRO health check warns you two weeks before one expires.

 
## Does it change my URLs in any other way?

 No. Only the language code of the domain's own language disappears; the rest of the URL is built by Joomla and your menu structure exactly as before.


[Read more...](https://www.joomill-extensions.com/domainperlanguage/frequently-asked-questions.md)

## Links in other extensions

This article is for developers. Inside a normal page you never have to do anything: the plugin makes sure every link to another language domain is a complete URL before the page is sent. It matters when your code stores a URL, mails it, or writes it to a file.

 
## Why a link needs resolving

 Joomla's router builds links for the current request and always keeps the host of that request. A link to a page in another language therefore leaves the router with a marker in its path, which the plugin replaces with the domain of that language while the page is finished.

 A URL that leaves the request before that moment keeps the marker. That happens when a scheduled task writes a sitemap, when an extension mails a link, or when an API hands URLs to another system.

 
## Resolving a URL yourself

 The plugin exposes one static method for this. Guard it with `class_exists()` so your extension keeps working when Domain per Language is not installed:

 
```
use Joomill\Plugin\System\Domainperlanguage\Extension\Domainperlanguage;

$url = Route::link('site', 'index.php?option=com_content&view=article&id=42&lang=nl', false, 0, true);

if (class_exists(Domainperlanguage::class)) {
    $url = Domainperlanguage::resolveUrl($url);
}```

 It accepts a single URL or a whole text containing URLs, and returns it with every marker replaced by the right domain. When the plugin is disabled or no domains are configured, it returns the input unchanged, so calling it is always safe.

 
## If a marker slips through anyway

 A URL with a marker that reaches a visitor still works: the plugin answers it with a permanent redirect to the same page on the right domain. A link in an old e-mail therefore never ends in a 404. Resolving is about clean URLs, not about broken ones.

 
## Both editions

 This method is part of the free edition as well, so an extension that supports Domain per Language works for every user of the plugin.


[Read more...](https://www.joomill-extensions.com/domainperlanguage/links-in-other-extensions.md)

## Staying logged in across domains (PRO)

Each domain has its own cookies, so a member who logs in on your Dutch domain is a guest on your English one. With **Stay logged in across domains** switched on, the login travels along the moment the visitor follows a link to another language domain.

 
## What the visitor notices

 Nothing, which is the point. They click a link to the other domain and they are still logged in when the page opens. The address bar shows the ordinary URL of that page.

 
## How it works

 
1. A logged-in visitor clicks a link to another configured domain.
2. The browser asks this site for a one-time token. The token is valid for sixty seconds, for that one domain, and only the token's fingerprint is stored, never the token itself.
3. The link opens with the token attached. The other domain logs the visitor in, removes the token from the URL with a redirect, and the token can never be used again.

 If anything goes wrong - the token expired, the network hiccupped, the visitor opened the link in a new tab much later - the page simply opens as a guest. Nothing breaks, and the visitor can log in as usual.

 
## Logging out

 Logging out on one domain ends the session on all of them. That is the half people forget when they build this themselves: a login that follows the visitor everywhere must also be revocable everywhere, especially on a shared computer.

 This part needs Joomla's default session settings: sessions stored in the database, with session metadata on. The domain health check tells you whether that is the case. Note that it also applies when the **User - Joomla** plugin has *Force Logout* switched off.

 
## Security

 
- Only the front end. The administrator is never touched: an administrator logs in per domain as before.
- The login runs through Joomla's own user plugins, so a blocked account, an account waiting for activation and the usual permissions all still apply. Multi-factor authentication is asked again on the other domain.
- A token works once, for sixty seconds, on one domain, for one user. Asking for one requires a valid session and Joomla's form token.
- The token is removed from the URL right away and is never sent on to another site through the referrer.
- No remember-me cookie is created. The visitor stays logged in as long as their session lasts, exactly as on the domain where they signed in.

 
## What it does not do

 It does not share logins between two different Joomla sites, and it does not keep a visitor logged in on a domain that is not in your list. The whole mechanism only works between the domains you configured in this plugin.


[Read more...](https://www.joomill-extensions.com/domainperlanguage/staying-logged-in-across-domains.md)

## Domain health check (PRO)

Domains break quietly. A certificate expires, a DNS record is changed, a www variant stops redirecting, and the first sign is a drop in traffic weeks later. The *Check domains* button on the plugin's settings screen tests every domain you saved and tells you what is wrong in one table.

 
## How to use it

 Open the plugin, press **Check domains** and wait a few seconds. The check uses the domains as they are saved, so save your changes first. Every row gets a status - OK, a warning or an error - with one sentence of explanation.

 
## What is checked, per domain

 
- **DNS** - the domain resolves, and the addresses it resolves to are shown.
- **HTTPS** - the domain reaches *this* Joomla site. The check asks the domain a question only this site can answer correctly, so a domain that points at another server or another Joomla is caught.
- **Certificate** - valid for this name, with a warning when it expires within two weeks and an error when it has expired.
- **HTTP to HTTPS** - the plain http address redirects to https.
- **www variant** - the variant you did not configure redirects to the one you did. A variant that does not resolve at all is fine and says so.
- **robots.txt** - the domain serves the robots.txt you filled in for it, if you filled one in.

 
## What is checked once, for the whole site

 
- **Sessions** - sessions are stored in the database, which is what makes logging out work on every domain.
- **Language filter** - the core plugin Domain per Language depends on is enabled.
- **SEF URLs** - search engine friendly URLs are on.

 
## Not reachable from the server

 Some hosting setups cannot reach their own public address: the server asks for your domain, the request goes out and never comes back. The check then says *not reachable from the server* and offers a link to open the test in your own browser. A 64-character code in that page means the domain does reach this site and only the server itself cannot see it. That is a hosting matter, not a plugin one; your visitors are not affected.

 
## Safety

 The check runs only for administrators who may manage plugins, and only against the domains you saved: it never sends a request to a host taken from the browser. Every connection has a five second time-out, and redirects are not followed, so a chain of redirects cannot hide a problem.


[Read more...](https://www.joomill-extensions.com/domainperlanguage/domain-health-check.md)

## Language suggestion (PRO)

A visitor from Germany who lands on your English domain may not know that the same page exists in German on another domain. With **Language suggestion** switched on, a small bar at the bottom of the page offers that page in the language their browser asks for. It never redirects anyone.

 
## What the visitor sees

 A fixed bar at the bottom of the page, in the visitor's own language, with one sentence, a link to the same page on the domain of that language, and a close button. The bar is positioned over the page instead of pushing it down, so nothing moves while the page loads.

 Closing it stores a cookie on that domain for 30 days, per language. The visitor is not asked again for that language, and the choice does not travel to your other domains, because a cookie belongs to one domain.

 
## When the bar appears

 Only when all of this is true:

 
- the setting is on and the visitor is on a configured domain;
- the page exists in another language on another domain, and the two pages are associated in Joomla;
- the first language the browser asks for is not the language of the page;
- that language was not dismissed on this domain in the last 30 days.

 An exact match wins from a general one: a browser asking for `nl-BE` gets the Belgian page when you have one, and the Dutch page otherwise.

 
## What it needs

 The suggestion reads the hreflang links of the page, so the **System - Language Filter** plugin must have *Add Alternate Meta Tags* switched on, which is its default. Pages without an association to another language get no bar, which is the honest outcome: there is nothing to link to.

 
## Page cache and privacy

 The page itself is the same for every visitor: it carries the possible suggestions, and the choice happens in the browser. That keeps Joomla's page cache working and means no visitor is profiled on the server. Nothing is stored except the dismiss cookie, and only when the visitor closes the bar.

 
## Styling

 The bar uses the class `dpl-suggest` with a link and a button inside it. The built-in style is deliberately weak, so a rule in your template stylesheet overrides it:

 
```
.dpl-suggest { background: #003366; font-size: 1.1rem; }
.dpl-suggest a { color: #ffd700; }```


[Read more...](https://www.joomill-extensions.com/domainperlanguage/language-suggestion.md)

## A robots.txt per domain (PRO)

A multilingual site on several domains has one `robots.txt` file, and that file can only name one sitemap and one set of rules. The PRO edition serves a separate robots.txt on every domain, straight from the plugin.

 
## Filling it in

 Every domain row has a **robots.txt** field. It starts with the content of Joomla's own `robots.txt.dist`, so you can add to it instead of writing it from scratch. Add the sitemap of that domain yourself:

 
```
User-agent: *
Disallow: /administrator/
Disallow: /tmp/

Sitemap: https://www.example.nl/sitemap.xml```

 Leave the field empty to let Joomla handle robots.txt for that domain as it normally would.

 
## How it is served

 A request for `/robots.txt` on a configured domain is answered by the plugin as `text/plain`, with exactly what you typed. Nothing is added and no placeholder is replaced, so what you see in the field is what a crawler gets.

 
## Two conditions

 
- **No robots.txt file in the root of your site.** The web server serves an existing file itself and Joomla never sees the request. A fresh Joomla ships `robots.txt.dist`, not `robots.txt`, so this is usually fine; if a file is there, the plugin warns you on its settings screen. Rename or remove it.
- **URL rewriting must send missing files to Joomla.** The `.htaccess` of Joomla does this out of the box, and a normal nginx configuration with `try_files $uri $uri/ /index.php?$args` does too.

 
## Checking the result

 Open `https://www.example.nl/robots.txt` and `https://www.example.com/robots.txt` in a browser: each should show its own content. The domain health check does the same automatically and tells you when a domain serves something else than what you filled in.


[Read more...](https://www.joomill-extensions.com/domainperlanguage/robots-txt-per-domain.md)

## Setting up your domains

The plugin decides which language belongs to which domain. Everything that makes a domain reach your site at all happens outside Joomla. This article lists what to arrange, in the order that works.

 
## 1. Point the domains at the same server

 Every domain needs an A record (and an AAAA record if you serve IPv6) pointing at the server that runs your Joomla site. Nothing else: no forwarding, no parking page.

 
## 2. Add the domains to the same website

 All domains must be served by the same Joomla installation, so they belong in one virtual host or one website entry in your hosting panel. Add both the www and the bare variant of every domain: the plugin sends visitors from the variant you did not configure to the one you did, but only if the request reaches Joomla in the first place.

 
## 3. Arrange certificates

 Every domain, and every www variant, needs a valid TLS certificate. With Let's Encrypt that is one certificate with several names, or one certificate per domain; both are fine. The domain health check in the PRO edition warns you two weeks before a certificate expires.

 
## 4. Set up the languages in Joomla

 Domain per Language builds on Joomla's own multilingual setup, which has to be complete before a domain means anything:

 
- install the site language for every language (**System > Install > Languages**);
- publish a content language per language (**System > Content Languages**);
- give every language its own menu with its own default home page;
- enable **System - Language Filter**, with *Item Associations* on;
- associate your menu items and articles across languages, so the language switcher and hreflang know which page belongs to which.

 Joomla's **Multilingual Status** button in the top bar of the administrator tells you what is still missing.

 
## 5. Fill in the plugin

 Add one row per language with its domain, save, and check the result on the front end. In the PRO edition, press *Check domains* afterwards: it tells you per domain whether it resolves, whether it reaches this site over HTTPS, whether the certificate is valid, whether http moves to https, and whether the www variant redirects.

 
## A note on the Site Domain field

 If you use the core **System - SEF** plugin with its *Site Domain* field filled in, leave it as it is. Domain per Language corrects the canonical link it produces, so every page keeps a canonical on its own language domain.

 
## Moving an existing site

 A site that was live with language codes keeps working: the old URL with the code redirects to the new one on its own domain, permanently, so search engines follow along. Give the new domains time to be crawled, and check in Search Console that every domain is verified as its own property.


[Read more...](https://www.joomill-extensions.com/domainperlanguage/setting-up-your-domains.md)

## How it works

Domain per Language sits between the domain a visitor arrives on and Joomla's own multilingual routing. This article describes what it does with a request, with a link, and with the tags search engines read.

 
## The language comes from the domain

 On a configured domain the plugin sets the language of the request before the language filter runs. That order matters: without it, Joomla would pick the language from a cookie, the session or the browser, and a visitor who once chose Dutch would keep seeing Dutch on your English domain.

 A page in that language is then served without its language code. A language that has no domain of its own keeps its code and is served on the domain of the default site language.

 
## Redirects

 All of these are permanent redirects (301), and none of them happen on a POST request:

 
- `www.example.com/en/about` on the English domain becomes `www.example.com/about` - the language code of the domain's own language is duplicate content.
- A page in another language moves to the domain of that language, so `www.example.com/nl/over-ons` becomes `www.example.nl/over-ons`.
- The www or bare variant of a configured domain moves to the configured one, keeping path and query.

 A host that is not configured is left completely alone. Your staging domain, `localhost` or an IP address therefore behaves like an ordinary multilingual Joomla site, with language codes in the URL.

 
## Links between domains

 Joomla builds links with its own router, which always keeps the host of the current request. A link to another language therefore leaves the router with a marker in its path, and the plugin turns that marker into a real absolute URL before the page reaches the visitor. You never see it; what you get is `https://www.example.nl/over-ons` in the menu, in the language switcher, in hreflang and in feeds.

 Three layers make sure a marker never reaches a visitor: the rewrite of the finished page, a redirect for a marker URL that arrives anyway (for example from an e-mail sent by a scheduled task), and a public method other extensions can call. See the article about links in other extensions.

 
## hreflang, x-default and the canonical

 The alternate links that the language filter adds are rewritten to the domain of each language, x-default included, so search engines see one clean set of URLs per page.

 The core **System - SEF** plugin has a *Site Domain* field. When it is filled in, that plugin puts the canonical of every page on that single domain, which is wrong as soon as a page lives on another language domain. Domain per Language moves such a canonical back to the domain of the page's language; only the host changes. On a host that is not configured, the behaviour of the SEF plugin is left untouched.

 
## Caching

 The plugin finishes its work before a page is stored in the cache, so a cached page already carries the right URLs. Joomla caches per full URL including the host, so each domain has its own cache entries.


[Read more...](https://www.joomill-extensions.com/domainperlanguage/how-it-works.md)

## Configuration

All settings live in the plugin itself: **System > Manage > Plugins**, search for *Domain per Language*.

 
## Domains

 The heart of the plugin is one row per language:

 
- **Language** - a published content language of your site. Every language may appear once.
- **Domain** - the host name only, for example `www.example.com`. A scheme, path, port or trailing dot is removed when you save, and an international domain name is stored in its punycode form. Every domain may appear once.
- **robots.txt** (PRO) - the robots.txt this domain serves. See the separate article.

 Saving is refused, with a message naming the row, when a domain is invalid or when a language or a domain is used twice.

 The domain of a language is where that language is served without its language code: `www.example.com/about` instead of `www.example.com/en/about`.

 
## Languages without a domain

 You do not have to give every language its own domain. A language without a domain stays available with its language code on the domain of the default site language, so `www.example.com/de/ueber-uns`. That makes it easy to grow: give a language its own domain the day it deserves one.

 
## PRO settings

 
- **Language suggestion** - shows visitors whose browser prefers another language a bar with a link to the same page on the domain of that language. Off by default.
- **Stay logged in across domains** - a visitor who logs in on one language domain is logged in on the others as well. Off by default.
- **Domain health check** - the *Check domains* button that tests every saved domain. It checks what is saved, so save your changes first.

 In the free edition these three appear as a PRO badge, together with a short description of what they do.

 
## Warnings on the settings screen

 The plugin checks your site when you open its settings and warns, without blocking anything, when:

 
- the **System - Language Filter** plugin is disabled - Domain per Language then does nothing at all;
- Search Engine Friendly URLs are switched off - the plugin only works with them on;
- (PRO) a physical `robots.txt` file sits in the root of your site, which stops the robots.txt per domain from ever being used.


[Read more...](https://www.joomill-extensions.com/domainperlanguage/configuration.md)

## Installation

## Requirements

 
- Joomla 6.0 or later
- PHP 8.3 or later
- A multilingual Joomla site: a published content language for every domain, with the **System - Language Filter** plugin enabled
- Search Engine Friendly URLs switched on (**System > Global Configuration > Site**)
- One domain name per language, all pointing at this one Joomla installation

 
## Installing the plugin

 Domain per Language is a single plugin: `plg_system_domainperlanguage_vX.X.X.zip` for the free edition, `plg_system_domainperlanguage_vX.X.X_PRO.zip` for PRO. Install it under **System > Install > Extensions > Upload Package File**. The installer enables the plugin for you.

 A freshly installed plugin does nothing until you add at least one domain: without configured domains it stays completely inactive, and your site keeps its language codes in the URL.

 You find the settings back under **System > Manage > Plugins**, search for *Domain per Language*.

 
## Updates

 Updates arrive through the Joomla updater (**System > Update > Extensions**). For the PRO edition, enter your Download Key under **System > Update > Update Sites > System - Joomill Domain per Language PRO plugin**; the free edition needs no key. Installing a newer version over an existing one keeps your configured domains.

 Moving from free to PRO is an ordinary install: upload the PRO package over the free one. Your domains stay as they are, and the PRO settings appear in the plugin.

 
## Uninstalling

 Uninstall under **System > Manage > Extensions**. The PRO edition also drops its own database table, which only holds short-lived login tokens. Your content, languages and menus are untouched.

 After uninstalling, every language is served again with its language code on whichever domain the visitor uses. Keep that in mind for search engines: the URLs that were indexed per domain no longer exist, so plan a set of redirects if the site was live.


[Read more...](https://www.joomill-extensions.com/domainperlanguage/installation.md)

