Bug 40102

Summary: SCRIPT_NAME set incorrectly with mod_rewrite
Product: Apache httpd-2 Reporter: Michael Minicki <martel>
Component: mod_rewriteAssignee: Apache HTTPD Bugs Mailing List <bugs>
Status: REOPENED ---    
Severity: normal CC: arnaud.lb, julian, michael, taffy-tyler6464
Priority: P2    
Version: 2.2-HEAD   
Target Milestone: ---   
Hardware: PC   
OS: All   

Description Michael Minicki 2006-07-24 11:08:23 UTC
SCRIPT_NAME is passed incorrectly to PHP depending on where you place
the mod_rewrite rule. If it is placed in .htaccess or under <Directory>, the 
SCRIPT_NAME is initialized correctly but when you place the same rule under 
<VirtualHost> it is an empty string (or any other bogus value).

I wasn't sure if it was an Apache or PHP bug and I have posted it on PHP 
bugtracker first. They claim it's an Apache bug:

http://bugs.php.net/bug.php?id=38141

The rule I'm using is:

    RewriteEngine On
    RewriteCond %{SCRIPT_FILENAME} !-f
    RewriteCond %{SCRIPT_FILENAME} !-d
    RewriteRule ^(.*)$ /index.php/$1

Or (it's not relevant - both have the same result):

    RewriteEngine On
    RewriteRule !\.(js|ico|gif|jpg|png|css|swf)$ index.php

Apache2 PHP SAPI. Apache version is 2.0.58 but I guess it may not be
relevant as it was reported by number of people (though I don't know what their 
setups are). 

Tested on Gentoo Linux.
Comment 1 Michael Minicki 2006-08-03 08:58:01 UTC
It seems that if you place the rules under VirtualHost more things are mangled 
- ie. PATH_INFO. 

For example, for virtual host of test.nebula.intranet and root dir the 
variables are initialized as follows:

$_SERVER['DOCUMENT_ROOT'] = '/var/www/localhost/htdocs/test'
$_SERVER['SCRIPT_FILENAME'] = '/var/www/localhost/htdocs/test/index.php'
$_SERVER['REQUEST_URI'] = '/'
$_SERVER['SCRIPT_NAME'] = ''
$_SERVER['PATH_INFO'] = '/index.html' // OMG!

And now with deeper URL where PATH_INFO should be initialized - (SCRIPT_NAME is 
still wrong):

$_SERVER['DOCUMENT_ROOT'] = '/var/www/localhost/htdocs/test'
$_SERVER['SCRIPT_FILENAME'] = '/var/www/localhost/htdocs/test/index.php'
$_SERVER['REQUEST_URI'] = '/archive/2006/05'
$_SERVER['SCRIPT_NAME'] = ''
$_SERVER['PATH_INFO'] = '/archive/2006/05'

SCRIPT_NAME should be '/index.php' in both of those examples.
Comment 2 Michael Minicki 2006-08-03 09:38:01 UTC
It still doesn't work on apache 2.0.59.
Comment 3 Nick Kew 2006-08-03 09:48:07 UTC
What happens if you run the PHP script as CGI?
Comment 4 Michael Minicki 2006-08-03 10:32:07 UTC
As a CGI script for URL of: http://test.nebula.intranet/archive/2006/05 values 
are set to:

$_SERVER['DOCUMENT_ROOT'] = string(0) 
$_SERVER['SCRIPT_FILENAME'] = string(40) /var/www/localhost/htdocs/test/
index.cgi
$_SERVER['REQUEST_URI'] = string(16) /archive/2006/05
$_SERVER['SCRIPT_NAME'] = string(40) /var/www/localhost/htdocs/test/index.cgi
$_SERVER['PATH_INFO'] = string(16) /archive/2006/05

Should document root be empty? I don't have much experience with PHP working as 
a CGI script. I have added a handler to <VirtualHost> (AddHandler cgi-script
 .cgi), added ExecCGI to Options under <Directory> and changed RewriteRule to 
"RewriteRule ^(.*)$ /index.cgi/$1".
Comment 5 Michael Minicki 2006-08-03 11:32:50 UTC
The values are the same in every case when run as a CGI. It does not matter if 
the rule is placed under <VirtualHost>, <Directory> or in .htaccess file. It 
seems it's a SAPI problem.
Comment 6 Nick Kew 2006-08-03 12:21:32 UTC
If CGI gets the expected SCRIPT_NAME, I infer the problem isn't in 
mod_rewrite, but somewhere within mod_php.  That is not an apache product.  
Please report the bug to its maintainers.
Comment 7 Nick Kew 2006-08-03 13:04:23 UTC
FWIW, if you want to tell the mod_php folks how to fix it, this is my guess:

(1) SCRIPT_NAME is set by ap_add_cgi_vars in Apache's "util_script".
(2) PHP presumably calls that somewhere - unless it's reinvented that wheel.
(3) The bug *looks like* a case of PHP calling it too early - specifically 
*before* the rewrite happened - so of course there was no SCRIPT_NAME.
(4) So a fix would be for mod_php to call it later.  Or maybe even call it 
twice, if the early call is unavoidable.
Comment 8 Michael Minicki 2006-08-03 13:12:07 UTC
Thank you, Nick. I will post your remarks on PHP bug tracker.
Comment 9 deadapache 2006-08-05 19:35:23 UTC
Here is the way it works:
1) ap_process_http_connection() is called when processing HTTP request;
2) ap_process_http_connection() calls ap_read_request() in order to create request struct;
3) ap_read_request() calls ap_getword_white(), which returns the original URI, not the file used to 
handle the request (this is how it works here, with PHP 5.2 and Apache 2.0.55);
4) ap_read_request() sets request->uri to the result of ap_getword_white();
5) finally PHP request handler is called, which in turn calls ap_add_cgi_vars() to get the variables.

