128

Is there a function in PHP to get the name of the subdomain?

In the following example I would like to get the "en" part of the URL:

en.example.com
4
  • 6
    Do you have an URL as string stored in a variable or where is this URL coming from? What is the context? Please elaborate. Commented Mar 13, 2011 at 22:39
  • Couldn't you use a regex that did something like (^|://)(.*)\. and capture the .*? I rather suck at both php and regex, but this comes to mind.
    – corsiKa
    Commented Mar 13, 2011 at 22:50
  • What should it get in en.foo.bar.example.com or en.example.co.uk? Commented Nov 19, 2012 at 12:33
  • parse_url can also help
    – Swapnil
    Commented Aug 28, 2014 at 10:55

29 Answers 29

151

Here's a one line solution:

array_shift((explode('.', $_SERVER['HTTP_HOST'])));

Or using your example:

array_shift((explode('.', 'en.example.com')));

EDIT: Fixed "only variables should be passed by reference" by adding double parenthesis.


EDIT 2: Starting from PHP 5.4 you can simply do:

explode('.', 'en.example.com')[0];
11
  • 18
    Only variables should be passed by reference.
    – Tamás Pap
    Commented Sep 19, 2012 at 7:09
  • 8
    Aren't you able to just do explode(...)[0] instead of using shift these days? Not been PHPing for several years..
    – Tor Valamo
    Commented Nov 9, 2012 at 15:23
  • 1
    Error: Strict Standards: Only variables should be passed by reference.
    – Justin
    Commented Dec 9, 2012 at 5:45
  • 1
    pretty sure you can (explode(...))[0] though, should be operating on the return array instead of the function paranthesis (prior to 5.4) Commented Apr 17, 2014 at 13:07
  • 5
    This solution will not work in case someone types in www.en.example.com and thus will return www as subdomain.
    – lolbas
    Commented Sep 23, 2017 at 6:24
68

Uses the parse_url function.

$url = 'http://en.example.com';

$parsedUrl = parse_url($url);

$host = explode('.', $parsedUrl['host']);

$subdomain = $host[0];
echo $subdomain;

For multiple subdomains

$url = 'http://usa.en.example.com';

$parsedUrl = parse_url($url);

$host = explode('.', $parsedUrl['host']);

