Search code examples
apache.htaccessmod-rewrite

rewrite request for /folder to folder/index.php without 301 redirect with apache


So I put an index.php in /pipe/index.php

I'd like to rewrite (internal, not redirect) https://host/pipe?token=abc to https://host/pipe/index.php?token=abc

what I tried (caveat, assumes there is always a ? in the url):

RewriteEngine on
RewriteRule "^([^?]*)(.*)$" "$1/$2" [PT]

my hope was to split at the ? and just insert a / there.

But it seems apache finds out that "oh, pipe is a folder" before checking my .htacces (?) Because despite my [PT] it still redirects with 301 to /pipe/?token=abc, when I hoped for internal rewrite.


Solution

  • But it seems apache finds out that "oh, pipe is a folder" before checking my .htacces (?)

    Yes, mod_dir will append the trailing slash with a 301 redirect. Although this occurs after mod_rewrite has processed the URL (if indeed it is being processed at all - see below). (The PT flag is irrelevant in .htaccess, since the resulting rewrite is passed through as a URL-path by default.)

    RewriteRule "^([^?]*)(.*)$" "$1/$2" [PT]
    

    However, your existing rule (by itself) would result in a rewrite-loop (500 Internal Server Error) since it matches itself and repeatedly appends a slash. If you are seeing a 301 redirect as mentioned above then either this rule is not doing anything (are .htaccess overrides enabled?) or you have a conflict with other rules.

    As you've stated, this rule also assumes that the query string (with leading ?) is also matched by the RewriteRule pattern. The RewriteRule directive matches against the URL-path only, not the query string. $2 in the above rule is therefore always empty (unless you have %3F in the URL-path, ie. a %-encoded ?).

    The query string is contained in its own variable, QUERY_STRING. But you simply want to pass through the same query string, so you don't need to do anything special here, since that happens by default.


    Solution

    To prevent mod_dir appending the trailing slash, you need to set DirectorySlash Off at the top of the root .htaccess file.

    Note that these directives must go in the .htaccess file in the root/parent directory, as opposed to the subdirectory that has the trailing slash omitted. This is because the mod_rewrite directives (that "fix" the URL by appending the trailing slash) would never actually be processed in the subdirectory .htaccess file. The trailing slash would seem to be required for mod_rewrite to function. (However, the mod_dir DirectorySlash Off directive would still be processed successfully, so the slash would not be appended.)

    For example:

    # Prevent mod_dir appending the trailing slash
    DirectorySlash Off
    
    # Must disable directory listings when "DirectorySlash Off" is set
    Options -Indexes
    

    However, you need to then manually append the trailing slash to any directory, where it is omitted, with an internal rewrite to "fix" the URL (and to correctly serve the DirectoryIndex document, ie. index.php).

    # Ensure DirectoryIndex is set correctly
    DirectoryIndex index.php
    
    RewriteEngine On
    
    # Append trailing slash to any directory where it has been omitted
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^(.+[^/])$ $1/ [L]
    

    The trailing slash on the directory (via the internal rewrite) is required in order to serve the DirectoryIndex document, otherwise, you get a 403 Forbidden, even if the DirectoryIndex document is present.

    If the trailing slash is omitted and directory listings (mod_autoindex) are enabled (disabled above) then a directory listing would be generated even if a DirectoryIndex document is present in that directory. (Which is why directory listings must be disabled when DirectorySlash Off is set.)

    NB: You will need to make sure the browser cache is cleared since the earlier 301 redirect by mod_dir to append the trailing slash will have been cached by the browser.