RewriteRule - discard trailing slash - php

I have a PHP project with a .htaccess file:
Options FollowSymLinks
RewriteEngine On
RewriteBase "/blog/"
RewriteRule "^([^.?/]+)/?$" "index.php?page=$1" [END,QSA]
I have a very strange problem with url localhost/blog/administration. It's definitely not caused by a PHP file, because happens even with empty source code.
My problem is: when I type localhost/blog/administration, the URL in address bar is changed to localhost/blog/administration/?page=administration. This doesn't happen after typing localhost/blog/administration/ (with trailing slash) in the address bar.
What's stranger - given problem happens only with administration URL but not with, for example, home.
What could be the cause?
DISCLAIMER: Said problem happens even when I use fresh browser (no cache) and index.php with exit; at the beggining...

Trailing slash is added at end of URI if that points to a real directory by Apache's mod_dir module for security reasons otherwise it may result in directory listing of your directory.
It is advisable to have index.php inside your /blog/administration/ directory to prevent this behavior. Otherwise you can have a separate rule to add trailing rule before final rule:
Options FollowSymLinks -Indxes
RewriteEngine On
RewriteBase /blog/
# add a trailing slash to directories
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule [^/]$ %{REQUEST_URI}/ [L,R=301]
RewriteRule ^([^./]+)/?$ index.php?page=$1 [END,QSA]
Also take note of -Indxes in options line above to disable directory listing.

Related

htaccess - Force a slash to the end of a dynamic URL

I have a single index.php file in a /slug subdirectory and would like to load dynamic content based on the file path. Regardless of what the url is, the content should reference that index.php.
In my code below, the slash is not being added at the end of the url. For example, example.com/slug/33 should be displayed in the address bar as example.com/slug/33/.
I have the following .htaccess in /slug:
Options -Indexes
# Turn mod_rewrite on
RewriteEngine On
RewriteBase /
# Dynamic url
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /slug/index.php/?path=$1 [NC,L,QSA]
I tried adding a / between index.php and ?path=$ but I'm not getting the desired result. Is this even possible?
RewriteRule ^(.*)$ /slug/index.php/?path=$1 [NC,L,QSA]
Changing the substitution string here changes the target of your internal rewrite - it does nothing to change the visible URL. By adding a slash after index.php you are (unnecessarily) adding additional pathname information (path-info) to the resulting URL that your application receives.
To change the visible URL (to append the slash) you need to implement an external redirect. However, to confirm... you must already be linking to the correct canonical URL (ie. with a slash) in your internal links. Appending the slash to the URL in .htaccess is only if you have changed the URL and search engines or 3rd parties are still using the old non-canonical URL (without a trailing slash).
Since the .htaccess file is in the /slug subdirectory and you are rewriting to index.php in that subdirectory then you don't need to prefix the rewritten URL with /slug/. By default, a relative URL-path is relative to the directory that contains the .htaccess file. However, you must also remove the RewriteBase directive (or set this "correctly" to RewriteBase /slug).
To redirect to append a trailing slash you can add the following before the current rewrite:
# Append trailing slash if omitted
RewriteRule ^(.*(?:^|/)[^/.]+)$ /slug/$1/ [R=301,L]
This requires the /slug/ prefix on the substitution string (unless RewriteBase /slug is set), otherwise the external redirect will attempt to redirect to a file-path, which will "break".
The RewriteRule pattern ^(.*(?:^|/)[^/.]+)$ captures URL-paths that do not already end in a slash and do not contain a dot in the last path segment. This is to avoid matching URLs that already contain (what looks-like) a file extension, ie. your static resources (images, CSS, JS, etc.). This should avoid the need for a filesystem check (which are relatively expensive) - to check that the request does not already map to a file. Although, if you are not referencing any static resources with the /slug/ prefix in the URL then this can be simplified.
NB: You should first test with a 302 (temporary) redirect to avoid potential caching issues.
In context (with the use of RewriteBase):
Options -Indexes
# Turn mod_rewrite on
RewriteEngine On
RewriteBase /slug
# Append trailing slash if omitted
RewriteRule ^(.*(?:^|/)[^/.]+)$ $1/ [R=301,L]
# Dynamic url
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.+) index.php?path=$1 [QSA,L]
The use of RewriteBase avoids you having to specify /slug/ in the other directives.
In the regex ^(.*)$, the start-of-string (^) and end-of-string ($) anchors are superfluous. And you might as well change this to use the + quantifier, since you don't want to match the base directory anyway (saves two additional filesystem checks). The NC flag was also superfluous here.