The way it works is the very same in both cases and it doesn't depend on the place where 
mod_rewrite directives were set, so I don't see how PHP could call ap_add_cgi_vars() too early or 
too late.
Comment 10 Arnaud LB 2008-11-25 17:14:20 UTC
I can reproduce this bug with a simple shell script:

----------
#!/bin/sh
cat <<HEADERS
Status: 200 Ok
Content-Type: text/plain

HEADERS

export
----------

I added the following the <VirtualHost> to test that:

SetHandler cgi-script
<Directory /var/www>
 Options +ExecCGI
</Directory>

And the rewrite rules:

RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^(.*)$ /test.sh/$1


When the script is executed "export" shows that SCRIPT_NAME is empty. This appends only when the URL is rewritten to a one which has a path info component:

Triggers the bug:

RewriteRule ^(.*)$ /test.sh/$1

Does not bug:

RewriteRule ^(.*)$ /test.sh
Comment 11 Bob Ionescu 2008-12-12 10:14:45 UTC
(In reply to comment #0)
> SCRIPT_NAME is passed incorrectly to PHP depending on where you place
> the mod_rewrite rule. If it is placed in .htaccess or under <Directory>, the 
> SCRIPT_NAME is initialized correctly but when you place the same rule under 
> <VirtualHost> it is an empty string (or any other bogus value).
[...]
> The rule I'm using is:
> 
>     RewriteEngine On
>     RewriteCond %{SCRIPT_FILENAME} !-f
>     RewriteCond %{SCRIPT_FILENAME} !-d
>     RewriteRule ^(.*)$ /index.php/$1

In per-server context, mod_rewrite acts by default as an URI-to-filename translator. If you add the [PT] flag, mod_rewrite will copy the rewrite result back to r->uri and does not map the request to the filesystem, i.e. another URI-to-filename translator will map the request to the filesystem. In that case (if you use the PT-flag) SCRIPT_NAME seems to be set correctly.

BTW: I'm wondering how your conditions will work in per-server context since w/o a URI-to-filename translation SCRIPT_FILENAME cannot contain the physical path of the request, which is needed in oder to check for existing files or dirs. See the docs bug 16402.


Using apache 2.2.10 (but I think this problem is in trunk, too):

- If e.g. php is used as a module
Original request_uri /bar ; rewrite result /foo.php 
In that case (mod_php), and if there is no internal redirect, SCRIPT_NAME contains in my environment w/o the PT-flag the value of r->uri (/bar) and with the PT-flag /foo.php. ENV PATH_INFO - rewrite result /foo.php/path - is '/path' and works correct with and w/o the PT-flag.

- If a directive is used which triggers an internal redirect - such as php cgi setup via the Action directive, SCRIPT_NAME will contain the value of the cgi prog.
Original request_uri /bar ; rewrite result /foo.php ; the result of the Action directive is r->uri /php-script/php with path_info /bar:

ScriptAlias /php-script/ "/path/to/php/"
AddHandler cgi-php .php
Action cgi-php /php-script/php
--> SCRIPT_NAME=/php-script/php (correct with and w/o PT-flag)
W/o the PT-Flag, PATH_INFO contains /bar (a subrequest for /bar will pass /foo.php to the cgi prog), REDIRECT_URL contains the original request_uri /bar, too (IMHO correct, because mod_rewrite doesn't change r->uri w/o the PT-flag, the only way Aliases can work). But that means that there is no ENV which points to the result of mod_rewrite (/foo.php).
With the PT-flag set, PATH_INFO and REDIRECT_URL both contain /foo.php, which is correct.

- Directly executed CGIs such as /cgi/printenv.pl with a config like (not Alias'd nor ScriptAlias'd in another way)
<Directory "/var/www/cgi">
  Options +ExecCGI
  AddHandler cgi-script .pl
</Directory>
r->uri: /bar ; rewrite result /cgi/printenv.pl

W/o the PT-flag: regardless if PATH_INFO was specified, SCRIPT_NAME contained /bar.
With the PT-flag: regardless if PATH_INFO was specified, SCRIPT_NAME contained /cgi/printenv.pl


I tried to reproduce an empty SCRIPT_NAME with a rule in per-servr context, but that seems to fail in my environment, I don't know why. Instead, SCRIPT_NAME contained the unchanged r->uri of the initial request if there was no internal redirect or the PT-flag was not set. I think this is correct because mod_rewrite acts in per-server context w/o specifying the PT-flag like an Alias. To my understanding, SCRIPT_NAME represents the physical web view and if it's Alias'd it seems to me complicated if not impossible to get a physical web view.
Comment 12 Michael Orlitzky 2023-02-18 04:13:21 UTC
Hi, this is still a problem 15 years later.