skip to Main Content

I have an HTTP-API, written in PHP and I redirect all calls to my index.php where a (Slim-)router does the rest. That works.

My redirection in .htaccess looks like:

Options +FollowSymLinks
Options -Indexes

RewriteEngine on
RewriteRule ^ index.php [QSA,L]

There is only one problem: If one endpoint is named identically with an existing folder, strange things happen. That’s the case when I create an endpoint [PUT] /test, while there is also a folder called test, containing unit tests.

This is what is happening:

  • If I call [PUT] /test with preflight I get no response at all for the OPTIONS call which leads my app to fail. That’s the real problem case, since I need CORS.
  • If I call it without preflight and PUT I get 301 forwarding on the same endpoint.
  • If I call it without preflight and POST I get 301 forwarding on the same endpoint, but with GET?!

I guess I have to change something in .htaccess to make it ignore the existence of the folder, but what?

How to reproduce

Have an Apache server and create directory as following:

  • /target
  • /target/.htaccess
Options +FollowSymLinks
Options -Indexes

RewriteEngine on
RewriteRule ^ index.php [QSA,L]
  • /target/index.php
<?php echo $_SERVER['REQUEST_URI'];
  • /target/subfolder

  • /testscript.html

<!DOCTYPE HTML>
<html lang="en">
<head>
    <title>test</title>
    <style>
        div {margin: 0.5em;}
        .error { background: rgba(255, 0, 0, 0.3);}
        .success { background: rgba(0, 255, 0, 0.3);}
    </style>
</head>
<body>


<script>
    const call = (url) => {

        fetch(url, {
            redirect: "manual",
            method: "POST"
        }).then(async response => {
            const className = (response.redirected) ? 'success' : 'error';
            const content = await response.text();
            document.querySelector('body').innerHTML += `<div class='${className}'>${url}<hr /><b>[${response.status}]</b> ${content}</div>`;
        });
    };

    call('target/endpoint');
    call('target/subfolder');

</script>

</body>
</html>

Open http://localhost/testscript.html in browser and see: the call on target/subfolder changed it’s method. Also when you look at network tab in browser console you can see, that there was 301 forwarding. Why? And more important: How can I circumvent this. My desired behavior is a rewrite without any changing in terms of method to the index.php in disregard of the existence of a folder with that name. In other words: The same what happens when I call target/endpoint for target/subfolder.

I know about -d (and -f) naturally, but that can be used – as far as I know – to to control behavior depending if file or folder exists – so I would assume without these flags the existence of the directory should not play any role, but it does, apparently.

2

Answers


  1. Chosen as BEST ANSWER

    With the help of @anuhava I found the right way to solve this. His answer alone did not help, but led me to this thread, and I found out the solution war actually to disable automatic trailing slash in .htaccess:

    DirectorySlash Off
    

    I had the additional (not mentioned before) requirement to define a exception directory which should be accessible directly. To make this possible as well, I needed the rule of @anuhava and I endet up with this .htaccess:

    DirectorySlash Off
    
    Options +FollowSymLinks
    Options -Indexes
    
    RewriteEngine on
    
    # add a trailing slash to directories
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule [^/]$ %{REQUEST_URI}/ [L]
    
    # rewrite all calls excpet for exception_dir to index.php
    RewriteCond %{REQUEST_URI} !/target/exception_dir
    RewriteRule ^ index.php [QSA,L]
    

  2. So first the reason of 301:

    1. You’re requesting a URI /target/subfolder which actually points to an directory on your site.
    2. Since your URI is missing a trailing slash. mod_dir module of Apache comes into action and sends a 301 redirect by making it /target/subfolder/. This is done for security reasons to avoid your directory content being exposed on the web.

    To fix, you can just use call with a trailing slash everywhere it points to a directory:

    call('target/subfolder/');
    

    But this cumbersome because front end developers won’t always know these specific cases.

    You can use a generic rule in your .htaccess or Apache config to add this trailing slash:

    # add a trailing slash to directories
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule [^/]$ %{REQUEST_URI}/ [L,R=301,NE]
    

    Make sure this rule is just below RewriteEngine On line and before your existing rules.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search