RewriteRule in htaccess fails, but it could be my host

I read that Self Sabotage is Not asking for help so here I am.
So, I have a site...it's working great using WAMP. It's working great on my current host. But I need to switch to a new host and now it's failing. I figured it's a .htaccess issue but now I'm not sure. On my current host I have no .htaccess file and it works great. On my localhost server using WAMP I had the same thing as on my new host but I just disabled the .htaccess file, renaming it to BAD.htaccess, and the site still works great. This is why I think it's a server-side problem and I need some help. On my WAMP server in vhosts I disabled +FollowSymLinks for that "domain". On my current host I had no easy way to do that so it's just whatever they gave me, but it works.
I am currently with Ionos and have switched to GreenGeeks, who use cPanel. So far I haven't found a vhosts file to edit to remove +FollowSymLinks, if that is even the problem.
Maybe it can be accomplished with .htaccess and if so here is what I need to do. First my current .htaccess:
Options +FollowSymLinks
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^poems$ poems.php [R]
#RewriteRule ^poems/$ poems.php
RewriteRule ^collections$ collections/ [R]
RewriteRule ^collections/$ collections.php
RewriteRule ^poem$ poem/ [R]
RewriteRule ^poem/$ poem.php
RewriteRule ^poem/([0-9]+)/([a-zA-Z])$ poem.php?num=$1&poem=$2 [NC,L]
RewriteRule ^collection$ collection/ [R]
RewriteRule ^collection/$ collection.php
# RewriteRule ^poem/([0=9]+)$ indpoem.php?num=$1 [NC,L]
With the first two setups I can go to example.com/poems and it will redirect or rewrite to example.com/poems.php but still look like example.com/poems. Same with collections. On the new host those rewrite rules do rewrite it but the URL or URI shows example.com/poems.php, which I don't want per current SEO standards. Still, I could live with that.
However, when I get to the next level... example.com/poem/#/poem-name it fails on my new host. I do have a file called poem.php which it should rewrite to. In that file I use the following to get the # and name...
$URL = explode("/",$_SERVER['REQUEST_URI']);
So I don't have to do a _GET.
As you can see I tried to do a RewriteRule to change it from the first to example.com/poem?111&name, but that just seems silly because on WAMP I don't have to do anything. I could try rewriting it to the same URL again, but I have a feeling that won't work. And if it does it will probably be poem.php/#/name/
Any thoughts on a server config I'm missing when using cPanel. I even tried doing
Options -FollowSymLinks
in my .htaccess file with no success.
Any help would be appreciated. My WAMP and the new host have all the most recent versions of Apache and PHP.
There are a few issues here...
For the site to work without the .htaccess file at all (on your WAMP dev server and current host) then MultiViews (part of mod_negotiation) must have been enabled. It is MultiViews that "rewrites" /foo to /foo.php.
If MultiViews is enabled then your current .htaccess file is essentially overridden since the MultiViews content-negotiation occurs before your mod_rewrite directives are processed so your RewriteRule patterns fail to match.
MultiViews is disabled by default on Apache, it needs to be "explicitly" enabled. Unfortunately, some shared hosts do enable this in the server config (which causes more problems than it fixes - if you are not expecting it.)
On the new host those rewrite rules do rewrite it but the URL or URI shows example.com/poems.php.
RewriteRule ^poems$ poems.php [R]
Because MultiViews is disabled and your directives externally "redirect" /poems to /poems.php. There is no "rewrite" here. The R (redirect) flag triggers an external redirect.
RewriteRule ^poem$ poem/ [R]
RewriteRule ^poem/$ poem.php
However, for /poem you are redirecting to /poem/ (appending the trailing slash) but you have omitted the L flag so processing continues and the request is further rewritten to /poem.php. But because you have already triggered a Redirect, the request is "redirected" (not rewritten) to /poem.php, again exposing the .php.
Redirects should nearly always include the L flag. In fact, all your rules should include the L flag for optimisation and to prevent accidental conflicts.
Why are you redirecting to append the trailing slash (as if this is the preferred canonical URL)? You make no mention of this in your question text and only one of your examples includes a trailing slash on the URL as far as I can see? So, what is the preferred/canonical URL? What URL are you linking to? Incidentally, MultiViews will not append a trailing slash - so either this redirect is not required, or your site is not actually "working great" on WAMP / current host without the .htaccess file. (?) (Personally, I would not use a trailing slash.)
RewriteRule ^poem/([0-9]+)/([a-zA-Z])$ poem.php?num=$1&poem=$2 [NC,L]
:
$URL = explode("/",$_SERVER['REQUEST_URI']);
Your PHP script parses the requested URL-path (ie. $_SERVER['REQUEST_URI']), it is not referencing the query string. However, the above RewriteRule directive is rewriting to a query string - which would seem to be entirely superfluous ("silly" - as you suggest). Because you are not using the query string in your PHP script, the request still "works" when using MultiViews.
According to your PHP script, you simply need to rewrite the request to poem.php, without a query string. (Which is what MultiViews does. Although, strictly speaking, MultiViews rewrites /poem/123/name to /poem.php/123/name - passing the additional URL-path as path-info to poem.php.)
This regex only matches a single letter in the 3rd (name) path segment so it will fail to match a requested URL of the form /poem/123/name, so the request is not rewritten to poem.php and the request fails (with a 404 I suspect).
This regex also does not match a trailing slash. (So, is the trailing slash really canonical?)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
These conditions are entirely superfluous where you have put them. RewriteCond directives only apply to the first RewriteRule directive that follows.
Options -FollowSymLinks
You need FollowSymLinks for mod_rewrite to work. Don't try to disable this. FollowSymLinks is actually the default Apache setting, so you only need to explicitly enable it (ie. +FollowSymLinks) in .htaccess if it has been disabled in the server config.
Solution
Personally, I would not use a trailing slash on the canonical URLs. (This is in line with most of your examples and the regex used in your rule.)
So, bringing the above points together:
Options +FollowSymLinks -MultiViews
RewriteEngine on
# Canonical redirect to remove trailing slash (from non-directories)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)/$ /$1 [R,L]
# Internal rewrites to ".php"
RewriteRule ^poems$ poems.php [L]
RewriteRule ^collections$ collections.php [L]
RewriteRule ^poem(/\d+/[\w-]+)?$ poem.php [L]
RewriteRule ^collection$ collection.php [L]
On the other hand, if the canonical URL should include a trailing slash then change it accordingly:
Options +FollowSymLinks -MultiViews
RewriteEngine on
# Canonical redirect to append trailing slash to "all" URLs
RewriteRule !/$ ${REQUEST_URI}/ [R,L]
# Internal rewrites to ".php"
RewriteRule ^poems/$ poems.php [L]
RewriteRule ^collections/$ collections.php [L]
RewriteRule ^poem/(\d+/[\w-]+/)?$ poem.php [L]
RewriteRule ^collection/$ collection.php [L]
If you have many such URLs/pages then you can make this entirely "generic" without having to explicitly name each URL/file. ie. "poems", "collections", etc.
Alternatively, you simply enable MultiViews and let mod_negotiation rewrite the URLs. However, you will not be able to canonicalise the trailing slash or validate the request before rewriting and MultiViews applies to everything, not just your .php files, so potentially creates duplicate content. If you need to do any specific rewriting, such are rewriting the query string then MultiViews is likely to conflict.
Options +MultiViews