$subdomains = array_slice($host, 0, count($host) - 2 );
print_r($subdomains);
7
  • @Mike Lewis - Does this solve the problem of multiple subdomains, such as usa.en.example.com? Just wondering (my own answer doesn't, btw). Commented Mar 13, 2011 at 22:53
  • @Jared, just added a solution to detect multiple subdomains.
    – Mike Lewis
    Commented Mar 13, 2011 at 22:59
  • 1
    @Mike - Will that work with tx.usa.en.example.com? (or science.news.bbc.co.uk)? (btw, that's not a working link, just an example, although news.bbc.co.uk does work) Commented Mar 13, 2011 at 23:02
  • 4
    That works for everything that has a single 'word' TLD like net, com, biz etc. However when dealing with co.uk, for example, it does not. As seen here This is actually a harder problem to solve.
    – Mike Lewis
    Commented Mar 13, 2011 at 23:12
  • 3
    this also fails if there is no subdomain at all.
    – raveren
    Commented Feb 3, 2012 at 13:20
40

You can do this by first getting the domain name (e.g. sub.example.com => example.co.uk) and then use strstr to get the subdomains.

$testArray = array(
    'sub1.sub2.example.co.uk',
    'sub1.example.com',
    'example.com',
    'sub1.sub2.sub3.example.co.uk',
    'sub1.sub2.sub3.example.com',
    'sub1.sub2.example.com'
);

foreach($testArray as $k => $v)
{
    echo $k." => ".extract_subdomains($v)."\n";
}

function extract_domain($domain)
{
    if(preg_match("/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i", $domain, $matches))
    {
        return $matches['domain'];
    } else {
        return $domain;
    }
}

function extract_subdomains($domain)
{
    $subdomains = $domain;
    $domain = extract_domain($subdomains);

    $subdomains = rtrim(strstr($subdomains, $domain, true), '.');

    return $subdomains;
}

Outputs:

0 => sub1.sub2
1 => sub1
2 =>
3 => sub1.sub2.sub3
4 => sub1.sub2.sub3
5 => sub1.sub2
4
  • 2
    This seems the best solution as it also allows for domains without a subdomain, rather than retruning the domain name as the subdomain being the part before the first dot. Very useful for checking the existence of a subdomain.
    – Karl M.W.
    Commented Jul 25, 2013 at 23:51
  • I needed to get the "base" domain (without the subdomain), and I was making my own solution by exploding the host and getting the last elements of the array with a for loop, but I had to check their length (to detect if they were a part of the domain like the "co.uk"). Actually, your solution is far more simple than what I was doing. Regex save lives, thanks!
    – Yoone
    Commented Sep 30, 2013 at 8:26
  • 1
    Awesome.. this works so well for all domain types and subdomains.. nice.
    – jon
    Commented Oct 30, 2013 at 9:58
  • 2
    while this solution is very neat and may work in almost all cases, be aware that domains names might have more than 6 chars, like pvt.k12.ma.us, health.vn or even k12.ak.us. Also, domains names may be use Chinese or Russian character set so the regex part [a-z\.]{2,6} would not match them. Check out here to have example domains name: publicsuffix.org/list
    – pomeh
    Commented Aug 28, 2014 at 8:14
13

http://php.net/parse_url

<?php
  $url = 'http://user:[email protected]/path?argument=value#anchor';
  $array=parse_url($url);
  $array['host']=explode('.', $array['host']);

  echo $array['host'][0]; // returns 'sub'
?>
0
9

As the only reliable source for domain suffixes are the domain registrars, you can't find the subdomain without their knowledge. There is a list with all domain suffixes at https://publicsuffix.org. This site also links to a PHP library: https://github.com/jeremykendall/php-domain-parser.

Please find an example below. I also added the sample for en.test.co.uk which is a domain with a multi suffix (co.uk).

<?php

require_once 'vendor/autoload.php';

$pslManager = new Pdp\PublicSuffixListManager();
$parser = new Pdp\Parser($pslManager->getList());
$host = 'http://en.example.com';
$url = $parser->parseUrl($host);

echo $url->host->subdomain;


$host = 'http://en.test.co.uk';
$url = $parser->parseUrl($host);

echo $url->host->subdomain;
0
7

PHP 7.0: Use the explode function and create a list of all the results.

list($subdomain,$host) = explode('.', $_SERVER["SERVER_NAME"]);

Example: sub.domain.com

echo $subdomain; 

Result: sub

echo $host;

Result: domain

2
  • 3
    You forget TLD's like .co.uk - your snippet will not work with these TLDs Commented Mar 22, 2019 at 8:39
  • This is the best answer for modern PHP.
    – Mecanik
    Commented Nov 29, 2022 at 19:26
6

Simply...

    preg_match('/(?:http[s]*\:\/\/)*(.*?)\.(?=[^\/]*\..{2,5})/i', $url, $match);

Just read $match[1]

Working example

It works perfectly with this list of urls

$url = array(
    'http://www.domain.com', // www
    'http://domain.com', // --nothing--
    'https://domain.com', // --nothing--
    'www.domain.com', // www
    'domain.com', // --nothing--
    'www.domain.com/some/path', // www
    'http://sub.domain.com/domain.com', // sub
    'опубликованному.значения.ua', // опубликованному ;)
    'значения.ua', // --nothing--
    'http://sub-domain.domain.net/domain.net', // sub-domain
    'sub-domain.third-Level_DomaIN.domain.uk.co/domain.net' // sub-domain
);

foreach ($url as $u) {
    preg_match('/(?:http[s]*\:\/\/)*(.*?)\.(?=[^\/]*\..{2,5})/i', $u, $match);
    var_dump($match);
}
6
  • 2
    PS - I don't have any idea of what it's written in the russian text. Just took some casual words from ru.wikipedia.org ;) Commented Aug 28, 2014 at 9:58
  • Isn't it Ukrainian? .ua is the country code for Ukraine.
    – nalply
    Commented Aug 28, 2014 at 13:38
  • Nope. Just mixed information. But I'm not sure, I'm not good enough to distinguish them ;) Commented Aug 28, 2014 at 14:43
  • 3
    In regards to the russian, a google translate from Russian to English comes back as "published values" (in case anyone was curious like I was) Commented Sep 9, 2014 at 14:32
  • @Kamafeather this looks bulletproof. Any way to just get the $match[1] part? $match[0] seems unnecessary.
    – Andres SK
    Commented Mar 27, 2019 at 2:18
5

Simplest and fastest solution.

$sSubDomain = str_replace('.example.com','',$_SERVER['HTTP_HOST']);
2
$REFERRER = $_SERVER['HTTP_REFERER']; // Or other method to get a URL for decomposition

