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. 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 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];
Sign up to request clarification or add additional context in comments.

11 Comments

Only variables should be passed by reference.
Aren't you able to just do explode(...)[0] instead of using shift these days? Not been PHPing for several years..
Error: Strict Standards: Only variables should be passed by reference.
pretty sure you can (explode(...))[0] though, should be operating on the return array instead of the function paranthesis (prior to 5.4)
This solution will not work in case someone types in www.en.example.com and thus will return www as subdomain.
|
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 Comments

@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, just added a solution to detect multiple subdomains.
@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)
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.
this also fails if there is no subdomain at all.
|
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 Comments

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.
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!
Awesome.. this works so well for all domain types and subdomains.. nice.
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
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'
?>

Comments

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;

Comments

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 Comments

You forget TLD's like .co.uk - your snippet will not work with these TLDs
This is the best answer for modern PHP.
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 Comments

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 ;)
Isn't it Ukrainian? .ua is the country code for Ukraine.
Nope. Just mixed information. But I'm not sure, I'm not good enough to distinguish them ;)
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)
@Kamafeather this looks bulletproof. Any way to just get the $match[1] part? $match[0] seems unnecessary.
|
5

Simplest and fastest solution.

$sSubDomain = str_replace('.example.com','',$_SERVER['HTTP_HOST']);

Comments

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 Comments

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.
Right, I was using an old piece of code. The example still stands, however. That's not the root of the question.
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.
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.

Comments

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'

Comments

1

What I found the best and short solution is

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

1 Comment

Will cause Strict error. Output of explode can not be passed directly to array_shift.
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 Comment

That was not the question, but thanks for your input.
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?

Comments

1

Simply

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

Comments

1

I'm doing something like this

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

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

$subdomain = $splitedByDot[0];

Comments

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";
    }

Comments

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]);

Comments

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

Comments

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 Comment

The start position "0" was missing in your answer. $subdomain = substr($_SERVER['HTTP_HOST'], 0, $numberOfLettersInSubdomain);
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];

Comments

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 />';

Comments

0

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

echo strstr($_SERVER["HTTP_HOST"], '.', true); //prints en

3 Comments

This will only work if there is no www at string start. A bit too trivial approach.
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...
For completeness sake, www is actually a subdomain. It's just commonly aliased to the domain name itself for historical reasons.
0

Try this...

$domain = 'en.example.com';
$tmp = explode('.', $domain);
$subdomain = current($tmp);
echo($subdomain);     // echo "en"

1 Comment

I think it would be more helpful for the OP and further visitors, when you add some explaination to youer intension.
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 Comment

line #7 should be $host = explode('.', isset($parsedUrl['path']) ? $parsedUrl['path'] : $parsedUrl['host']);
0

you can use this too

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

Comments

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.

Comments

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

Comments

-4

If you only want what comes before the first period:

list($sub) = explode('.', 'en.example.com', 2);

6 Comments

What if there is a protocol handler at the beginning, such as http://, https://, ftp://, etc...? ;)
@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.
So we've provided two approaches which will be appropriate in different contexts.
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.
And if the hostname is en.example.co.uk?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.