Specification
The specification can be considered stable, but slight changes might happen in the future.
General Requirements
TXT record
- Record must not exceed 255 characters
- Record key-value pairs must be delimited by semicolons “;”
- Key and value must be encoded using utf8
- Punycode for domain names should be used
- Ordering key-value pairs can be done
- Arbitrary data/non key-value pairs can be used and will be ignored
Location/Zone
- TXT record must be accessible under the subdomain “_redirect”
URLs
- All URLs must be encoded
- “;” must be escaped as “%3B”
Examples:
? -> %3F
= -> %3D
https://example.com/page/about=us -> https://example.com/page/about%3Dus
Further links about URL encoding:
https://en.wikipedia.org/wiki/Percent-encoding
https://tools.ietf.org/html/rfc3986#page-11
Multipart Records
TXT records that are longer than 255 characters should be split into multiple parts. As mentioned in RFC4408, “A single text DNS record can be composed of more than one string. If a published record contains multiple strings, then the record MUST be treated as if those strings are concatenated together without adding spaces”. Example:
_redirect.multipart.example.com 3600 IN TXT "v=txtv0;type=host" "code=301" "to=https://txtdirect.org"
Cache Headers
TXTDirect adds an Cache-Control
header with duration of 1 week by default
to all 301
redirects.
Since a 301
status code is for permanent redirects, most clients might
not refresh their cache without specifying an Cache-Control
header on 301
redirects.
It's recommended to always add a Cache-Control
header with at least a duration of a day
or a week to all 301
redirects.
Host Type
Description
host
is the most simple type. Using host
you can redirect an incoming request to a specific endpoint with a custom status code defined in the TXT record.
For example records you can check host.
Record fields
Type
- Key: type
- Mandatory
- Permitted values: “host”
- Example: “type=host”
Version
- Key: v
- Mandatory
- Permitted values: “txtv0”
- Example: “v=txtv0”
Redirect URL
- Key: to
- Recommended
- Default: Fallback to www or redirect config
- Permitted values: “absolute|relative URL”
- Example: “to=https://example.com”
- Example: “to=/example/”
Redirect Code
- Key: code
- Optional
- Default: “302”
- Permitted values: “301|302”
- Example: “code=301”
Path Type
Description
Sometimes you may need to redirect a request coming into a single host/domain to separate locations based on the request's path. One common use-case is a URL shortener.
path
type extracts each directory by default. Each directory is used to generate a new sub zone to fetch the TXT record from. To reorder the default order you can use the from=
field.
path
also contains some additional configuration to process the request's path. The re=
field enables use of a custom regex to control which data is used to construct the sub zones, where TXT record data is fetched.
path
uses \/([A-Za-z0-9-._~!$'()*+,;=:@]+)
as the default regex to parse
the request's path. The matches from this regex would get used to construct the sub zones
and they're also available as numbered regex placeholders.
Path Sanitization
To use the request's path for generating the zone address it needs to be normalized into allowed DNS chars. For example .
(dot) is replaced with -
(dash) to keep each match in its own zone. You can visit RFC1034 for more information about the allowed chars.
The request's path is used to construct the zone address, which provides the redirect data used for this specific path. By default each directory is extracted and the order reverted meaning the less specific zone will come first:
example.com/firstDirectory/secondDirectory/ -> secondDirectory.firstDirectory.example.com
A custom ordering can be achieved by using the from=
field. It will reorder the sub zones accordingly.
If a custom regex is configured with the re=
field each match is used as a sub zone. Ordering can be modified by using named matches. The default ordering will use the first match as the first sub zone and continue until there are no new matches.
Wildcards
If a specific record is non-existent it will fallback to a wildcard record, if available.
Wildcard records can be set using _
such as _redirect._.path.example.com
.
For non-existent zones or catch-all use-cases a general wildcard can be used.
For example look at these sample records:
_redirect.path.example.test. IN TXT "v=txtv0;type=path;code=302;to=https://about.txtdirect.org/docs/specification/#path-type"
_redirect._.path.example.test. IN TXT "v=txtv0;type=host;code=302;to=https://about.txtdirect.org/docs/specification/#wildcards"
If the incoming request's path isn't empty it will get redirected to wildcard record's to=
field. But if the request doesn't have a path, it gets redirected to the first record's to=
field.
https://path.example.test -> https://about.txtdirect.org/docs/specification/#path-type
https://path.example.test/wildcards -> https://about.txtdirect.org/docs/specification/#wildcards
Multi-level wildcards
Wildcard records can have more than just one placeholder and be multi-level. By default the incoming request's path would get reversed and then used to generate a zone address. For example if the request's path is /first/second
the resulting zone address would be second.first.host.tld
.
The order can be custimzed using a simplified regex aka the from=
field. For example look at these sample records:
_redirect.path.example.test. IN TXT "v=txtv0;type=path;code=302;from=/$2/$1;to=https://parent.example.test"
_redirect._.first.path.example.test. IN TXT "v=txtv0;type=host;code=302;to=https://first.example.test"
_redirect._.second.path.example.test. IN TXT "v=txtv0;type=host;code=302;to=https://second.example.test"
_redirect._._.path.example.test. IN TXT "v=txtv0;type=host;code=302;to=https://nothing.example.test"
Now if the incoming request's path is /first/second
it would use the _redirect._.second.path.example.test
record and get redirected to https://second.example.test
. That's because we used the /$2/$1
regex for the parent path record's from=
field. Also if the request's path is /second/first
it would use the _redirect._.first.path.example.test
record.
If the request's path is neither of those cases, it would then use the last wildcard record. So if the request's path for example is /not/available
, it would use the _redirect._._.path.example.test
record and get redirected to https://nothing.example.test
.
Record Fields
Type
- Key: type
- Mandatory
- Permitted values: “path”
- Example: “type=path”
Version
- Key: v
- Mandatory
- Permitted values: “txtv0”
- Example: “v=txtv0”
Redirect URL
- Key: to
- Recommended
- Default: Fallback to www or redirect config
- Permitted values: “absolute|relative URL”
- Example: “to=https://example.com”
- Example: “to=/example/”
Root Redirect URL
- Key: root
- Recommended
- Default: Fallback to www or redirect config
- Permitted values: “absolute|relative URL”
- Example: “to=https://example.com”
- Example: “to=/example/”
Simplified Regex
- Key: from
- Permitted values: “simplified regex”
- Example: “from=/$2/$1/$3”
Regex
- Key: re
- Permitted values: “regex/ordered regex”
- Note: “Named regexes can be used to reorder the results. a > b > b1 > b2”
- Example: “
re=\\?query=(?P<a>[^&]+)\\&more=(?P<b>[^&]+)
”
Dockerv2 Type
Description
The dockerv2
type enables vanity URLs for your container images. Using your own URl for container images enables easier switching between your backend infrastructure/storage and adds more control.
The type implements the Docker registry API v2 and redirects requests for both metadata and image blobs to the backend storage. All data besides the redirect is therefore served by your underlying infrastructure such as Google Container Registry (gcr.io).
The upstream endpoint used for to=
can be different depending on the use case.
- Image link including a specific tag enables serving this specific tag under various image names and tags.
- Image link enables serving available tags for a specific image under various image names.
- Referencing the root of a container registry enables the full serving of the registry under this specific domain.
Record Fields
Type
- Key: type
- Mandatory
- Permitted values: “dockerv2”
- Example: “type=dockerv2”
Version
- Key: v
- Mandatory
- Permitted values: “txtv0”
- Example: “v=txtv0”
Backend URL
- Key: to
- Mandatory
- Permitted values: “absolute registry URL”
- Note: “For non-container traffic it will fallback to website, www or redirect config”
- Example: “to=https://gcr.io/” pass through namespaces, container and tag
- Example: “to=https://gcr.io/txtdirect/container” pass through tag
- Example: “to=https://gcr.io/txtdirect/container:tag” force specific tag
Website Redirect URL
- Key: website
- Recommended
- Default: Fallback to www or redirect config
- Note: “Non container traffic redirect”
- Permitted values: “absolute|relative URL”
- Example: “to=https://about.txtdirect.org/docs/”
Gometa Type
Description
The gometa
type enables vanity URLs for your Go packages. Using your own URL for packages, enables easier switching between your backend hosting service (GitHub, etc.) and lets you have custom import path for your package, independent of your hosting service. Using gometa
you can switch your hosting service without worrying about impacting downstream for your users.
Using gometa
type you can point to a Go package inside your records using the to=
field and we use that URI to generate a simple HTML page that only contains go-import and go-source (Only for packages served on GitHub) tags for go get
to use it and find your package.
Record fields
Type
- Key: type
- Mandatory
- Permitted values: “gometa”
- Example: “type=gometa”
Version
- Key: v
- Mandatory
- Permitted values: “txtv0”
- Example: “v=txtv0”
Backend URL
- Key: to
- Recommended
- Default: Fallbacks such as www or redirect config
- Permitted values: “absolute repository URL”
- Example: “to=https://github.com/txtdirect/”
- Example: “to=/docs/”
Repository Type
- Key: vcs
- Recommended
- Default: git
- Permitted values: “git|bzr|fossil|hg|svn”
- Example: “to=https://github.com/txtdirect/”
Proxy Type (experimental)
Description
The proxy
type let's you proxy your requests to a specific upstream. It reads the upstream from the record's to=
field and reverse proxies the request.
Type
- Key: type
- Mandatory
- Permitted values: “proxy”
- Example: “type=proxy”
Version
- Key: v
- Mandatory
- Permitted values: “txtv0”
- Example: “v=txtv0”
Upstream URL
- Key: to
- Mandatory
- Permitted values: “absolute URL”
- Example: “to=https://example.com/subpage/”
Wildcard Records
A wildcard DNS record is a record in a DNS zone that will match requests for non-existent domain names. For example you can specify a record that matches all the subdomains that don't have a specific TXT record.
NOTE: Using a CNAME record for the wildcard is not supported and will lead to wrong behaviour
Take a look at these sample records:
; NOTE: Using a CNAME record for the wildcard is not supported and will lead to wrong behaviour
*.example.test. IN A 127.0.0.1
_redirect._.example.test. IN TXT "v=txtv0;type=host;code=302;to=http://wildcard.test"
; NOTE: Specific records can use a CNAME or A/AAAA records.
specific.example.test IN A 127.0.0.1
_redirect.specific.example.test. IN TXT "v=txtv0;type=host;code=302;to=http://specific.test"
All the requests for a given subdomain of example.test
such as test.example.test
would use the wildcard record of the parent zone _redirect._.example.test
.
In the case a more specific record is available it will take be used.
For a specific record to work a specific subdomain needs to be setup as well. This is based on DNS specifics.
With specific.example.test
and _redirect.specific.example.test
set it will be used instead of the above wildcard.
The redirect flow for these records would look like this:
specific.example.test -> http://specific.test
*.example.test -> http://wildcard.test
Custom Headers
Custom headers can be added to the responses with specifing them inside the records. For example to add a HSTS header to the responses you can use the following example:
_redirect.example.test. IN TXT "v=txtv0;type=host;to=http://example.test;>Strict-Transport-Security=>Strict-Transport-Security=max-age%3D163072000%3B%20includeSubDomains%3B%20preload"
You can separate the headers just like the other record fields by using ;
.
Also the custom headers would get added to the records that are chained to a path
record. For example:
"_redirect.example.test.": "v=txtv0;type=path;>Test=TestValue"
"_redirect.second.example.test.": "v=txtv0;type=host;to=https://example.test"
The second.example.test
record would also have the Test
header in this example.
Important Note: Since headers can contain characters that can interrupt the record parser, urlencoding is required for the header values.
Also custom headers will overwrite the previous headers that were added to the responses. like the headers from the Caddyfile.