Introduction
DNSLink is the specification of a format for DNS TXT records that allows the association of arbitrary content paths and identifiers with a domain.
DNSLink leverages the powerful distributed architecture of DNS for a variety of systems that require internet scale mutable names or pointers.

Try it out
How does it work?
A DNSLink record is a specifically formatted TXT record that allows you to link resources to that domain. It follows RFC 1464, which defines a structured format of TXT records as <key>=<value>:
dnslink=<value>
When you register a domain at your name registrar you can set TXT records additionally to A, MX or CNAME records.
Using just a DNS client, anyone can quickly resolve DNSLink, discover available records, and request the resource through linked protocols.
Here is an example for a valid DNSLink record pointing at content root on IPFS:
dnslink=/ipfs/bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze
Looking up DNSLink records
You can use any DNS resolution tool, such as dig, to lookup TXT records for a domain.
> dig +short TXT _dnslink.en.wikipedia-on-ipfs.org
"dnslink=/ipfs/bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze"
Tips
MS Windows user?
dig.exeis part of the ISC'sbindpackageGeneric DNS tool like
digwill show allTXTrecords for a domain. To make things easier, you can use thednslinkcommand-line client provided with either the JavaScript or Golang package. Either will list only the valid DNSLinkTXTrecords.> dnslink en.wikipedia-on-ipfs.org /ipfs/QmaCveU7uyVKUSczmiCc85N7jtdZuBoKMrmcHbnnP26DCx
Specification
Record Format
DNSLink records are of the form:
dnslink=/<namespace>/<identifier>
- The prefix
dnslink=signals that theTXTrecord in question is a DNSLink. This is important asTXTrecords are used for many purposes that DNSLink should not interfere with. - Starting with
/<namespace>/ensures multiple links can coexist.- This prefix allows clients to ignore records linking to unsupported protocols or addressing systems.
- Implementations should never error on invalid record. Simply ignore and move to the next one.
- The remainder after the second
/is anidentifierspecific to thenamespace.
DNSLink Record Example
In the example below, the namespace is ipfs and identifier is the IPFS CID bafy...cjze.
dnslink=/ipfs/bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze
_dnslink subdomain
DNSLink records MUST be specified on the _dnslink subdomain instead of defining them directly on the domain:
> dig +short TXT _dnslink.example.com
dnslink=/ipfs/bafy...abcd
This allows for secure DNSLink management and CNAME delegation.
DNSLink implementations automatically lookup the _dnslink subdomain behind the scenes:
> dnslink example.com
/ipfs/bafy...abcd
Multiple records
Multiple dnslink= TXT records per domain
DNSLink is a general-purpose protocol. It is not tied to any specific namespace. It can point at content-addressed data on /ipfs/, /hyper/, etc.
Therefore you are free to specify more than one dnslink=/<namespace>/<identifier> TXT record per domain:
> dig +short TXT _dnslink.example.com
"dnslink=/ipfs/.."
"dnslink=/hyper/.."
"dnslink=/foo/.."
Multiple values per namespace
Publishing more than one record for the same namespace is allowed by DNS clients and DNSLink libs and this specification, but system responsible for processing those records is free to reject such state as invalid, if it makes no sense for a specific namespace.
Sorting
DNS specification states that TXT records do not need to arrive at the client in order. It is suggested to sort DNSLink records in lexicographical order before they get evaluated. This ensures deterministic results when more than one record is present,
Redirects and delegation
CNAME delegation
Use of CNAME is the suggested way for delegating DNSLink resolution from one DNS name to another.
Example below delegates DNSLink record resolution for example.com to TXT records at _dnslink.external-service.example.net
> dig +noall +answer TXT _dnslink.example.com
_dnslink.dnslink.dev. 1612 IN CNAME _dnslink.external-service.example.net
_dnslink.external-service.example.net. 112 IN TXT "dnslink=/ipfs/bafkqae2xmvwgg33nmuqhi3zajfiemuzahiwss"
Standard DNS chaining rules apply, as noted in RFC 1034:
Of course, by the robustness principle, domain software should not fail when presented with CNAME chains or loops; CNAME chains should be followed and CNAME loops signalled as an error.
TTL of the TXT record
The default TTL for DNS records is usually 3600, which is 1 hour. We recommend setting a low TTL in the TXT record, something like 60 seconds or so. This makes it so you can update your name quickly, and use it for website deploys.
Tutorial
Prefer learning by doing? Follow the below tutorial.
It covers DNSLink creation and resolution using popular command-line tools.
Step 0: Find something to link to
In this tutorial, we'll link to the libp2p website, on IPFS. At the time of writing this, the website had the IPFS address:
/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU
You can view this on one of public gateways: dweb.link/ipfs/Qmc2...ZDmU
And even download it with Kubo ipfs CLI:
ipfs get /ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU
Step 1: Choose a domain name to link from
For this tutorial, I'll use the domain name libp2p.io. You can use whatever domain you control. I happen to be using an ALIAS record on this domain as well (to make the website load directly via the IPFS gateway), but that is not a problem: the convention to prefix the TXT record domain with _dnslink. allows for complex DNS setups like this.
So the full domain name I'll set the record on is: _dnslink.libp2p.io.
Step 2: Set the DNSLink value on a TXT record
Let's set the link by creating a TXT record on the domain name. This is going to be specific to your DNS tooling. Unfortunately there is no standard cli tool or web service to set domain names 🙁.
Every registrar and name server has its own web app, and API tooling. Refer to their respective documentations and tutorials.
The gist is to create a new TXT record on _dnslink.libp2p.io with the value:
dnslink=/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU
To illustrate, setting the record via an imaginary DNS CLI tool could look like this:
> my-dns-tool set \
--type=TXT \
--ttl=60 \
--domain=libp2p.io \
--name=_dnslink \
--value="dnslink=/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU"
_dnslink.libp2p.io TXT 60 dnslink=/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU
Setting it via web interface could look similar to this one:

Step 3: Resolve the DNSLink
Now, let's try resolving the link, and reading the data behind it using namespace/protocol-specific tool.
You can get the link value manually using dig or another DNS lookup tool:
> dig +short TXT _dnslink.libp2p.io
dnslink=/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU
Extract the value with sed:
> dig +short TXT _dnslink.libp2p.io | sed -E 's/"dnslink=(.*)"/\1/g'
/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU
Then, you can feed the output of that to software that understands IPFS paths.
Here, we use ipfs CLI from Kubo to list the contents of a directory identified by CID Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU:
> ipfs ls /ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU
QmeotoX2bE7fVgMvUS9ZXL2RMoZQHiuQVZZR1Hts3JVmUT 265 _previous-versions
QmP5JpytsFEQ5Y8oQ875kPrdA1dAtyXAH6U8eKbTUbptNd - bundles/
QmcRUrMyFePBNnvoqZB5Uk7y6aoWGoUW6EB8JWUxztKTma - categories/
QmeVM9YZStiFcjjQYpzJ1KWJsaFGcRWaeMAymSfLydu9mh - css/
QmRJjyE1Bi64AD7MSpt4xKHaiWXq7WGAne8KTzn7UyYeWq - fonts/
Qma6Eg1JMAPjBg827ywDG1nx4TBwxYWxQxeb1CXUkDnrHk - img/
QmdB9xXJHNXnaiikCXVpomHriNGXwvSUqdkC1krtFq4WWW - implementations/
QmXCq4KMZC4mdxovpnrHU9K92LVBLSExLEsrvwTGNEswnv 62880 index.html
QmQNjTorGWRTqEwctqmdBfuBBRTj8vQD3iGjNNCu7vA5i9 3138 index.xml
QmPsosZeKZTUcBkcucPtPnk3fF4ia4vBdJ6str9CRr6VTQ - js/
QmYBUY8Y8uXEAPJSvMTdpfGoL8bujNo4RKoxkCnnKXoTD9 - media/
QmUZ23DEtL3aUFaLgCEQv5yEDigGP2ajioXPVZZ6S7DYVa 561 sitemap.xml
QmRgig5gnP8XJ16PWJW8qdjvayY7kQHaJTPfYWPSe2BAyN - tags/
Or chain it all together:
> dig +short TXT _dnslink.libp2p.io | sed -E 's/"dnslink=(.*)"/\1/g' | ipfs ls
QmeotoX2bE7fVgMvUS9ZXL2RMoZQHiuQVZZR1Hts3JVmUT 265 _previous-versions
QmP5JpytsFEQ5Y8oQ875kPrdA1dAtyXAH6U8eKbTUbptNd - bundles/
QmcRUrMyFePBNnvoqZB5Uk7y6aoWGoUW6EB8JWUxztKTma - categories/
QmeVM9YZStiFcjjQYpzJ1KWJsaFGcRWaeMAymSfLydu9mh - css/
QmRJjyE1Bi64AD7MSpt4xKHaiWXq7WGAne8KTzn7UyYeWq - fonts/
Qma6Eg1JMAPjBg827ywDG1nx4TBwxYWxQxeb1CXUkDnrHk - img/
QmdB9xXJHNXnaiikCXVpomHriNGXwvSUqdkC1krtFq4WWW - implementations/
QmXCq4KMZC4mdxovpnrHU9K92LVBLSExLEsrvwTGNEswnv 62880 index.html
QmQNjTorGWRTqEwctqmdBfuBBRTj8vQD3iGjNNCu7vA5i9 3138 index.xml
QmPsosZeKZTUcBkcucPtPnk3fF4ia4vBdJ6str9CRr6VTQ - js/
QmYBUY8Y8uXEAPJSvMTdpfGoL8bujNo4RKoxkCnnKXoTD9 - media/
QmUZ23DEtL3aUFaLgCEQv5yEDigGP2ajioXPVZZ6S7DYVa 561 sitemap.xml
QmRgig5gnP8XJ16PWJW8qdjvayY7kQHaJTPfYWPSe2BAyN - tags/
TIP
Kubo IPFS CLI has DNSLink resolution built in though, so you could just do the following to achieve the same result as in the tutorial above:
> ipfs resolve /ipns/libp2p.io
/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU
Usage Examples
Below are some examples of DNSLink being used in real world.
Robust Website Hosting
It is a good idea to publish DNSLink records with identifiers of website content addressed with non-HTTP protocols (e.g., IPFS). This improves resiliency, and enables user agents to opportunistically upgrade to more modern and decentralized transports and routing protocols.
- IPFS Gateways support resolving DNSLinks automatically. See public gateway list.
- Install Kubo daemon and follow gateway recipes
- Or use third-party service such as Cloudflare or Fleek
- The IPFS Companion browser extension works with Chromium and Firefox. It can resolve DNSLinks automatically and load the content with help of a local IPFS gateway. It will also recover from HTTP server being down, and load content over IPFS, even when redirect is disabled by default. See more in the IPFS Companion documentation.
Website Examples
Many websites use DNSLink and IPFS. Some examples:
- https://ipfs.tech
- https://libp2p.io
- https://ipld.io
- https://multiformats.io
- https://ipfs.kiwix.org (https://github.com/ipfs/distributed-wikipedia-mirror)
In IPFS Ecosystem
Many IPFS implementations (e.g., Kubo) come with built-in supports resolving DNSLink names on /ipns/ namespace:
> ipfs resolve /ipns/libp2p.io
/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU
You can find out more at the IPFS DNSLink documentation.
IPFS Gateway
IPFS gateways resolve DNSLink automatically for:
- Requests for
/ipns/example.comcontent paths. Check it out at https://ipfs.io/ipns/libp2p.io or https://dweb.link/ipns/libp2p.io (the latter will provide proper origin isolation for use in browsers). - Requests with
Host: example.comHTTP header that includes domain with a valid DNSLink. For example, https://en.wikipedia-on-ipfs.org is backed by an IPFS gateway.
How does that work?
The gateway takes the part after /ipns/, if it is a DNS domain name, it checks for a DNSLink at either the domain name, or _dnslink. prefixed version. In this case it finds our DNSLink at _dnslink.libp2p.io and resolves that.
But what about https://libp2p.io?
Yes, libp2p also works, that uses a combination of DNSLink, a ALIAS record in libp2p.io, and the IPFS gateway reading domain from the Host header sent with HTTP request.
Basically:
The browser first checks for
Arecords forlibp2p.io. DNS finds anALIAStogateway-int.ipfs.io, and thoseArecords:> dig A libp2p.io gateway.ipfs.io. 119 IN A 209.94.90.1The browser then connects to HTTP server at
209.94.90.1, using aHost: libp2p.ioHTTP header.The IPFS gateway reads the
HOST: libp2p.ioheader, and -- recognizing a DNS name -- checks for an associated DNSLink at eitherlibp2p.ioor_dnslink.libp2p.io.> dig +short TXT _dnslink.libp2p.io "dnslink=/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU"The gateway finds the link at
_dnslink.libp2p.ioleading to/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU.The gateway fetches the IPFS web content at
/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmUand serves it to the browser.The browser renders it happily, preserving the original pretty name of
https://libp2p.io
IPFS Companion
Similar to the IPFS Gateway, IPFS Companion browser extension uses DNSLink natively within the browser to resolve IPFS web content. IPFS Companion has a feature that tests domain names for the presence of dnslink TXT records, and if it finds them, then it serves content via a local IPFS gateway instead.
You can find out more about how it works at IPFS Companion's DNSLink documentation.
Libraries
DNSLink protocol is simple enough to use standard DNS libraries, however, more advanced tooling for testing and debugging exists.
There are currently reference implementations for two programming languages available:
- Golang: dnslink-std/go
- JavaScript: @dnslink/js
Both are tested using the universal DNSLink test harness.
External Resources
- IPFS and DNSLink
- IPFS Companion and DNSLink
- DNSLink support at Cloudflare
- DNSLink support at Fleek
- Youtube: Explanation of how DNSLink and the IPFS Gateway works
FAQ
Is there an IETF RFC spec?
Not yet. We should write one.
Can I use DNSLink in non-DNS systems?
Yes absolutely. For example, you can use DNSLink to resolve names from alternative systems such as Ethereum Name System (ENS). An example of such "DNS Gateway" can be found at https://github.com/wealdtech/coredns-ens/.
Hey!
If you use DNSLink for something cool like this, be sure to add it to the Usage Examples section of this doc.
Why use _dnslink.domain instead of domain?
_dnslink.domain does not conflict with CNAME and ALIAS records, and is better for operations security: enables you to set up an automated publishing or delegate control over your DNSLink records to a third party without giving away full control over the original DNS zone.
TXT record on domain is deprecated and resolved by some software only for legacy reasons: we historically started with supporting records on the normal domain, and only found the issue with CNAME and ALIAS records later on. Those problems are absent when _dnslink.domain is used.
Rule of thumb: always use _dnslink.domain.
Why use the dnslink= prefix in the TXT value?
The prefix dnslink= is there to signal that this TXT record value is a DNSLink. This is important because many systems use TXT records, and there is a convention of storing multiple space separated values in a single TXT record. Following this format allows your DNSLink resolver to parse through whatever is in the TXT record and use the first record prefixed with dnslink=.
Why not use other DNS record types, like SRV?
Special purpose records enforce some structure to their values, and these are not flexible enough. We wanted a simple protocol that allowed us to link to any other system or service, in perpetuity. This can only be achieved with systems designed to allow structure within the input. The TXT record is a good example of this -- it imposes a string value, but it allows the user to make sense out of anything within that string. Sets of users can impose different rules for resolution.
In our case, this is handled through Multiaddr.
Why not just extend DNS with more record types?
It seems that what DNSLink does could be accomplished by a verity of new special purpose record types. But extending DNS is a very expensive process -- in terms of time and adoption. We wanted to find a way to use all the existing infrastructure without requiring any changes at all.
Why DNS?
The Domain Name System (DNS) is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network. ... By providing a worldwide, distributed directory service, the Domain Name System has been an essential component of the functionality of the Internet since 1985.
DNS is:
- somewhat decentralized - it delegates naming authority to nations and organizations world-wide
- hierarchical - it achieves scalable naming via name space partitioning
- scalable - it is the dominant name system on the planet -- most internet traffic uses it
- ubiquitous - as the dominant internet name system since 1985, DNS is well-known and well-supported.
As a name system, DNS has pragmatic solutions for:
- control of names
- scalable naming
- scalable reads and writes
- partition tolerance
- and more
Of course, DNS has shortcomings:
- somewhat centralized - central authorities manage DNS roots (ICANN, etc.)
- censorable - DNS is easy to censor, at the registrar and ISP levels
- temporary - DNS names are rented, not really owned, and can be revoked and seized
- not offline-first - DNS does not have good solutions for values to spread through intermittently connected networks
- cumbersome - buying, registering, and setting values on a name is more complicated than acquiring a mutable pointer should be.
- not program friendly - registering DNS names requires being a human or a human organization with human agents, with access to credit cards. this isn't suitable for lots of programs.
These are all good reasons to develop new, better name systems. But until those work better than DNS, you'll probably use DNS.
Community
See resources at dnslink-std/community.
Contributors
- @jbenet
- @whyrusleeping
- @lgierth
- @victorb
- @diasdavid
- @martinheidegger
- @lidel
- and undoubtedly many more
Developed with the support of Protocol Labs