Radek
Radek

Reputation: 11121

How to make nginx virtual directories accessible in php?

Let's say I have a web server (nginx) server.com where I have only one php file index.php (there is no directory structure). I want to be able to access anything after server.com. It will be an url structure. For example server.com/google.com, server.com/yahoo.com.au etc...

An example would be http://whois.domaintools.com/google.com (They don't have a directory that's called /google.com, right?)

Q1: How can I access whatever is after 'server.com' from index.php

Q2: Can I get the protocol from such URL? For example server.com/http://www.google.com or server.com/https://www.google.com

PS I'm not certain if the term virtual directory is used here correctly. I just want to do what I saw somewhere else.

Upvotes: 9

Views: 2083

Answers (5)

Jakub Strzadala
Jakub Strzadala

Reputation: 163

Have you thought of rewrites? I know only Apache's rules. In Apache I would do it this way:

RewriteCond $1 !^(index\.php|js|css|admin|images|img|png|robots\.txt|sitemap\.xml|sitemap\.xml\.gz|sitemap\.kml|robots\.txt|javascripts|style.css)
RewriteRule ^(.*)$ index.php/?page=$1 [L]

This will pass everything on to $_GET['page'] (PHP) except if anything in the URL matches index.php, js, css and others.

If you have any questions, let me know. Hope this helped.

Upvotes: 2

ghoti
ghoti

Reputation: 46856

Well, matzahboy and VBart have already contributed nginx configuration excerpts that correctly show you how to rewrite the URL to a GET variable. But in order to USE this, you have to interpret the value provided in $_GET['q']. You haven't specified the rules you want to follow, so here's a suggestion.

To be tested in this order:

  1. Valid URL per RFC2396 using PHP's Validate Filter: test with cURL, respond TRUE for HTTP response codes < 400, FALSE for anything else.
  2. (host.)example.com/path (missing protocol): assume HTTP protocol, test per #1.
  3. host.example.com (hostname only): same as #2
  4. example.com (domain only): test as #2, then test as www.example.com.
  5. Anything else: fail.

If that makes sense to you, then the following index.php may get you started:

<?php

function http_response($url) {
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_HEADER, TRUE);
  curl_setopt($ch, CURLOPT_NOBODY, TRUE); // remove body
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  $head = curl_exec($ch);
  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  if (!$head) {
    return FALSE;
  }

  if ($httpCode < 400) {
    return $url;
  } else {
    return FALSE;
  }
}

function test_string($q) {
  if (filter_var($q, FILTER_VALIDATE_URL)) {
    // Matches RFC2396, so let's generate a hit.
    return http_response($q);
  }
  elseif (preg_match('/^([a-z0-9][a-z0-9-]+\.)+[a-z]{2,}(:[0-9]+)?\/.+$/', $q)) {
    // Matches: (host.)example.com/path
    return http_response("http://" . $q);
  }
  elseif (preg_match('/^([a-z0-9][a-z0-9-]+\.){2,}[a-z]{2,}$/', $q)) {
    // Matches: host.example.com
    return http_response("http://" . $q . "/");
  }
  elseif (preg_match('/^([a-z0-9][a-z0-9-]+\.)+[a-z]{2,}$/', $q)) {
    // Matches: example.com
    $ret=http_response("http://" . $q . "/");
    if ($ret === FALSE) {
      return http_response("http://www." . $q . "/");
    } else {
      return $ret;
    }
  }
  else {
    return FALSE;
  }
}

$q = $_GET['q'];
//$q = $argv[1]; // for command-line testing

$url = test_string($q);

if ($url === FALSE) {
  printf("<p>The URL <strong>%s</strong> is invalid.</p>\n", $q);
} else {
  printf("<p>The URL is <strong>%s</strong>.</p>\n", $url);
}

I don't claim that this is the prettiest or most secure code, but at least it implements an analysis strategy for supplied URLs like:

  • http://example.com/https://www.example.net/foo/bar,
  • http://example.com/example.org/foo/bar or
  • http://example.com/example.org

Note that cURL's gopher support may be broken, and other protocols (which do not return HTTP response codes) are not supported by the code above. If you need to support protocols other than HTTP and HTTPS, please say so in your question and I'll adjust the PHP accordingly.

Specifically, if you want to be able to check http://example.com/ping://host.example.net it wouldn't be hard, but it would have to be coded separately from the bit handled by cURL.

Upvotes: 7

VBart
VBart

Reputation: 15110

location / {
    try_files $uri @dynamic;
}

location @dynamic {
    fastcgi_pass backend;

    include fastcgi_params;
    fastcgi_param  PATH_INFO        $uri;
    fastcgi_param  SCRIPT_NAME      /index.php;
    fastcgi_param  SCRIPT_FILENAME  /absolute/path/to/index.php;
}

The fastcgi_params file is bundled with nginx

$ cat fastcgi_params

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

#fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

And you have access to all these fastcgi environment parameters using built-in $_SERVER array in PHP. http://php.net/manual/en/reserved.variables.server.php


Upvotes: 7

Zane
Zane

Reputation: 4752

Using matzahboy's nginx code:

location / {
     rewrite ^/(.*)$ /index.php?q=$1
}

along with the following PHP code:

$basis = array(
    'scheme' => 'http',
);

$info = array_merge( $base, parse_url( 'www.google.com' ) );

print_r( $info );

Which will return something like this for example.com/google.com or example.com/http://google.com/

Array ( [scheme] => http [path] => www.google.com )

Note that the $base array contains the 'scheme' value of 'http'. This defaults the value of scheme so that you can later do something like

$info['scheme'] . '://' . $info['path'];

which would result in http://google.com/

Hope this answers your full question.

Upvotes: 3

matzahboy
matzahboy

Reputation: 3024

location / {
    rewrite ^/(.*)$ /index.php?q=$1
}

location = /index.php {
    #Do your normal php passing stuff here now
}

Is that what you were looking for?

As an answer to your second question, you can parse the protocol in php. Nginx doesn't need to do that. To parse the url, you can use the parse_url function

Upvotes: 11

Related Questions