Subdomain Gateways extend [path-gateway] with HTTP Host header support. Below should be read as a delta on top of that spec.
This specification enables isolated website hosting based on root CID-derived Origins, ensures compatibility with native ipfs:// and ipns:// URIs, and aligns with the existing Same-origin security model in web browsers, including relative URL pathing and permission scopes of Web APIs.
Summary:
Host
header rather than as a URL path prefix
{cidv1}.ipfs.example.net
instead of example.net/ipfs/{cid}
/
points at the content root identified by the CIDThe API is a superset of [path-gateway], the differences are documented below.
The main one is that Subdomain Gateway expects CID to be present in the Host
header.
GET /[{path}][?{params}]
Downloads data at specified content path.
path
– optional path to a file or a directory under the content root sent in Host
HTTP headerHEAD /[{path}][?{params}]
Same as GET, but does not return any payload.
Below MUST be implemented in addition to "HTTP Request" of [path-gateway].
Host
(request header)Defines the root that should be prepended to the path
before IPFS content
path resolution is performed.
The value in Host
header must be a valid FQDN with at least three DNS labels:
a case-insensitive content root identifier followed by ipfs
or ipns
namespace, and finally the domain name used by the gateway.
Converting Host
into a content path depends on the nature of requested resource:
/ipfs/{cid}
:
Host: {cid-mbase32}.ipfs.example.net
Host: bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi.ipfs.dweb.link
/ipns/{libp2p-key}
:
Host: {libp2p-key-mbase36}.ipns.example.net
Host: k2k4r8jl0yz8qjgqbmc2cdu5hkqek5rj6flgnlkyywynci20j0iuyfuj.ipns.dweb.link
/ipns/{dnslink-name}
:
Host: {inlined-dnslink-name}.ipns.example.net
.
which means they MUST be inlined into a single DNS label to provide unique origin and work with wildcard TLS certificates.
-
is replaced with --
.
is replaced with -
-
is replaced with .
--
is replaced with -
example.net/ipns/en.wikipedia-on-ipfs.org
→ Host: en-wikipedia--on--ipfs-org.ipns.example.net
Host
header does not include any subdomain, but the requested path is a
valid content path, gateway MUST attempt to
migrate from Path to Subdomain Gateway.Host
,
return HTTP Error 400
Bad Request, as seen in [path-gateway].X-Forwarded-Proto
(request header)Optional. Allows http://
gateway implementation to be deployed behind
reverse proxies that provide TLS (https://
) termination.
Setting X-Forwarded-Proto: https
on reverse proxy informs gateway
implementation that it MUST:
https://
(not http://
)Example (GET with X-Forwarded-Proto: https
):
GET http://dweb.link/ipfs/{cid}
→ HTTP 301 with Location: https://{cid}.ipfs.dweb.link
GET http://dweb.link/ipns/your-dnslink.site.example.com
→ HTTP 301 with Location: https://your--dnslink-site-example-com.ipfs.dweb.link
X-Forwarded-Host
(request header)Optional. Enables Path Gateway requests to be redirected to a Subdomain Gateway on a different domain name.
See also: migrating from Path to Subdomain Gateway.
Example (GET with X-Forwarded-Host: example.com
):
GET https://dweb.link/ipfs/{cid}
→ HTTP 301 with Location: https://{cid}.ipfs.example.com
uri
(request query parameter)Optional. When present, passed address should override regular path routing.
See URI router section for usage and implementation details.
Below MUST be implemented in addition to "HTTP Response" of [path-gateway].
Location
(response header)Below MUST be implemented in addition to Location
requirements defined in [path-gateway].
The Location
HTTP header is returned with 301
Moved Permanently
([path-gateway]) when Host
header does
not follow the subdomain naming convention, but the requested URL path happens
to be a valid /ipfs/{cid}[/{path}][?{query}]
or /ipfs/..
content path.
This redirect allows a subdomain gateway to be used as a drop-in replacement compatible with regular path gateways, as long as the rules below are followed:
{path}
and {query}
parameters, if
present.
_redirects
file, SHOULD only be executed by the subdomain gateway after
the redirect.https://dweb.link/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR
returns HTTP 301 redirect to the same CID but in case-insensitive base32:
Location: https://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi.ipfs.dweb.link/
https://dweb.link/ipns/en.wikipedia-on-ipfs.org
returns HTTP 301 redirect
to subdomain with DNSLink name correctly inlined:
Location: https://en-wikipedia--on--ipfs-org.ipns.dweb.link/
See also: Migrate from Path to Subdomain Gateway.
See: URI router
Subdomain Gateway MUST implement a redirect on paths defined in [path-gateway].
HTTP redirect will route path requests to correct subdomains on the same domain
name, unless X-Forwarded-Host
is present.
NOTE:
During the migration from a path gateway to a subdomain gateway, even though
the Location
header is present, some clients may
check for HTTP 200, and consider other responses as invalid.
It is up to the gateway operator to clearly communicate when such a transition is to happen, or use a different domain name for subdomain gateway to avoid breaking legacy clients that are unable to follow HTTP 301 redirects.
DNS labels, must be case-insensitive, and up to a maximum of 63 characters per label (Section 11 of [rfc2181]). Representing CIDs within these limits requires some care.
Base32 multibase encoding is used for CIDs to ensure case-insensitive, URL safe characters are used.
Base36 multibase is used for ED25519 libp2p keys to get the string representation to safely fit with the 63 character limit.
How to represent CIDs with a string representation greater than 63
characters, such as those for sha2-512
hashes, remains an
open question.
Until a solution is found, subdomain gateway implementations should return HTTP 400 Bad Request for CIDs longer than 63.
Wildcard TLS certificates should be set for *.ipfs.example.net
and
*.ipns.example.net
if a subdomain gateway is to be exposed on the public
internet.
X-Forwarded-Proto
at a
reverse HTTP proxy can be used for preserving https
protocol.Subdomain gateways provide unique origin per content root, however the origins still share the parent domain name used by the gateway. To fully isolate websites from each other:
dweb.link
gateway is listed on PSL as *.dweb.link
https://{content-root-id}.ip[f|n]s.example.net
) and dynamically
append it to internal PSL.Optional uri
query parameter overrides regular path routing.
Subdomain gateway implementations MUST provide URI router for ipfs://
and
ipns://
protocol schemes, allowing external apps to resolve these native
addresses on a gateway.
The /ipfs/?uri=%s
endpoint MUST be compatible with registerProtocolHandler(scheme, url)
,
present in web browsers. The value passed in %s
should be UTF-8 percent-encode.
Example
Given registration:
navigator.registerProtocolHandler('ipfs', 'https://dweb.link/ipfs/?uri=%s', 'IPFS resolver')
navigator.registerProtocolHandler('ipns', 'https://dweb.link/ipns/?uri=%s', 'IPNS resolver')
Opening ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
should produce an HTTP GET request for
https://dweb.link/ipfs/?uri=ipfs%3A%2F%2Fbafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
which in turn should redirect to
https://dweb.link/ipfs/bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
.
From there, regular subdomain gateway logic applies.
Subdomain Gateway implementations SHOULD include _redirects
file
support defined in [web-redirects-file].
We gratefully acknowledge the following individuals for their valuable contributions, ranging from minor suggestions to major insights, which have shaped and improved this specification.