how to remove the .php extension from url, for a file named same like a folder

I used this code
## remove the php extention
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php
This works for some files the e.g. example.com/contact, but doesn't work when I have a .php file that is also a directory. For example, in the root folder:
science.php
science - folder
The articles are in categories e.g. http://example.com/science/themost-blabla.php - this works, the .php extionsion doesn't appear in the URL.
So I want to know if is any possible to hide the .php extension to science.php because when I type example.com/science ... it redirects me to the content of the science folder....
Index of /science directory:
afla-care-a-fost-primul-meci-televizat-de-fotbal-din-lume-1937-arsenal.php
cazinoul-din-constanta.php cele-7-minuni-ale-lumii.php
descoperire-colosala-a-epavei-navei-spaniole-san-jose-ce-avea-la-bord-o-avere-impresionanta.php
imagini/ mitologia-greaca.php poenaru.php
top-10-cele-mai-importante-inventii-romanesti-din-istorie.php
top-5-enigme-ale-lumii.php turnul-eiffel.php
So, can I do something to hide the extension to this page? Or do I need to change the name of the file - to not be the some as the folder?
Try this
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
One of the "problems" is that mod_dir will try to "fix" the URL when accessing a directory by appending a slash to the end of the URL. However, this can be disabled.
# Prevent mod_dir from automatically appending slashes to directories
DirectorySlash Off
# Disable directory listings
# In cases where there is a directory with no similar .php file and no DirectoryIndex
Options -Indexes
# If a PHP file exists for the requested URL then rewrite to this
RewriteEngine On
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule (.*) $1.php [L]
If required, bare directories (or rather, the DirectoryIndex) can be accessed by explicitly appending a slash to the URL. eg. example.com/science/. However, this is presumably unnecessary (and probably best avoided to avoid user confusion) since I assume example.com/science (no trailing slash, ie. science.php) returns your "science" category content. Without a DirectoryIndex document, example.com/science/ will simply return a 403 Forbidden. Alternatively you could explicitly remove trailing slashes from such URLs with an external redirect.

