mod_rewrite existing directory changes URL in address bar - .htaccess

I'm trying get mod_rewrite to redirect all requests that aren't files to index.php so I can handle routing for clean URLs.
<IfModule mod_rewrite.c>
RewriteEngine On
# Route requests to index.php for processing
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ index.php?request=$1 [QSA,L]
</IfModule>
For some reason, when I access an existing directory without a trailing slash, the address bar is reformed to include a trailing slash and the query string, which is not very clean.
I'm able to improve this by changing the RewriteRule to ^(.+)/$ and adding RewriteBase /. However, this directs all URLs to one with a trailing slash. Not a big deal, but not how I want it.
If, for example, /test/folder exists and I go to that directly, I'd like the address bar to display that, instead of displaying /test/folder/ or /test/folder/?request=/test/folder.

I'd give this a try:
DirectoryIndex index.php
<IfModule mod_rewrite.c>
RewriteEngine On
# I tested this inside a subdir named "stack", so I had to uncomment the next line
#RewriteBase /stack
# Route requests to index.php for processing
# Check if the request is NOT for a file:
RewriteCond %{REQUEST_FILENAME} !-f
# Check if the request IS for an existing directory:
RewriteCond %{REQUEST_FILENAME} -d
# If all criteria are met, send everything to index.php as a GET request whose
# key is "request" and whose value is the entire requested URI, including any
# original GET query strings by adding QSA (remove QSA if you don't want the
# Query String Appended):
RewriteRule .* index.php?request=%{REQUEST_URI} [R,L,QSA]
</IfModule>
If this doesn't happen to work, please let me know what else is in your .htaccess file because for the most part, it looks like it should be working. Should. ;)

Well, persistence pays off. jerdiggity's answer provided insight, which led to further experimentation and research. Eventually, I came to the conclusion that there must be something built into Apache that is rewriting the trailing slash.
As jerdiggity suspected, all of the Rewrite logic was accurate, but something called a DirectorySlash Directive in another directory-related Apache module, mod_dir, was adding the trailing slash.
Apparently, you can simply disable this directive, which I added to the top of my logic:
DirectorySlash Off
RewriteEngine On
...

Related

Redirect parameter to its parent post url

i have several set of error URL with parameter that i need to redirect to the parent post URL
http://www.mysite.co/post1.html?amp=1
http://www.mysite.co/post1.html?amp=0
http://www.mysite.co/post1.html?utm_source=xxxxx
so what im trying to achieve http://www.mysite.co/post1.html?amp=1 (status 404 ) should redirect to http://www.mysite.co/post1.html (200 ok)
i tried to add htaccess code but it always gave me 500 errors, can someone help me with the proper htaccess code
Htacess
# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
current permalinks are set to
/%postname%.html
Parameter in url ?amp, ?utm_source are added by third party service/plugins which resulted 404 error
Try the following at the top of the .htaccess file, before the # BEGIN WordPress section:
RewriteCond %{QUERY_STRING} ^(amp=[01]|utm_source=[^&]+)$
RewriteRule ^[\w-]\.html$ /$0 [QSD,R=301,L]
This matches any URL-path that ends in .html. Only the specific query strings as mentioned in the question are matched. ie. amp=0 or amp=1 or utm_source=<something>. It will not redirect amp=2 or utm_source= or utm_source=<something>&foo=1 etc.
The QSD flag (Apache 2.4) discards the original query string.
Test first with 302 (temporary) redirects to avoid potential caching issues.
UPDATE:
#REDIRECTION UTM CLEAR
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} "utm" [NC]
RewriteRule (.*) /$1? [R=301,L,QSD]
<IfModule mod_rewrite.c>
which one you thing serves better?
What do you mean by "better"?
As written, this code is strictly invalid (and contains superfluous directives). But (when corrected) this code arguably matches too much (and does not handle the amp URL parameter at all). It matches utm anywhere in the query string which could potentially create conflicts with existing code. It also matches any URL-path, so is potentially checking 1000s of requests that don't need checking. eg. It would match /image.jpg?nutmeg=5 and /?scoutmaster=1 - which clearly have nothing to do with utm tracking parameters (which all start utm_).
The code I posted above matches precisely the criteria you've stated in the question. And thus avoids potential conflicts. So, from that perspective, the code I posted above is "better".
However, to match amp or any URL parameter that simply starts utm_ and only whole URL parameters that might occur anywhere in the query string then use something like the following instead:
RewriteCond %{QUERY_STRING} (?:^|&)(amp|utm_\w+)=
RewriteRule ^[\w-]\.html$ /$0 [QSD,R=301,L]
This matches URLs of the form /%postname%.html - your permalink structure. It does not match /image.jpg etc.
You do not need to repeat the RewriteEngine directive. The RewriteBase directive is entirely superfluous. You should not wrap these directives in a <IfModule> container.
Note that if you have any legitimate URL parameters mixed in then they will also be removed.
This matches the following:
/<postname>.html?amp=<anything>
/<postname>.html?utm_source=<anything>
/<postname>.html?utm_campaign=<anything>&bar=1
/<postname>.html?foo=1&utm_<something>=<anything>
etc.
But does not match:
/<postname>.html?wamp=<anything>
/<postname>.html?nutmeg_source=<anything>
/image.jpg?utm_source=<anything>
etc.

