Fixed XF\Http\Request#isHostLocal returns true for *.dev


Well-known member
Affected version
dev is a public TLD; you can go out and buy [whatever].dev: However, isHostLocal treats .dev domains as local, despite .dev not being a reserved TLD.

Within XF's core and first-party add-ons as of writing, isHostLocal is only ever used in the context of the expression: $request->isSecure() || $request->isHostLocal(). By sheer luck, dev has HSTS enabled for the entire TLD, so this bug is inconsequential. However, if any third-party add-ons use isHostLocal expecting it to do what it claims to do, bugs are likely to arise.

The following TLDs currently appear in isHostLocal:

  • dev: Bad; public TLD
  • localhost: Good; reserved for testing in RFC 2606
  • local: Not great; reserved for use with mDNS in RFC 6762, so it'll never be public, but it's in active use on most home networks and may result in a non-localhost hostname being treated as such. I can't think of any sane scenario in which this would cause an issue within XF, but developers shouldn't be using it as a development TLD (especially if they're on macOS), so there's no reason to include it.
  • test: Good; reserved for testing in RFC 2606

While we're at it, isHostLocal should really cover all of, not just Using alternatives such as is quite common on Linux.

XF Bug Bot

XenForo bug fixer bot
Staff member
Thank you for reporting this issue, it has now been resolved. We are aiming to include any changes that have been made in a future XF release (2.2.5).

Change log:
Adjust Request::isHostLocal to only return true for loopback addresses (and add a further explanation about its intended usage).
There may be a delay before changes are rolled out to the XenForo Community.


XenForo developer
Staff member
To follow up, the changes are actually a bit different. Specifically, this function should only return true for a host/address that is known to be a loopback address. The intended use of this function is generally to allow access to client-side functionality that normally requires a secure connection, such as push notifications or PWA installation. Most browsers are ensuring that loopback addresses are treated as secure, so this function allows us to mimic that.

On that basis, it was matching a wider net than it should. As such, it now matches:
  • localhost
  • [::1]
  • (though technically as a regex since we're just receiving this as a string)
  • The .localhost TLD
Everything else will return false. This roughly matches Chrome's implementation.

Further, there is a note about not using this in security related functions (like bypassing some sort of verification on localhost). The host is browser supplied and whether this is verified/modified by the server is configuration dependent and thus can't be relied on.