mod_rewrite into subdirectory file from folder

This could be probably duplicate due to unable to find that original even after checked the similar questions list of stackoverflow. Actually if user type myipaddress/gt then it should redirect to myipaddress/gt/gt.htm
I tried various mod_rewrites but still gets the 'gt' folder contents listed in browser. I gave the last try with below mod_rewrite.
Options +FollowSymlinks
RewriteEngine On
RewriteBase /
RewriteRule ^gt$ gt/gt.htm [NC,L]
But still the browser shows the contents of the 'gt' folder. What am I doing wrong?
You can use this rule in /gt/.htaccess:
Options +FollowSymlinks
RewriteEngine On
RewriteBase /gt/
RewriteRule ^/?$ gt.php [L]
Due to the fact that Apache sees gt as a directory, it treats it as such. However, that doesn't stop all rewriting for that particular path.
This is the basic process:
You navigate to /gt.
Apache sees that as an existing directory, and thus redirects to /gt/.
Your rule is ignored, because it doesn't contain the trailing slash.
This is why you still see the directory listing.
As such, you should change your rule to this:
RewriteRule ^gt/$ gt/gt.php [NC,L]
Alternatively, you can make use of DirectorySlash off and make the trailing slash optional, like so:
Options +FollowSymlinks
DirectorySlash off
RewriteEngine On
RewriteBase /
RewriteRule ^gt/?$ gt/gt.php [NC,L]
Doing either of the above allows you to have multiple rules of a similar nature in a single .htaccess file.

mod_rewrite with Trailing Slash breaks CSS/IMG/SCRIPTS paths

I'm trying to make mod_rewrite the first sub-directory string from url in order to create similar functionality as 'jsfiddle.net saved url's within a class/db. The script works fine and does the rewrite.
e.g. of url
http://jsfiddle.net/RyEue/
This works fine (loads all css, scripts, etc.):
http://www.domain.com/787HHJ2
This is what I've used in the past which does the trick.
The problem Is when ending URL with last slash, script, css and others loose path.
http://www.domain.com/787HHJ2/
rewrite script:
DirectoryIndex index.php index.html
Options +FollowSymlinks
RewriteEngine On # Turn on the rewriting engine
#RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{QUERY_STRING} !.
RewriteRule ^.+/?$ index.php [QSA,L]
Unsure if this has to do with Rewritebase, I've tried multiple ways.
PS. I've tried setting paths to absolute (e.g. src="/img/theimage.jpg") without luck.
1. Make sure you have css/images/js etc linked relative to root folder (with leading slash): /styles/main.css
2. Add one of these ruls before current one:
# do not touch files with .css/.js/.jpg etc extensions
RewriteRule \.(css|js|jpg|png|gif)$ - [L]
or
# do not touch any resources in images/css/js folders
RewriteRule ^(images|css|js)/ - [L]
3. Clear browser caches and restart (sometimes browser may display cached page/resource when rewrite rule was fixed which brings a lot of confusion).
Try escaping
RewriteRule ^.+\/?$ index.php [QSA,L]

Categories