2

So I have the below code in my .htaccess file in my local apache XAMPP server in the root directory and it works 100% correctly. However, when I try to deploy to Heroku it does not work. I have tried slightly modified versions of this found on the internet to perform the same thing, but none of them seem to work. This is supposed to remove .php file extensions and allow things like /api/products/all where the file api.php exists and the products/all are just read in with PHP $_SERVER['PATH_INFO'] and output is returned based on what they are.

Options +MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]

I don't know a ton about .htaccess so any help would be appreciated.

Apickle
  • 21
  • 3

1 Answers1

1

This is supposed to remove .php file extensions

Well, strictly speaking, this code is intended to add the .php file extension back again (in order to route the URL). (You've already "removed" it by making a request for /api/products/all.)

However, this code isn't doing what you think it's doing. This is only working on your local server because of the MultiViews line (part of mod_negotiation). The mod_rewrite code that follows is not doing anything at all, which is possibly why this is not working for you on Heroku?

If the mod_rewrite directives were allowed to run (ie. if MultiViews was disabled) then this would rewrite a request for /api/products/all to /api/products/all.php, which is clearly not correct in this example.

I don't know what other URL formats you are trying to handle, but to specifically route a URL of the form /api/products/all to /api.php/products/all using mod_rewrite, you would need something like the following instead:

RewriteEngine On
RewriteRule ^(api)/(.+) $1.php/$2 [L]

The RewriteCond directives are not required if you are only matching this specific URL.

...and the "products/all" are just read in with php

This is called additional pathname information and is managed/collected by the server and passed onto PHP in the PATH_INFO element of the $_SERVER superglobal. Whether path info is permitted at all is dependent on the server. (If not then such URLs would result in a 404.)

UPDATE#1:

...if I also have files such as example.js.php (where I use php code, but render them as javascript files) and I would like their .php to be removed so they can be accessed via example.js how would I modify the rewrite rule?

You could add an additional rule block like the following:

RewriteCond %{DOCUMENT_ROOT}/$1.$2.php -f
RewriteRule (.+)\.(js)$ $1.$2.php [L]

This intercepts all requests for .js files and rewrites the request to the corresponding .js.php file if it exists.

Alternatively, you could specify the RewriteCond directive like this:

RewriteCond %{REQUEST_FILENAME}.php -f

Note that if your .js files are also located under a URL-path that starts /api/ then this rule will need to go before the above rule that routes your api calls, in order to avoid a conflict.

UPDATE#2: You may also need to set the correct mime-type for these resources. (Whether it works or not with the wrong mime-type is browser dependent.) Since the .js URL is being internally rewritten to a PHP script (ie. .js.php) Apache will naturally serve this with a text/html mime-type (as it does for all PHP files by default). You can either make sure you are explicitly setting the correct Content-Type header from within your script, or set this in .htaccess using the Header directive (mod_headers). For example:

<FilesMatch "\.js.php$">
    Header set Content-Type "application/javascript; charset=UTF-8"
</FilesMatch>

Note that this will only work on Apache 2.2.12+ (before this version it simply wasn't possible to set the Content-Type header with the Header directive).

Note also, that the "simpler" method of setting the mime-type using the T flag on the RewriteRule doesn't work in a per-directory/.htaccess context - it gets overwritten when the rewriting process starts over.

MrWhite
  • 11,643
  • 4
  • 25
  • 40
  • Appreciate the in depth explanation. But to add on to my original question, if I also have files such as example.js.php (where I use php code, but render them as javascript files) and I would like their .php to be removed so they can be accessed via example.js how would I modify the rewrite rule? – Apickle Aug 08 '17 at 01:05
  • The updated bit does not seem to work for me (just testing locally right now). I also tried just that updated bit without the first part (aside from RewriteEngine On) and still no luck. Is it possible there is syntax error or is there something else I need to include? – Apickle Aug 08 '17 at 01:56
  • What actually happens? What happens if you request the `.js` file directly? If there was a "syntax error" you would get a 500 internal server error. It's possible that your server is not responding with the correct mime-type for this resource. I've updated my answer. – MrWhite Aug 08 '17 at 12:08
  • It gives an error 404 if I try to access `example.js`. If I just try to access`example.js.php` it works. I already set the content type header's to be application/javascript within the `.js.php` files. Do it matter that my local server is on windows, but the remote server is on linux. – Apickle Aug 09 '17 at 00:04
  • Was this working before with the `Options +MultiViews` directive? Where is the `.htaccess` file? I'm assuming it's in the "document root" of the site? Presumably, you've tested this on both your local (Windows) server and the "live" (Linux) server, and neither work? Whether it's Windows or Linux should not matter here. (Just to clarify, you're not linking to the remote server from the local server? ie. JS files stored remotely?!) – MrWhite Aug 09 '17 at 10:11
  • Yes this worked locally with the `Options +MultiViews`. The `.htaccess` file is in the document root. That is correct that neither work, I was just making sure there isn't different syntax for windows/linux. No, I am not linking to the remote server from the local server. – Apickle Aug 09 '17 at 22:27
  • Sorry for the delay getting back to this... Not sure why this wouldn't be working, other than perhaps a conflict with URL-paths? Where are your scripts located? And what URL-path are you specifying the HTML? If your `.js.php` files are also located under the URL-path `/api/...` then you will need to check the order of these directives, as the routing of your js file will need to be first. If all your `.js` files are rewritten to the corresponding `.js.php` script then you can remove the preceding `RewriteCond` directive altogether. I'll update my answer. – MrWhite Aug 14 '17 at 18:08