$domain = substr($REFERRER, strpos($REFERRER, '://')+3);
$domain = substr($domain, 0, strpos($domain, '/'));
// This line will return 'en' of 'en.example.com'
$subdomain = substr($domain, 0, strpos($domain, '.')); 
3
  • 1
    There are better ways to auto detect the current host (like $_SERVER['HTTP_HOST']) then relying on a spoof-able referrer header, assuming that's what the general idea behind the answer is.
    – Matthew
    Commented Mar 13, 2011 at 22:42
  • Right, I was using an old piece of code. The example still stands, however. That's not the root of the question. Commented Mar 13, 2011 at 22:43
  • Just to add up on these comments above, relying on $_SERVER['HTTP_HOST'] may not be effiicient, since there's a chance it may not be set.
    – gmslzr
    Commented Sep 20, 2017 at 14:47
2

There isn't really a 100% dynamic solution - I've just been trying to figure it out as well and due to different domain extensions (DTL) this task would be really difficult without actually parsing all these extensions and checking them each time:

.com vs .co.uk vs org.uk

The most reliable option is to define a constant (or database entry etc.) that stores the actual domain name and remove it from the $_SERVER['SERVER_NAME'] using substr()

defined("DOMAIN")
    || define("DOMAIN", 'mymaindomain.co.uk');



function getSubDomain() {

    if (empty($_SERVER['SERVER_NAME'])) {

        return null;

    }

    $subDomain = substr($_SERVER['SERVER_NAME'], 0, -(strlen(DOMAIN)));

    if (empty($subDomain)) {

        return null;

    }

    return rtrim($subDomain, '.');

}

Now if you're using this function under http://test.mymaindomain.co.uk it will give you test or if you have multiple sub-domain levels http://another.test.mymaindomain.co.uk you'll get another.test - unless of course you update the DOMAIN.

I hope this helps.

2

Using regex, string functions, parse_url() or their combinations it's not real solution. Just test any of proposed solutions with domain test.en.example.co.uk, there will no any correct result.

Correct solution is use package that parses domain with Public Suffix List. I recomend TLDExtract, here is sample code:

$extract = new LayerShifter\TLDExtract\Extract();

$result = $extract->parse('test.en.example.co.uk');
$result->getSubdomain(); // will return (string) 'test.en'
$result->getSubdomains(); // will return (array) ['test', 'en']
$result->getHostname(); // will return (string) 'example'
$result->getSuffix(); // will return (string) 'co.uk'
1

What I found the best and short solution is

array_shift(explode(".",$_SERVER['HTTP_HOST']));
1
  • 1
    Will cause Strict error. Output of explode can not be passed directly to array_shift.
    – YAAK
    Commented Jan 30, 2013 at 18:45
1

For those who get 'Error: Strict Standards: Only variables should be passed by reference.' Use like this:

$env = (explode(".",$_SERVER['HTTP_HOST'])); $env = array_shift($env);

1
  • That was not the question, but thanks for your input.
    – FazoM
    Commented Oct 26, 2013 at 7:10
1
$domain = 'sub.dev.example.com';
$tmp = explode('.', $domain); // split into parts
$subdomain = current($tmp);
print($subdomain);     // prints "sub"

As seen in a previous question: How to get the first subdomain with PHP?

1

Simply

reset(explode(".", $_SERVER['HTTP_HOST']))

1

I'm doing something like this

$url = https://en.example.com

$splitedBySlash = explode('/', $url);
$splitedByDot = explode('.', $splitedBySlash[2]);

$subdomain = $splitedByDot[0];
1

Suppose current url = sub.example.com

    $host = array_reverse(explode('.', $_SERVER['SERVER_NAME']));

    if (count($host) >= 3){
       echo "Main domain is = ".$host[1].".".$host[0]." & subdomain is = ".$host[2];
       // Main domain is = example.com & subdomain is = sub
    } else {
       echo "Main domain is = ".$host[1].".".$host[0]." & subdomain not found";
       // "Main domain is = example.com & subdomain not found";
    }

1

this is my solution, it works with the most common domains, you can fit the array of extensions as you need:

$SubDomain = explode('.', explode('|ext|', str_replace(array('.com', '.net', '.org'), '|ext|',$_SERVER['HTTP_HOST']))[0]);
0
// For www.abc.en.example.com 
$host_Array = explode(".",$_SERVER['HTTP_HOST']); // Get HOST as array www, abc, en, example, com
array_pop($host_Array); array_pop($host_Array);   // Remove com and exmaple
array_shift($host_Array);                         // Remove www (Optional)
echo implode($host_Array, ".");                   // Combine array abc.en
0

I know I'm really late to the game, but here goes.

What I did was take the HTTP_HOST server variable ($_SERVER['HTTP_HOST']) and the number of letters in the domain (so for example.com it would be 11).

