Persian Domain Names: Punycode, Browser Support, and Homograph Risk

How a Persian domain is converted to Punycode, which browsers support it, and why similar-looking characters create phishing risk. A technical guide with real commands and records.

7 min Updated 25 Oct 2026

If you've typed a domain like مثال.ir into your browser and instead of the site opening you got the ERR_NAME_NOT_RESOLVED error, or in your domain's DNS panel you saw a strange string like xn--mgbh0fb.ir instead of Persian characters, the problem isn't your configuration. You've run into the Persian domain storage layer, which was standardized in RFC 3492 back in 2003 and is still misused by most technical teams.

A Persian domain doesn't exist in DNS. What exists is an ASCII label built from a Unicode string using the Punycode algorithm. Anywhere you deal with a "Persian domain," you're actually dealing with two representations of the same thing: a human-readable representation and a resolver-resolvable representation.

What exactly does Punycode store

Domain labels in DNS are limited to 63 bytes and the full name to 253 bytes. Punycode converts a Unicode string into an ASCII sequence that starts with the xn-- prefix. For example:

مثال.ir    →    xn--mgbh0fb.ir
ایران.ir   →    xn--mgba3a4f16a.ir
تست.ir     →    xn--hgbk6aj7f.ir

This conversion is deterministic. One input always yields one output, and no resolver can interpret it any other way. But the important point is that this conversion happens at the application layer, not in DNS. That is, when you type مثال.ir in the browser, the browser first converts it to xn--mgbh0fb.ir using the IDNA library, and then sends the DNS query.

In the terminal this conversion isn't automatic. If you query a Persian domain directly with dig, depending on the tool version, you may get a response or an error. To be safe, always use the ASCII form:

dig +short xn--mgbh0fb.ir A
dig +short xn--mgbh0fb.ir MX
dig +short xn--mgbh0fb.ir TXT

If you want to see what string a Persian domain converts to, the idn tool on Linux or idn2 on newer GNU versions will do the job:

echo "مثال.ir" | idn2
# xn--mgbh0fb.ir

Why some browsers display the Persian domain raw

Browsers have supported IDN for years, but they don't all behave the same. Chrome and Firefox display the domain in Unicode form, but only if all labels are from a single script and a single language. If a Persian label and a Latin label are mixed, the browser reverts the name to xn-- form to prevent spoofing attacks.

This behavior is intentional. There's an attack called IDN homograph in which the attacker substitutes a character from another alphabet that looks similar to the original character in the font. For example, the Persian letter "ی" and the Arabic letter "ي" render identically in many fonts, but they have different Unicode code points and therefore produce different Punycode. A phishing domain can look exactly like your bank's domain while being somewhere completely different in DNS.

Browsers handle this with an "all or nothing" policy: either the whole name is uniform and it displays Unicode, or it's suspicious and it shows the ASCII form. Here's where people go wrong: many technical teams think that because the browser displays the domain in Persian, it works that way everywhere. Then in the web server log they see the Host header arriving as xn--mgbh0fb.ir, their vhost doesn't match, and the default site is returned. The telltale sign is that the site opens via direct IP but not via the domain, and in access.log the domain name is recorded in ASCII form.

Which form should we use to create the DNS record

Short answer: always the Punycode form. Iranian DNS panels usually accept both and convert them themselves, but foreign panels and APIs often only accept ASCII. If you register the record with Persian characters and the panel performs the conversion incorrectly, the record is stored in the zone file under a name that the resolver will never query.

A healthy zone file for the domain مثال.ir looks like this:

$ORIGIN xn--mgbh0fb.ir.
@       IN  A      185.xx.xx.xx
www     IN  CNAME  xn--mgbh0fb.ir.
@       IN  MX 10  mail.xn--mgbh0fb.ir.
@       IN  TXT    "v=spf1 include:_spf.example.com ~all"

Note that on the right-hand side of the MX record you must also write the Punycode form, otherwise the resolver can't find the mail server. The same rule applies to NS and CNAME records. If you're connecting the domain to a virtual server, Connecting a domain to a virtual server; a complete guide to A records through SSL explains the exact record order and certificate issuance timing.

