(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-).)+[a-zA-Z]{2,63}$)
regex is always going to be at best an approximation for things like this, and rules change over time. the above regex was written with the following in mind and is specific to hostnames-
Hostnames are composed of a series of labels concatenated with dots.
Each label is 1 to 63 characters long, and may contain:
- the ASCII letters a-z (in a case insensitive manner),
- the digits 0-9,
- and the hyphen ('-').
Additionally:
some assumptions:
- TLD is at least 2 characters and only a-z
- we want at least 1 level above TLD
results: valid / invalid
- 911.gov - valid
- 911 - invalid (no TLD)
- a-.com - invalid
- -a.com - invalid
- a.com - valid
- a.66 - invalid
- my_host.com - invalid (undescore)
- typical-hostname33.whatever.co.uk - valid
EDIT:
John Rix provided an alternative hack of the regex to make the specification of a TLD optional:
(?=^.{1,253}$)(^(((?!-)[a-zA-Z0-9-]{1,63}(?<!-))|((?!-)[a-zA-Z0-9-]{1,63}(?<!-).)+[a-zA-Z]{2,63})$)
- 911 - valid
- 911.gov - valid
EDIT 2:
someone asked for a version that works in js.
the reason it doesn't work in js is because js does not support regex look behind.
specifically, the code (?<!-)
- which specifies that the previous character cannot be a hyphen.
anyway, here it is rewritten without the lookbehind - a little uglier but not much
(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9].)+[a-zA-Z]{2,63}$)
you could likewise make a similar replacement on John Rix's version.
EDIT 3: if you want to allow trailing dots - which is technically allowed:
(?=^.{4,253}.?$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-).)+[a-zA-Z]{2,63}.?$)
I wasn't familiar with trailing dot syntax till @ChaimKut pointed them out and I did some research
Using trailing dots however seems to cause somewhat unpredictable results in the various tools I played with so I would be advise some caution.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…