Then I used the substr function to get the subdomain. I did

$numberOfLettersInSubdomain = strlen($_SERVER['HTTP_HOST'])-12
$subdomain = substr($_SERVER['HTTP_HOST'], $numberOfLettersInSubdomain);

I cut the substring off at 12 instead of 11 because substrings start on 1 for the second parameter. So now if you entered test.example.com, the value of $subdomain would be test.

This is better than using explode because if the subdomain has a . in it, this will not cut it off.

1
  • The start position "0" was missing in your answer. $subdomain = substr($_SERVER['HTTP_HOST'], 0, $numberOfLettersInSubdomain);
    – Jamie
    Commented May 19, 2013 at 18:48
0

if you are using drupal 7

this will help you:

global $base_path;
global $base_root;  
$fulldomain = parse_url($base_root);    
$splitdomain = explode(".", $fulldomain['host']);
$subdomain = $splitdomain[0];
0
$host = $_SERVER['HTTP_HOST'];
preg_match("/[^\.\/]+\.[^\.\/]+$/", $host, $matches);
$domain = $matches[0];
$url = explode($domain, $host);
$subdomain = str_replace('.', '', $url[0]);

echo 'subdomain: '.$subdomain.'<br />';
echo 'domain: '.$domain.'<br />';
0

From PHP 5.3 you can use strstr() with true parameter

echo strstr($_SERVER["HTTP_HOST"], '.', true); //prints en
3
  • 1
    This will only work if there is no www at string start. A bit too trivial approach.
    – FooBar
    Commented Jul 27, 2014 at 17:09
  • This simplify things for other developers in the team, I'd rather use this than some advanced reg exp. If you want to trim www use trim($s,'www'); or just adjust it to your business logic...
    – tasmaniski
    Commented Aug 21, 2014 at 10:03
  • 1
    For completeness sake, www is actually a subdomain. It's just commonly aliased to the domain name itself for historical reasons. Commented Aug 26, 2014 at 19:23
0

Try this...

$domain = 'en.example.com';
$tmp = explode('.', $domain);
$subdomain = current($tmp);
echo($subdomain);     // echo "en"
1
  • I think it would be more helpful for the OP and further visitors, when you add some explaination to youer intension.
    – Reporter
    Commented Aug 27, 2014 at 10:58
0
function get_subdomain($url=""){
    if($url==""){
        $url = $_SERVER['HTTP_HOST'];
    }
    $parsedUrl = parse_url($url);
    $host = explode('.', $parsedUrl['path']);
    $subdomains = array_slice($host, 0, count($host) - 2 );
    return implode(".", $subdomains);
}
1
  • 1
    line #7 should be $host = explode('.', isset($parsedUrl['path']) ? $parsedUrl['path'] : $parsedUrl['host']);
    – Kal
    Commented Sep 9, 2015 at 7:59
0

you can use this too

echo substr($_SERVER['HTTP_HOST'], 0, strrpos($_SERVER['HTTP_HOST'], '.', -5));
0

Maybe I'm late, but even though the post is old, just as I get to it, many others do.

Today, the wheel is already invented, with a library called php-domain-parser that is active, and in which two mechanisms can be used. One based on the Public Suffix List and one based on the IANA list.

Simple and effective, it allows us to create simple helpers that help us in our project, with the ability to know that the data is maintained, in a world in which the extensions and their variants are very changeable.

Many of the answers given in this post do not pass a battery of unit tests, in which certain current extensions and their variants with multiple levels are checked, and neither with the casuistry of domains with extended characters.

Maybe it serves you, as it served me.

0
$parts = explode('.', $_SERVER["HTTP_HOST"]);
$subdomain = count($parts) < 3 ? null : $parts[0];
-4

If you only want what comes before the first period:

list($sub) = explode('.', 'en.example.com', 2);
6
  • What if there is a protocol handler at the beginning, such as http://, https://, ftp://, etc...? ;) Commented Mar 13, 2011 at 22:44
  • @Jared, there's no protocol in the string he's looking to parse... But if there were, I'd use parse_url() to extract the host.
    – Matthew
    Commented Mar 13, 2011 at 22:47
  • So we've provided two approaches which will be appropriate in different contexts. Commented Mar 13, 2011 at 22:49
  • 1
    Mainly, I'm just glad someone hasn't posted a regex answer (yet). Not to mention the last line of my answer also accomplishes the same thing yours does. Commented Mar 13, 2011 at 22:50
  • And if the hostname is en.example.co.uk?
    – Marc B
    Commented Mar 13, 2011 at 23:06

Not the answer you're looking for? Browse other questions tagged or ask your own question.