124

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
random
  • 9,774
  • 10
  • 66
  • 83
Damiano
  • 1,249
  • 2
  • 9
  • 3

29 Answers29

148

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];
Gras Double
  • 15,901
  • 8
  • 56
  • 54
Michael Deal
  • 2,948
  • 2
  • 18
  • 8
  • 18
    Only variables should be passed by reference. – Tamás Pap Sep 19 '12 at 07: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 Nov 09 '12 at 15:23
  • 1
    Error: `Strict Standards: Only variables should be passed by reference.` – Justin Dec 09 '12 at 05:45
  • @Tor: As late as 5.3, no. PHP's parser has always been particularly crappy in regards to stuff like that. Looks like it was semi fixed in 5.4. – cHao Jul 24 '13 at 13:15
  • 1
    pretty sure you can (explode(...))[0] though, should be operating on the return array instead of the function paranthesis (prior to 5.4) – That Realty Programmer Guy Apr 17 '14 at 13:07
  • Another one, for the *one-liner string folks*: `$sub = substr($_SERVER['HTTP_HOST'], 0, strpos($_SERVER['HTTP_HOST'], "."));` – Francisco Presencia Aug 21 '14 at 14:53
  • It's not good solution, because about `example.com` domain, return `example` as the subdomain! you know in this case don't have subdomain. – Nabi K.A.Z. Jun 02 '16 at 09:33
  • @NabiK.A.Z. Easy enough to check for: `if(count(explode(".", $_SERVER['HTTP_HOST'])) === 3)`... – War10ck Oct 04 '16 at 20:45
  • To solve the problem "Only variables should be passed by reference" do: `$arrayHost = explode('.', $_SERVER['HTTP_HOST']); echo array_shift($arrayHost);` – Leandro Castro Jun 06 '17 at 17:40
  • 5
    This solution will not work in case someone types in `www.en.example.com` and thus will return `www` as subdomain. – lolbas Sep 23 '17 at 06:24
  • @lolbas, correct. I used... `echo array_shift((explode('.', str_replace("www.", "", $_SERVER['HTTP_HOST']))));` – DanimalReks Jul 14 '19 at 03:46
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);
Mike Lewis
  • 63,433
  • 20
  • 141
  • 111
  • @Mike Lewis - Does this solve the problem of multiple subdomains, such as usa.en.example.com? Just wondering (my own answer doesn't, btw). – Jared Farrish Mar 13 '11 at 22:53
  • @Jared, just added a solution to detect multiple subdomains. – Mike Lewis Mar 13 '11 at 22:59
  • 1
    @Mike - Will that work with tx.usa.en.example.com? (or http://science.news.bbc.co.uk/)? (btw, that's not a working link, just an example, although news.bbc.co.uk does work) – Jared Farrish Mar 13 '11 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](http://stackoverflow.com/questions/288810/get-the-subdomain-from-a-url) This is actually a harder problem to solve. – Mike Lewis Mar 13 '11 at 23:12
  • That's true. Nice link. Just wondering, why not use `strrpos()` and an offset? – Jared Farrish Mar 13 '11 at 23:15
  • 3
    this also fails if there is no subdomain at all. – raveren Feb 03 '12 at 13:20
  • What about https://example.co.uk? – Marco Panichi Jul 10 '22 at 07:37
39

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
mazon
  • 407
  • 4
  • 2
  • 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. Jul 25 '13 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 Sep 30 '13 at 08:26
  • 1
    Awesome.. this works so well for all domain types and subdomains.. nice. – jon Oct 30 '13 at 09: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: https://publicsuffix.org/list/ – pomeh Aug 28 '14 at 08:14
13

http://php.net/parse_url

<?php
  $url = 'http://user:password@sub.hostname.tld/path?argument=value#anchor';
  $array=parse_url($url);
  $array['host']=explode('.', $array['host']);

  echo $array['host'][0]; // returns 'sub'
?>
Taha Paksu
  • 15,371
  • 2
  • 44
  • 78
JMW
  • 7,151
  • 9
  • 30
  • 37
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;
Sascha Frinken
  • 3,134
  • 1
  • 24
  • 27
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

Jeacovy Gayle
  • 447
  • 9
  • 9
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);
}
Community
  • 1
  • 1