SSL and Persian domains: where you get stuck

SSL certificates have supported IDN domains since 2008, but only in Punycode form. If you enter the name in Persian in the CSR, Let's Encrypt rejects it with the error Error creating new order :: Cannot issue for "مثال.ir": Domain name contains an invalid character. The correct approach is to always provide the ASCII form in certbot:

certbot certonly --webroot -w /var/www/html \
  -d xn--mgbh0fb.ir -d www.xn--mgbh0fb.ir

After issuance, in nginx you must also write server_name in ASCII form. If you write it in Persian, nginx sees it as a different byte string and the request goes to the default vhost. This is one of the most common reasons for "SSL is installed but the site won't open" on Persian domains.

Comparing the two approaches in registration and management

TopicUnicode form (Persian)Punycode form (ASCII)
Display to userReadable and promotableUnreadable
DNS panel supportVariable, some panels convertWorks everywhere
SSL supportRejectedStandard
Homograph riskHigh, because the user can't see the differenceZero, because the Unicode code point is explicit
Logging and monitoringRequires normalizationDirectly usable

If I had to choose between the two, for anything infrastructure-related I'd choose Punycode. I'd use the Unicode form only in site content and advertising. The reason is simple: every tool that works with domains, from curl to openssl s_client to zone files, ultimately speaks ASCII. If you work with ASCII from the start, you'll never be caught off guard.

The only case where I prefer the Unicode form is a domain designed entirely for branding and Persian organic traffic, where the technical team knows it must normalize everywhere. In that case, a normalization function in the application code that converts every input to Punycode before use is mandatory.

A point I see a lot in practice

In DNS and web server logs, Persian domains always appear in ASCII form. If you're troubleshooting and don't know why a subdomain isn't working, first check which form it's registered under in the zone file. Query dig with the ASCII form, and if you get a response, the problem is on the application side, not DNS. To check records and compare responses from different resolvers, DNS record lookup is the fastest way. And if you're not sure whether the record was published correctly or an old cache is answering, What is DNS propagation and why does it take time? A complete guide to TTL and caching explains the checking order with real TTL numbers.

For .ir domains there's an extra layer: their registration and management is done through IRNIC and requires a user ID. If you're stuck at the registration stage, The complete guide to registering and managing .ir domains with an IRNIC ID covers the steps one by one. And if your domain is close to expiring, Domain renewal; a complete guide to expiration stages and domain rescue pinpoints the rescue window precisely.

One more practical note: if you're hosting on a server with several Persian domains, use server_name in ASCII form in nginx and create a separate block for each domain. Combining several Persian domains in one block with a wildcard works, but if you have dynamic subdomains, Wildcard records and managing dynamic subdomains in DNS clarifies the behavioral difference between catch-all and wildcard. And for hosting the site itself, if you need a stable Linux environment, Linux hosting is an option where nginx and certbot configuration is done without extra hassle.

Frequently asked questions

How is a Persian domain stored in DNS?

A Persian domain is stored in DNS as an ASCII label with the xn-- prefix, built from the Unicode string using the Punycode algorithm. For example, مثال.ir converts to xn--mgbh0fb.ir. Resolvers never see the Unicode form; the conversion happens in the browser or application.

Why does the browser show my Persian domain in xn-- form?

When domain labels come from multiple scripts or multiple languages, the browser reverts the name to ASCII form to prevent visual similarity attacks. This behavior is intentional and isn't a sign of a misconfiguration on your part. If all labels are Persian and uniform, the browser displays the name in readable form.

Can I get an SSL certificate for a Persian domain?

Yes, but only in Punycode form. If you enter the Persian name in the certificate request, the issuing authority rejects it with an invalid character error. In certbot and in nginx's server_name, always write the xn-- form.

What's the main security risk of Persian domains?

The IDN homograph attack; that is, substituting a character with one from another alphabet that looks similar in the font but has a different Unicode code point and therefore different Punycode. The user sees no difference in the address bar and goes to the attacker's domain. That's why working with the ASCII form in logs and configurations is safer.

Was this page helpful?