4.6.x common.inc url($path = NULL, $query = NULL, $fragment = NULL, $absolute = FALSE)
4.7.x common.inc url($path = NULL, $query = NULL, $fragment = NULL, $absolute = FALSE)
5.x common.inc url($path = NULL, $query = NULL, $fragment = NULL, $absolute = FALSE)
6.x common.inc url($path = NULL, $options = array())
7.x common.inc url($path = NULL, array $options = array())

Generates an internal or external URL.

When creating links in modules, consider whether l() could be a better alternative than url().


$path: (optional) The internal path or external URL being linked to, such as "node/34" or "http://example.com/foo". The default value is equivalent to passing in '<front>'. A few notes:

  • If you provide a full URL, it will be considered an external URL.
  • If you provide only the path (e.g. "node/34"), it will be considered an internal link. In this case, it should be a system URL, and it will be replaced with the alias, if one exists. Additional query arguments for internal paths must be supplied in $options['query'], not included in $path.
  • If you provide an internal path and $options['alias'] is set to TRUE, the path is assumed already to be the correct path alias, and the alias is not looked up.
  • The special string '<front>' generates a link to the site's base URL.
  • If your external URL contains a query (e.g. http://example.com/foo?a=b), then you can either URL encode the query keys and values yourself and include them in $path, or use $options['query'] to let this function URL encode them.

$options: (optional) An associative array of additional options, with the following elements:

  • 'query': An array of query key/value-pairs (without any URL-encoding) to append to the URL.
  • 'fragment': A fragment identifier (named anchor) to append to the URL. Do not include the leading '#' character.
  • 'absolute': Defaults to FALSE. Whether to force the output to be an absolute link (beginning with http:). Useful for links that will be displayed outside the site, such as in an RSS feed.
  • 'alias': Defaults to FALSE. Whether the given path is a URL alias already.
  • 'external': Whether the given path is an external URL.
  • 'language': An optional language object. If the path being linked to is internal to the site, $options['language'] is used to look up the alias for the URL. If $options['language'] is omitted, the global $language_url will be used.
  • 'https': Whether this URL should point to a secure location. If not defined, the current scheme is used, so the user stays on HTTP or HTTPS respectively. TRUE enforces HTTPS and FALSE enforces HTTP, but HTTPS can only be enforced when the variable 'https' is set to TRUE.
  • 'base_url': Only used internally, to modify the base URL when a language dependent URL requires so.
  • 'prefix': Only used internally, to modify the path when a language dependent URL requires so.
  • 'script': The script filename in Drupal's root directory to use when clean URLs are disabled, such as 'index.php'. Defaults to an empty string, as most modern web servers automatically find 'index.php'. If clean URLs are disabled, the value of $path is appended as query parameter 'q' to $options['script'] in the returned URL. When deploying Drupal on a web server that cannot be configured to automatically find index.php, then hook_url_outbound_alter() can be implemented to force this value to 'index.php'.
  • 'entity_type': The entity type of the object that called url(). Only set if url() is invoked by entity_uri().
  • 'entity': The entity object (such as a node) for which the URL is being generated. Only set if url() is invoked by entity_uri().

Return value

A string containing a URL to the given path.

247 calls to url()
AddFeedTestCase::testAddFeed in modules/aggregator/aggregator.test
Creates and ensures that a feed is unique, checks source, and deletes feed.
AggregatorRenderingTestCase::testBlockLinks in modules/aggregator/aggregator.test
Adds a feed block to the page and checks its links.
AggregatorTestCase::getFeedEditArray in modules/aggregator/aggregator.test
Returns a randomly generated feed edit array.
aggregator_form_aggregator_admin_form_alter in modules/aggregator/aggregator.processor.inc
Implements hook_form_aggregator_admin_form_alter().
aggregator_form_feed in modules/aggregator/aggregator.admin.inc
Form constructor for adding and editing feed sources.

... See full list

5 string references to 'url'
aggregator_form_feed_validate in modules/aggregator/aggregator.admin.inc
Form validation handler for aggregator_form_feed().
aggregator_schema in modules/aggregator/aggregator.install
Implements hook_schema().
aggregator_update_7003 in modules/aggregator/aggregator.install
Increase the length of {aggregator_feed}.url.
statistics_top_referrers in modules/statistics/statistics.admin.inc
Page callback: Displays the "top referrers" in the access logs.
UpdateFeedTestCase::testUpdateFeed in modules/aggregator/aggregator.test
Creates a feed and attempts to update it.


includes/common.inc, line 2235
Common functions that many Drupal modules will need to reference.


function url($path = NULL, array $options = array()) {

  // Merge in defaults.
  $options += array(
    'fragment' => '',
    'query' => array(),
    'absolute' => FALSE,
    'alias' => FALSE,
    'prefix' => '',
  if (!isset($options['external'])) {
    $options['external'] = url_is_external($path);

  // Preserve the original path before altering or aliasing.
  $original_path = $path;

  // Allow other modules to alter the outbound URL and options.
  drupal_alter('url_outbound', $path, $options, $original_path);
  if (isset($options['fragment']) && $options['fragment'] !== '') {
    $options['fragment'] = '#' . $options['fragment'];
  if ($options['external']) {

    // Split off the fragment.
    if (strpos($path, '#') !== FALSE) {
      list($path, $old_fragment) = explode('#', $path, 2);

      // If $options contains no fragment, take it over from the path.
      if (isset($old_fragment) && !$options['fragment']) {
        $options['fragment'] = '#' . $old_fragment;

    // Append the query.
    if ($options['query']) {
      $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($options['query']);
    if (isset($options['https']) && variable_get('https', FALSE)) {
      if ($options['https'] === TRUE) {
        $path = str_replace('http://', 'https://', $path);
      elseif ($options['https'] === FALSE) {
        $path = str_replace('https://', 'http://', $path);

    // Reassemble.
    return $path . $options['fragment'];

  // Strip leading slashes from internal paths to prevent them becoming external
  // URLs without protocol. /example.com should not be turned into
  // //example.com.
  $path = ltrim($path, '/');
  global $base_url, $base_secure_url, $base_insecure_url;

  // The base_url might be rewritten from the language rewrite in domain mode.
  if (!isset($options['base_url'])) {
    if (isset($options['https']) && variable_get('https', FALSE)) {
      if ($options['https'] === TRUE) {
        $options['base_url'] = $base_secure_url;
        $options['absolute'] = TRUE;
      elseif ($options['https'] === FALSE) {
        $options['base_url'] = $base_insecure_url;
        $options['absolute'] = TRUE;
    else {
      $options['base_url'] = $base_url;

  // The special path '<front>' links to the default front page.
  if ($path == '<front>') {
    $path = '';
  elseif (!empty($path) && !$options['alias']) {
    $language = isset($options['language']) && isset($options['language']->language) ? $options['language']->language : '';
    $alias = drupal_get_path_alias($original_path, $language);
    if ($alias != $original_path) {
      $path = $alias;
  $base = $options['absolute'] ? $options['base_url'] . '/' : base_path();
  $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];

  // With Clean URLs.
  if (!empty($GLOBALS['conf']['clean_url'])) {
    $path = drupal_encode_path($prefix . $path);
    if ($options['query']) {
      return $base . $path . '?' . drupal_http_build_query($options['query']) . $options['fragment'];
    else {
      return $base . $path . $options['fragment'];
  else {
    $path = $prefix . $path;
    $query = array();
    if (!empty($path)) {
      $query['q'] = $path;
    if ($options['query']) {

      // We do not use array_merge() here to prevent overriding $path via query
      // parameters.
      $query += $options['query'];
    $query = $query ? '?' . drupal_http_build_query($query) : '';
    $script = isset($options['script']) ? $options['script'] : '';
    return $base . $script . $query . $options['fragment'];


itqn2004’s picture

Use function url:

$account1 = user_load(array('uid' => $db->uid));		
		$fullname1 = trim($account1->profile_firstname ." " . $account1->profile_lastname);
		$url = url('admin/affiliate/'.$account1->uid.'/affiliate-dashboard', array('absolute' => TRUE));
		$link = '<a id="sticky" href="'.$url.'" rel="'.$url.'">'.$fullname1.'</a>';
Garrett Albright’s picture

Not the best example. Aside from using a URL for the "rel" parameter (wha?), your best bet when making a link is to use the l() function.

pillarsdotnet’s picture

Calling the url() function within a hook_requirements() implementation results in a fatal error.

Issue reported here: http://drupal.org/node/1149580

berenddeboer’s picture

It appears the https option is simply ignored. I.e. if you are in http mode, you cannot emit an https url, see http://drupal.org/node/932738

prograham’s picture

I found that the https option was in fact being ignored by the url() function as used in the Secure Pages module 7-x.1-x-dev, causing an infinite loop for any page I told the module to secure. This is particuarly grim because the module specifies a lot of pages to be secured by default, so if you accept the defaults and then you enable the module, it may suddenly seem like your whole site goes into infinite loops. I am thinking about submitting a patch for the Secure Pages module so it catches and recovers from this bug in the url() function, but it is hard for me to believe the module has any users presently if other people are having an experience at all similar to mine. I was able to fix it with a simple secondary check on the results of the url() function. In other words, I chose not to trust the https key to actually result in an https url.

David_Rothstein’s picture

Per the documentation on this function as well as the issue you linked to (https://drupal.org/node/932738), you're required to have $conf['https'] = TRUE; in your settings.php file in order for linking back and forth between http and https versions of the same site to work.

arpieb’s picture

Look at the code above - the "https" option isn't even processed unless the "external" option evals to TRUE. Documentation needs to be updated to reflect that.

Alex Savin’s picture

Do not use 'prefix' option to prefix urls in custom modules as it will break your site when in conjunction with i18n (if using path prefix language negotiation). In fact that is the only place 'prefix' option is used.
It may give you the false impression that it works until you enable i18n.

sisko’s picture

I'm using the following code:

drupal_goto("event/registration/failed", array('query' => array('nid'=>$nid, 'state'=>$form_state['values']['submitted']['questionnaire']),)

and I just wonder if there is a way to send arguments by POST rather than GET where the arguments get appended to the URL?

xurizaemon’s picture

You can't redirect the visitor to a new POST request, but you can point a Drupal form at an external URL and submit directly to it (see hook_form_alter() for existing forms; the property you want to change is $form['#action']).

drupal_goto() returns an HTTP header instructing the visitor's browser to make a new GET request with new location.

Making the visitor's browser fire POST requests requires either a form pointed at the correct location OR javascript, which has other limitations.

smartango’s picture

I have echo

echo url('sites/all/modules/mymodule/templates/grigio-giu.gif',array('absolute'=>TRUE));

and this should work as expected .. What do I should expect? the link without language prefix, but that is in there, and there are no option to take it away.

Does not work. Use:

global $base_unsecure_url;
echo $base_unsecure_url .base_path() . 'sites/all/modules/mymodule/templates/grigio-giu.gif';

because this feature is missing

infojunkie’s picture

We've found that the following works to remove the language prefix for the generated URL:

url($your_resource_path, array('language' => (object)array('language' => FALSE)));
allenfantasy’s picture

Works like a charm. Many thanks.

emanaton’s picture

... and this note of yours is still helping folks out; it certainly just saved me from further digging. Thanks for taking the time to add this, infojunkie!

rjacobs’s picture

For file links outside if the files directory (shipped files) consider file_create_url() as it will take care of all the "base" parts universally and will omit the language prefix.

sumachaa’s picture

adding 'external' => TRUE to the $options also fixes this

tswaters’s picture

The following returns an absolute aliased path.

url( 'node/1', array('absolute' => true, 'alias' => false ));

I suppose I will have to reference $base_url after all :(

leymannx’s picture

Simply set $options['alias'] = TRUE on that one and you'll get the non-aliased path. It simply works like that as long as you enter a system path in the first place.

swirt’s picture

I was doing a bit of research into which was faster, url() or drupal_lookup_path() or drupal_get_path_alias.
I found no declared speed winner, but I did notice something interesting. Only the url() function is documented out to D8.

Zac_JH’s picture

We use a CDN to distribute files. Because we also have some pages using SSL and some not we prefix the urls with // rather than http:// - this allows the browser to pick the right protocol.

However the url function messes with this as it's looking for : only. I've used the hook_outbound_later to fix this like:

function HOOK_url_outbound_alter(&$path, &$options, $original_path) {
// add http/s: if link is using // prefix

SergFromSD’s picture

I'm creating a CDN ready site so my internal paths start with public://. This function is not performing the correct substitution (or any substitution) and takes an input of public://file1.pdf and returns http://locahost/public://file1.pdf.

I will correct this with my own HOOK_url_outbound_alter like roodoo but I think this should be handled by this function since public:// is supported by other areas of drupal.

dayer4b’s picture

SiliconMind’s picture

If you are pulling your hair out trying to figure out why absolute urls does not have correct DOMAIN prefix if you provide 'language' option, then read this: http://drupal.stackexchange.com/a/84629/5982

sheldonkreger’s picture

I wanted Drupal's normal URL (non-alias) to come back from this function, but also needed some of the magic in the options array. So, I made sure to pass in a non-aliased $path and set $options['alias'] = TRUE;

Misleading, but it works :-)

leymannx’s picture

Thanks a lot!

magendiran’s picture

Hi , thanks in advance
I need the url with a fragment in D7 like a :
The below one it's not working
url('user', array('query' => array('#' => 'registerform')))
Can you please advise me the correct one.

sstedman’s picture

url('user', array('fragment' => 'registerform', 'external' => TRUE));

the666bbq’s picture

$prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];


$path = drupal_encode_path($prefix . $path); ==> ....nl//

gaellafond’s picture

To be XHTML compliant, the ampersand needs to be encoded:

$path .= (strpos($path, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($options ['query']);

Otherwise, it won't produce working URLs with examples like this one:

url('some/path', array('query' => array('copy_txt' => 'me')));

This snippet produce the following URL: "/some/path&copy_txt=me"
Which is interpreted by most browsers as: "/some/path©_txt=me"

For more info:

studiotwelve’s picture

You need a semicolon to make &copy return ©

sanduhrs’s picture

liquidcms’s picture

Why does this not work?

$url = 'www.linkedin.com/test1';
echo url($url, array('absolute' => true, 'external' => true));

output is: www.linkedin.com/test1

should it not be: http://www.linkedin.com/test1

j.lucky’s picture

How will implement in Drupal 8 ?