Cannot ignore internal mod_rewrite redirects with NS flag

I defined a couple of mod_rewrite rules in the .htaccess file to rewrite the URL path from /rwtest/source.html to /rwtest/target.html and the other to block direct access to /rwtest/target.html . That is, all users who want to see the contents of /rwtest/target.html must enter /rwtest/source.html in their URL string.

I tried to use the NS flag in the ban rule to also prevent URL rewriting, but this flag does not seem to distinguish between the first request and the internal redirect. It would seem that NS should do the job, but I’m sure that I don’t understand something.

Can anyone clarify this behavior? What exactly makes this internal redirection not an internal subquery that can ignore the NS flag?

Details:

Here is my full .htaccess file:

 Options +FollowSymLinks -Multiviews RewriteEngine on RewriteBase /rwtest # Forbid rule. Prohibit direct access to target.html. Note the NS flag. RewriteRule ^target.html$ - [F,NS] # Rewrite rule. Rewrite source.html to target.html. RewriteRule ^source.html$ target.html 

I am running Apache 2.4.9 on Windows 7 x64, but I have observed similar behavior on Apache 2.4.3 on Linux. Here Log /rwtest/source.html request to /rwtest/source.html .

 [rewrite:trace3] [rid#20b6200/initial] [perdir C:/Apache24/htdocs/rwtest/] strip per-dir prefix: C:/Apache24/htdocs/rwtest/source.html -> source.html [rewrite:trace3] [rid#20b6200/initial] [perdir C:/Apache24/htdocs/rwtest/] applying pattern '^target.html$' to uri 'source.html' [rewrite:trace3] [rid#20b6200/initial] [perdir C:/Apache24/htdocs/rwtest/] strip per-dir prefix: C:/Apache24/htdocs/rwtest/source.html -> source.html [rewrite:trace3] [rid#20b6200/initial] [perdir C:/Apache24/htdocs/rwtest/] applying pattern '^source.html$' to uri 'source.html' [rewrite:trace2] [rid#20b6200/initial] [perdir C:/Apache24/htdocs/rwtest/] rewrite 'source.html' -> 'target.html' [rewrite:trace3] [rid#20b6200/initial] [perdir C:/Apache24/htdocs/rwtest/] add per-dir prefix: target.html -> C:/Apache24/htdocs/rwtest/target.html [rewrite:trace2] [rid#20b6200/initial] [perdir C:/Apache24/htdocs/rwtest/] trying to replace prefix C:/Apache24/htdocs/rwtest/ with /rwtest [rewrite:trace5] [rid#20b6200/initial] strip matching prefix: C:/Apache24/htdocs/rwtest/target.html -> target.html [rewrite:trace4] [rid#20b6200/initial] add subst prefix: target.html -> /rwtest/target.html [rewrite:trace1] [rid#20b6200/initial] [perdir C:/Apache24/htdocs/rwtest/] internal redirect with /rwtest/target.html [INTERNAL REDIRECT] [rewrite:trace3] [rid#20ba360/initial/redir#1] [perdir C:/Apache24/htdocs/rwtest/] strip per-dir prefix: C:/Apache24/htdocs/rwtest/target.html -> target.html [rewrite:trace3] [rid#20ba360/initial/redir#1] [perdir C:/Apache24/htdocs/rwtest/] applying pattern '^target.html$' to uri 'target.html' [rewrite:trace2] [rid#20ba360/initial/redir#1] [perdir C:/Apache24/htdocs/rwtest/] forcing responsecode 403 for C:/Apache24/htdocs/rwtest/target.html 

Bypass

I posted some workarounds below .

+8
url-rewriting apache mod-rewrite
source share
1 answer

There are several workarounds for this, each of which has its pros and cons. As a disclaimer, I tested them only in the context of .htaccess .


Workaround 1. Check for an empty REDIRECT_STATUS

Add a RewriteCond check to see if %{ENV:REDIRECT_STATUS} empty. If it is empty, then the current request is not an internal redirect.

Pros

  • The most direct way to determine internal redirection.

Against

  • Lack of documentation. The Custom Error Response page briefly describes this variable:

    REDIRECT_ environment variables are created from environment variables that existed before the redirect. They are renamed with the prefix REDIRECT_ , i.e. HTTP_USER_AGENT becomes REDIRECT_HTTP_USER_AGENT . REDIRECT_URL , REDIRECT_STATUS and REDIRECT_QUERY_STRING guaranteed to be set, and the remaining headers will be set only if they existed before the error condition.

    I tried every other REDIRECT_ variable in RewriteCond , but all of them, except REDIRECT_STATUS , were empty for internal redirects. Why REDIRECT_STATUS is special in mod_rewrite remains a mystery.

Example

 # Forbid rule. Prohibit direct access to target.html. RewriteCond %{ENV:REDIRECT_STATUS} ^$ RewriteRule ^target.html$ - [F] # Rewrite rule. Rewrite source.html to target.html. RewriteRule ^source.html$ target.html 

Credits for this approach are moving to rewriting URLs: an internal server error .


Workaround 2. Stop rewriting rule processing using END

Unlike the L flag, END stops rewriting rules even for internal redirects.

Pros

  • Plain. Only an additional flag.

Against

  • It does not give you enough control over which rules to process and what to skip.

Example

 # Forbid rule. Prohibit direct access to target.html. RewriteRule ^target.html$ - [F] # Rewrite rule. Rewrite source.html to target.html. RewriteRule ^source.html$ target.html [END] 

See the END flag for more information.


Workaround 3. Match source URL in THE_REQUEST file

%{THE_REQUEST}

The full string of the HTTP request sent by the browser to the server (for example, "GET / index.html HTTP / 1.1").

THE_REQUEST does not change with internal redirects, so you can match it.

Pros

  • It can be used to match the source URL even in the second round of URL processing.

Against

  • Much more complicated than other approaches. Forces to use RewriteCond , where one RewriteRule would be enough.

  • Matches a full URL that has not been canceled (decoded), unlike most other variables.

  • It is inconvenient to use in several RewriteRule s. RewriteCond can be copied over each RewriteRule or a value can be exported to an environment variable (see example). Both hacker alternatives.

Example

 # Forbid rule. Prohibit direct access to target.html. RewriteCond %{THE_REQUEST} "^[^ ]+ ([^ ?]*)" # extract path from request line RewriteCond %1 ^/rwtest/target.html$ RewriteRule ^ - [F] # Rewrite rule. Rewrite source.html to target.html. RewriteRule ^source.html$ target.html 

Or export the path to the environment variable and use it in multiple RewriteRule s.

 # Extract the original URL and save it to ORIG_URL. RewriteCond %{THE_REQUEST} "^[^ ]+ ([^ ?]*)" # extract path from request line RewriteRule ^ - [E=ORIG_URL:%1] # Forbid rule. Prohibit direct access to target.html. RewriteCond %{ENV:ORIG_URL} ^/rwtest/target.html$ RewriteRule ^ - [F] # Rewrite rule. Rewrite source.html to target.html. RewriteCond %{ENV:ORIG_URL} ^/rwtest/source.html$ RewriteRule ^ target.html 
+9
source share

All Articles