Kamafeather
  • 8,663
  • 14
  • 69
  • 99
  • 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* ;) – Kamafeather Aug 28 '14 at 09:58
  • Isn't it Ukrainian? `.ua` is the country code for Ukraine. – nalply Aug 28 '14 at 13:38
  • Nope. Just mixed information. But I'm not sure, I'm not good enough to distinguish them ;) – Kamafeather Aug 28 '14 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) – Jeremy Harris Sep 09 '14 at 14:32
  • @Kamafeather this looks bulletproof. Any way to just get the `$match[1]` part? `$match[0]` seems unnecessary. – Andres SK Mar 27 '19 at 02:18
  • I am not sure to understand what you mean/need. From [*preg_match* PHP manual](https://www.php.net/manual/en/function.preg-match.php): *"`$matches[0]` will contain the text that matched the full pattern, `$matches[1]` will have the text that matched the first captured parenthesized subpattern, and so on."* I don't think you can't get rid of `match[0]`, it is always there, because of how `preg_match` is implemented. You can eventually `array_shift` it, if you don't want to have it in the array. – Kamafeather Mar 27 '19 at 15:10
5

Simplest and fastest solution.

$sSubDomain = str_replace('.example.com','',$_SERVER['HTTP_HOST']);
Arjen
  • 416
  • 4
  • 12
3
$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, '.')); 
Jared Farrish
  • 48,585
  • 17
  • 95
  • 104
  • 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 Mar 13 '11 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. – Jared Farrish Mar 13 '11 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 Sep 20 '17 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.

Sebastian Sulinski
  • 5,815
  • 7
  • 39
  • 61
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'
Oleksandr Fediashov
  • 4,315
  • 1
  • 24
  • 42
1

What I found the best and short solution is

array_shift(explode(".",$_SERVER['HTTP_HOST']));
Marijn
  • 10,367
  • 5
  • 59
  • 80
Zulqurnain abbas
  • 732
  • 1
  • 5
  • 5
  • 1
    Will cause Strict error. Output of explode can not be passed directly to array_shift. – YAAK Jan 30 '13 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);

Naseer
  • 11
  • 5
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?

Community
  • 1
  • 1
1

Simply

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

Adam Fowler
  • 1,750
  • 1
  • 17
  • 18
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]);
Muhammad Hassaan
  • 7,296
  • 6
  • 30
  • 50
1

I'm doing something like this

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

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

$subdomain = $splitedByDot[0];
Sheik Althaf
  • 1,595
  • 10
  • 16
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";
    }

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
Rahul Prasad
  • 8,074
  • 8
  • 43
  • 49
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.

Piccolo
  • 1,612
  • 4
  • 22
  • 38
  • The start position "0" was missing in your answer. $subdomain = substr($_SERVER['HTTP_HOST'], 0, $numberOfLettersInSubdomain); – Jamie May 19 '13 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];
contradictioned
  • 1,253
  • 2
  • 14
  • 26
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 />';
Brynner Ferreira
  • 1,527
  • 1
  • 21
  • 21
0

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

echo strstr($_SERVER["HTTP_HOST"], '.', true); //prints en
tasmaniski
  • 4,767
  • 3
  • 33
  • 65
  • 1
    This will only work if there is no `www` at string start. A bit too trivial approach. – FooBar Jul 27 '14 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 Aug 21 '14 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. – Levi Morrison Aug 26 '14 at 19:23
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);
}
itsazzad
  • 6,868
  • 7
  • 69
  • 89
  • 1
    line #7 should be `$host = explode('.', isset($parsedUrl['path']) ? $parsedUrl['path'] : $parsedUrl['host']);` – Kal Sep 09 '15 at 07:59
0

Try this...

$domain = 'en.example.com';
$tmp = explode('.', $domain);
$subdomain = current($tmp);
echo($subdomain);     // echo "en"
Edwin Thomas
  • 1,186
  • 2
  • 18
  • 31
  • I think it would be more helpful for the OP and further visitors, when you add some explaination to youer intension. – Reporter Aug 27 '14 at 10:58
0

you can use this too

echo substr($_SERVER['HTTP_HOST'], 0, strrpos($_SERVER['HTTP_HOST'], '.', -5));
XIMvad
  • 1
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.

abkrim
  • 3,512
  • 7
  • 43
  • 69
-2
<?php
// Your code here!

function get_domain($host) {
   $parts = explode('.',$host);
   $extension  = $parts[count($parts)-1];
   $name = $parts[count($parts)-2];
   return  $name.'.'.$extension;
}

echo get_domain("https://api.neoistone.com");
?>

  • You can't be sure there are more than 2 parts of the domain. This function will work only for specific cases. – ozahorulia Dec 08 '21 at 00:31
-5

If you only want what comes before the first period:

list($sub) = explode('.', 'en.example.com', 2);
Matthew
  • 47,584
  • 11
  • 86
  • 98