How to access directory's index.php without a trailing slash AND not get 301 redirect
Asked Answered
S

3

16

I have this structure: site.com/api/index.php. When I send data to site.com/api/ there is no issue, but I imagine it would be better if the api would work without the trailing slash also, like this: site.com/api. This causes a 301 redirect and thus loses the data (since data isn't forwarded). I tried every re-write I could think of and couldn't avoid the redirect. This is my current re-write rule (though it may be irrelevant).

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^api/(.*)$ api/index.php [L]

Can I make this url work and maintain the post data without using the trailing slash?

Some rewrites that didn't work: (all still redirect)

RewriteRule ^api$ api/index.php [L] 
RewriteRule ^api/*$ api/index.php [L]
Sciomachy answered 4/9, 2013 at 23:27 Comment(5)
The trailing slash is added by the apache module mod_dir: httpd.apache.org/docs/2.2/mod/mod_dir.htmlSensitive
Also, your rewrite rule, the way it is written, has the trailing slash as part of the rule. You could probably just remove the / from the rule and it would work.Sensitive
Right. I was thinking that I could rewrite a request to that directory right to index.php so that the module never kicks in, but it didn't work. I'll update to show what I tried there.Sciomachy
Where is this .htaccess file located? It would have to be in the parent directory of api. Also, the !-d condition would probably have to be removed. that means if the requested file is not a directory and /api is a directory so the condition will not match.Sensitive
It is in the parent directory and I have tried without the conditions.Sciomachy
T
14

You'd first need to turn off directory slash, but there's a reason why it is very important that there's a trailing slash:

Mod_dir docs:

Turning off the trailing slash redirect may result in an information disclosure. Consider a situation where mod_autoindex is active (Options +Indexes) and DirectoryIndex is set to a valid resource (say, index.html) and there's no other special handler defined for that URL. In this case a request with a trailing slash would show the index.html file.But a request without trailing slash would list the directory contents.

That means accessing a directory without a trailing slash will simply list the directory contents instead of serving the default index (e.g. index.php). So if you want to turn directory slash off, you have to make sure to internally rewrite the trailing slash back in.

DirectorySlash Off

RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*[^/])$ /$1/

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^api/(.*)$ api/index.php [L]

The first rule ensures that the trailing slash gets appended to the end, though only internally. Unlike mod_dir, which externally redirects the browser, the internal rewrite is invisible to the browser. The next rule then does the api routing, and because of the first rule, there is guaranteed to be a trailing slash.

Transpose answered 5/9, 2013 at 0:12 Comment(6)
Is this certainly the only way to avoid the redirect while the module is enabled?Sciomachy
Also, is it common for shared hosts to allow this change? I have found them to be strict about certain things like that.Sciomachy
@Sciomachy There is no way to avoid the redirect while mod_dir's DirectorySlash is turned on. You can avoid the redirect by turning it off, but mod_dir processes the URI before mod_rewrite does (almost always) so there's no way around it. I don't now how strict hosting services are about this. But DirectorySlash is part of the "Indexes" override, so it's most likely they allow that.Transpose
Should it be ^(.*[^/])$ $1/ without the leading slash? The leading slash was causing me to visit the previous directory.Sciomachy
@Sciomachy if your htacces file isn't in your document root, then you don't want the leading slash (your question made it appear that everything was in the document root)Transpose
Hmmm. Always more complexity haha. My project needs to be portable. My .htaccess is at the root of the app, but not the document root (at the moment). Whatever I do here, it has to remain functional whether the app folder is at the document root or nested. If you want to save me terrible time of figuring that out, I'd appreciate it, but I can mess with it if I'm asking too much! I'm guessing I'd need to add a condition?Sciomachy
L
1

My Next.js example has additional comments, with additional RewriteOptions AllowNoSlash for UX links, like /login, to work, instead of ugly /login/:

<VirtualHost *:443>
  ServerName my.local

  # For basePath+assetPrefix in next.config.js.
  Alias "/agent" "/www/sites/my"
  <Directory "/www/sites/my">
    Require all granted

    # Prevent 301 redirect for directories without slashes.
    DirectorySlash Off
    # mod_dir doesn't help - it expects a traling slash.
    # DirectoryIndex index.html
    # Prevent directory listing - it is a security flaw / UX unfriendly.
    Options -Indexes

    # For trailingSlash:true + skipTrailingSlashRedirect:true in next.config.js.
    RewriteEngine On
    # LogLevel rewrite:trace8
    # Transfrom logical paths: /login => /login/index.html.
    RewriteOptions AllowNoSlash
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^(.*)$ $1/index.html [L]
  </Directory>

  ProxyPass "/api" "http://192.168.1.2:8080/api"
  ProxyPassReverseCookieDomain "192.168.1.2" "api.local"
</VirtualHost>
Lietman answered 11/3 at 11:52 Comment(0)
K
0

If you do not want to use the solution provided by Jon Lin (reconfiguring all your URLs pointing to directories), you could use the following code (note the ? in the regexp - it basically says that the trailing slash after "api" is optional). I have not tested it, but it should work as it is:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^api/?(.*)$ api/index.php [L]
Krishnakrishnah answered 5/9, 2013 at 0:27 Comment(3)
I did try exactly that. The optional slash doesn't help, nor removing the slash altogether to be certain.Sciomachy
I'm pretty sure that doesn't work because the module kicks in and redirects before the rewrite gets a chance to fix it.Sciomachy
@Sciomachy I think the reason you were redirected afterwards is because your browser had cached the 301 response, so it automatically redirected to URL with trailing slash. You should test in curl or a fresh browserHuihuie

© 2022 - 2024 — McMap. All rights reserved.