.htaccess subdomain Rewrite rule is not working

I am making a website builder an I would like to make urls prettier.
The url would be for example:
https://ccc-bb.example.com => https://example.com/project/show/ccc/bb
This is my .htaccess file:
<IfModule mod_rewrite.c>
RewriteEngine On
# prevents files starting with dot to be viewed by browser
RewriteRule /\.|^\.(?!well-known/) - [F]
# front controller
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)\-(.*)$ https://example.com/project/show/$1/$2 [L]
When I use above (https://ccc-bb.example.com) it sends me to the subdomain empty folder. The folder has only the .htaccess file.
What am I missing? I've never edited an .htaccess file and Google didn't help me (or I don't know what should I looking for).
Your first rule for dotfiles is okay but would be better the other way around, since the second part can only match the start, but the first can only match in subdirectories.
RewriteRule ^\.(?!well-known/)|/\. - [F]
Your other rule's problem is that you are expecting it to match the subdomain. RewriteRules do not operate on the entire string you see in your browser's address bar, only the path part, and in .htaccess they see even less as the leading directory is stripped off, too. To access the info you want, use a RewriteCond:
RewriteCond %{HTTP_HOST} ^([^-]++)-([^-.]++)\.example\.com$
RewriteRule ^(?!project/show/).* project/show/%1/%2/$0 [L,DPI]
(You don't need to include \.example\.com$ if your main domain contains no hyphens.)

.htaccess folder as parameter to root index.php

What I need is any subfolder to be passed as a parameter to the root index.php
This is the code and it actually works.
RewriteEngine On
RewriteBase /
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} -d
RewriteRule ^(.+?)(/[^/]*|)$ index.php?dir=$1/ [L,QSA]
There is a problem:
When the url is like this (no end slash after 'projects'):
http://example.com/projects
the rewrite rule changes the link in the address bar and it looks like this:
http://example.com/projects/?dir=projects/
Is there a chance the url in the address bar always stays the same(no matter if there is an end slash or not) so the dir parameter is not visible to the user?
I tried with multiple rules - the first one to add an end slash, and then the second rule to pass the directory as parameter, but with no luck so far.
EDIT: so far thanks to w3d I managed to get it working. In the .htaccess just add:
DirectorySlash Off
tl;dr Make the trailing slash mandatory in the RewriteRule pattern (and remove the DirectorySlash Off directive, ie. keep it On).
RewriteRule ^(.+)/$ index.php?dir=$1/ [L,QSA]
As suggested in comments, this "strange" redirect is the result of mod_dir's DirectorySlash On directive, which is On by default. This can be quickly resolved by including DirectorySlash Off at the top of your .htaccess file (or making the trailing slash mandatory - see above and below).
The DirectorySlash On directive instructs Apache to automatically append a slash to URLs that end in a file system directory. In this sense it is "fixing" the URL. mod_dir achieves this with a 301 external redirect.
So, what is actually happening in the above, when DirectorySlash is enabled, is:
Initial request:
/projects (no trailing slash)
Internal rewrite in .htaccess:
/index.php?dir=projects/ (note that the request URL is still /projects)
mod_dir now kicks in and "fixes" the initial request (/projects --> /projects/) by appending a slash to the end of the URL-path. However, the query string from the rewritten URL (above) is passed through:
/projects/?dir=projects/ (this is a 301 external redirect, ie. a new request!)
Internal rewrite in .htaccess (again - new request):
/index.php?dir=projects/&dir=projects/ (note that the request is still /projects/?dir=projects/)
The doubling of the dir=projects/ query param is a result of the QSA flag on the RewriteRule (which I assume is required for other requests?). Your PHP script simply sees a single dir GET param (the later overwrites the former), unless you included dir[]=$1/ in your RewriteRule and you will end up with a 2-element array!
Your RewriteRule pattern also looks unnecessarily complex. You could simply make the trailing slash optional. ie:
RewriteRule ^(.+?)/?$ index.php?dir=$1/ [L,QSA]
Alternatively, having said all the above, you should probably leave DirectorySlash On (default) and simply make the trailing slash mandatory! For example:
RewriteRule ^(.+)/$ index.php?dir=$1/ [L,QSA]
mod_dir will now kick in before your internal rewrite (since it won't match without a trailing slash). This is also better for canonicalising your URLs and there are also potential security risks with turning it off.
Reference:
http://httpd.apache.org/docs/2.2/mod/mod_dir.html#directoryslash

Trying to stop urls such as mydomain.com/index.php/garbage-after-slash

I know very little about .htaccess files and mod-rewrite rules. Looking at my statcounter information today, I noticed that a visitor to my site entered a url as follows:
http://mywebsite.com/index.php/contact-us
Since there is no such folder or file on the website and no broken links on the site, I'm assuming this was a penetration attempt. What was displayed to the visitor was the output of the index.php file, but without benefit of the associated CSS layout.
I need to create a rewrite rule that will either remove the information after index.php (or any .php file), or perhaps more appropriately, insert a question mark (after the .php filename), so that any following garbage will be treated like a parameter (and will be gracefully ignored if no parameters are required).
Thank you for any assistance.
If you're only expecting real directories and real files that do exist, then you can add this to an .htaccess file. What it does is it takes a non-existent file or directory request and gives the user the index.php page with the original request as a query string. [QSA] appends any existing query string.
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) index.php?$1 [PT,QSA]
I found a solution, using information provided by AbsoluteZero as well as other threads that popped up on the right side of the screen as the solution came closer.
Here's the code that worked for me...
Options -Multiviews -Indexes +FollowSymLinks
RewriteEngine On
RewriteBase /
DirectorySlash Off
# remove trailing slash
RewriteRule ^(.*)\/(\?.*)?$ $1$2 [R=301,L]
# translate PATH_INFO information into a parameter
RewriteRule ^(.*)\.php(\/)(.*) $1.php?$3 [R=301,L]
# rewrite /dir/file?query to /dir/file.php?query
RewriteRule ^([\w\/-]+)(\?.*)?$ $1.php$2 [L,T=application/x-httpd-php]
I got the removal of trailing slash from another post on StackOverflow. However, even after removing the trailing slash, the rewrite rule did not account for someone appending what looks to be valid information after the .php file
(For example: mysite.com/index.php/somethingelse)
My goal was to either remove the "/somethingelse", or render it harmless. The PATH_INFO rule locates a "/" after the .php file, and turns everything else from that point forward into a query string (which will usually be ignored by the PHP file).

.htaccess falling over on trailing slash

TL;DR
How can I get .htaccess to rewrite http://domain.com/images to http://domain.com/images/ (i.e. add a trailing slash to URLs without one)? The URLs can be deeper than one level, for example http://domain.com/images/page/1.
More info
Say I have a URL like this:
http://jamwaffles2/wallpapers
This will redirect to this in the URL bar, with the rewrite rule working fine:
http://jamwaffles2/wallpapers/?page=wallpapers
However
http://jamwaffles2/wallpapers/ (note trailing slash)
Rewrites fine to
http://jamwaffles2/index.php?page=wallpapers (not visible to user)
With a nice http://jamwaffles2/wallpapers/ in the address bar.
The issue here is that when a trailing slash isn't given to the URL, the URL in the address bar changes to a not-so-pretty one. Can someone offer a solution to this?
Here's my .htaccess:
# turn rewriting on
RewriteEngine on
RedirectMatch permanent ^/$ http://jamwaffles2/home
Options +FollowSymLinks
RewriteRule ^([^/\.]+)/?$ /index.php?page=$1 [L,NC,QSA]
RewriteRule ^([^/\.]+)/([^/\.]+)/?$ /index.php?page=$1&var1=$2 [L,NC,QSA]
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ /index.php?page=$1&var1=$2&var2=$3 [L,NC,QSA]
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ /index.php?page=$1&var1=$2&var2=$3&var3=$4 [L,NC,QSA]
As a side note, there are more levels to the URL; the .htaccess should make that apparent (e.g. http://jamwaffles2/home/page/2).
EDIT
Curiously, this only happens on /wallpapers. If I type in http://jamwaffles2/home it works as expected, but won't work with http://jamwaffles2/wallpapers.
1) Try this directive: Options +FollowSymLinks -MultiViews -- depending on Apache config it can be the deal breaker.
2) Use this code (one of possible variants) to add trailing slash for NON-EXISTING resources ONLY:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*[^/])$ /$1/ [R=301,L]
It will redirect (301 Permanent Redirect) so URL will change in browser (e.g. example.com/hello => example.com/hello/).
If it still does not work (for whatever the reason may be) -- if you can edit Apache config files -- please enable rewrite debugging (RewriteLogLevel 9) and check the rewrite log to see why some URL failing correct rewrite. Every server can be configured differently, so the same rule may work a bit differently in your case.

Resources