Using rewrite mod in .htaccess - php

I'd like to write a rewrite rule to do the following: if any of the following files exist on the server as a static html file in a specific directory then I'd like .htaccess to serve that static file. Otherwise, I'd like the id (first number after the .com/ and before the first hyphen) to be passed as a query parameter to www.gallery.com/index.php
Redirect should occur for the following URLs if it doesn't exist as a static HTML page.
www.gallery.com/2-swimming.html
www.gallery.com/2
gallery.com/2
Below is my entire .htaccess file
<Files .htaccess>
order allow,deny
deny from all
</Files>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENANE} !-f
RewriteCond %{REQUEST_FILENANE} !-d
RewriteRule ^([0-9]+)-(.*)\.html$ /index.php?id=$1 [L]
</IfModule>
Is there anything wrong with my rewrite condition.(I'm having a hard time). Also what is an efficient way to grab the id incase of a redirect.

Why not just always pass everything to index.php?
PHP is a lot better at manipulating strings than .htaccess
The -f line already takes care of existing static files. (And -d for directories)
RewriteRule ^(.*)$ /index.php?id=$1 [L,QSA]
This rule matches and captures everything.

Related

How to get .htaccess to return 404 instead of 500 for missing files

Background
I am building a PHP application that uses MVC principles. I am not every experienced in writing .htaccess files, but I have a solution that works very well for my application. All requests are routed to a public/ directory for security and only absolute resources in this directory can be accessed, for example: http://localhost/public/css/main.css.
My issue is properly handling resource files (js, css, or images) that do not actually exist.
Issue & Question
My current .htaccess rules are causing an infinite loop and returning an http 500 status when a resource file (js, css, or images) does not exists.
I know I need to catch this file does not exists problem and return an http 404 status instead, but how do I do that without skipping the last 4 lines of my rules? I'm referring to the last 3 rewrite conditions and 1 rewrite rule. These are crucial to my app.
htaccess code
# Disable index view.
Options -Indexes
# Hide sensitive files.
<Files ~ "\.(env|json|config|md|gitignore|gitattributes|lock|yml|xml)$">
Order allow,deny
Deny from all
</Files>
# Redirect all requests to the public directory.
<IfModule mod_rewrite.c>
RewriteEngine On
# Rewrite base if site is not at the servers root.
RewriteBase /sub/directory/
# If nothing or an index page is requested go to `public/index.php` and stop processing.
RewriteRule ^(\/||.*index\..*)$ public/index.php [L,QSA]
# If the URL already contains the `public/` directory go to URL and stop processing.
RewriteRule ^(.*public\/.*)$ $1 [L,QSA]
# Does it appear that this URL is for a resource (JS, CSS, etc.)?
# ▼ ▼ ▼ ISSUE STARTS HERE ▼ ▼ ▼
RewriteCond %{REQUEST_URI} ^([\w\d\s\-\_\/\%]*)\.(?!php|phtml|htm|html).*$
# Yes, prefix with `public/` and go to the modified URL.
RewriteRule ^(.*)$ public/$1 [L,QSA]
# Rewrite all remaining URLs to our apps MVC structure.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^([\w\d\s\-\_\/\%]*)$ public/index.php?p=$1 [L,QSA]
</IfModule>
Wrong approach in your question. You assume that you have to "catch" the file not found situation and return a 404. But that is not the case. Actually that is the default behavior of the apache http server so you'd have to do nothing for that. Your issue is that you implemented a rewriting loop for those requests, that is what you need to break. Then a 404 will get returned as a standard response.
The actual issue is a classic one: your last rewrite rule rewrites to public/index.php. That target however is matched again by the pattern of the same rewrite rule. So your next rewriting target looks like public/index.php?p=public/index.php&p=some-page. Each time the rule rewrites the rewriting restarts, since the target has been altered. That behavior is documented.
You have two options:
Either you need to use the END flag instead of the L flag in that rule (to finally terminate the rewriting process for that request:
RewriteRule ^([\w\d\s\-\_\/\%]*)$ public/index.php?p=$1 [END,QSA]
Or you need to add an additional condition to break that loop:
RewriteCond %{REQUEST_URI} !public/index\.php$
I would definitely recomment the first approach, assuming that there are no further rewriting steps interfering here.
arkascha's answer helped point me in the right direction; using END instead of L in the correct locations. For those looking to create a similar security focused MVC structure my working .htacces file is below. It provides the following benefits:
Directory listing is disabled and common sensitive files are forbidden from direct viewing. [A]
Any attempt to view an index page is redirected to the public directory. [B]
Rewrite loops to the public directory are caught and prevented from occurring. [C]
Direct access to resource files (JS, CSS, etc.) are allowed in the public directory. [D]
Missing resource files properly return a 404 instead of a 500. Thanks again to #arkascha for pointing out the difference between L and END in htacces files. [E]
# Disable index view. [A]
Options -Indexes
# Hide sensitive files. [A]
<Files ~ "\.(env|json|config|md|gitignore|gitattributes|lock|yml|xml)$">
Order allow,deny
Deny from all
</Files>
# Redirect all requests to the public directory.
<IfModule mod_rewrite.c>
RewriteEngine On
# Rewrite base if site is not at the servers root.
RewriteBase /sub/directory/
# If nothing or an index page is requested go to `public/index.php` and stop processing. [B]
RewriteRule ^(\/||.*index\..*)$ public/index.php [END,QSA]
# If the URL already contains the `public/` directory go to URL and stop processing. [C][E]
RewriteRule ^(.*public\/.*)$ $1 [END,QSA]
# Does it appear that this URL is for a resource (JS, CSS, etc.)?
RewriteCond %{REQUEST_URI} ^([\w\d\s\-\_\/\%]*)\.(?!php|phtml|htm|html).*$
# Yes, prefix with `public/` and go to the modified URL. [D]
RewriteRule ^(.*)$ public/$1 [L,QSA]
# Rewrite all remaining URLs to our apps MVC structure. [E]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^([\w\d\s\-\_\/\%]*)$ public/index.php?p=$1 [END,QSA]
</IfModule>

LimeSurvey: Skip "The following surveys are available" Page?

I have recently installed the latest LimeSurvey, and while it has many features I like, I only really intend on having one survey available at a survey.example.com subdomain.
I want to skip the page that states "The following surveys are available:" which is the "survey.example.com/index.php". and go straight to the survey, which is "survey.example.com/index.php/311746?lang=en"
I tried setting a DirectoryIndex in .htaccess but that didn't do anything
DirectoryIndex index.php/311746?lang=en
I've tried playing with mod_rewrite, but LimeSurvey itself already has conditions set up, so whatever I tend to do breaks theirs (likely since they're already rewriting index.php).
<IfModule mod_rewrite.c>
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
# otherwise forward it to index.php
RewriteRule . index.php
</IfModule>
I was going to try a redirect, but the index.php is more than just the index, so I don't want to change that too much.
I've tried searching around but I haven't had much luck.
Then why not just rewrite the main rule of the survey. Instead of rewriting it to the main index.php which you don't want to do, write it directly to the survey you want. You should be able to use this in your htaccess to go directly to the survey.
<IfModule mod_rewrite.c>
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to the survey
RewriteRule . index.php/311746?lang=en [R=301,L]
</IfModule>
Let me know if this solves your issue

PHP SEO friendly with clean URL

Currently I works on to transfer my site into SEO friendly URL (in localhost),
Here's the original URL with query string:
http://{ip}/sitename/item.php?category=44
I want convert to:
http://{ip}/sitename/item/category/44
.htaccess file (same directory with item.php):
DirectoryIndex index.php
Options -Indexes
<files page>
ForceType application/x-httpd-php
</files>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^.*$ item.php%{REQUEST_URI} [L]
RewriteRule ^/item/([0-9]+) /item.php?category=$1
</IfModule>
<Files *htaccess>
Deny from all
</Files>
in item.php, I use $_GET['category']
$category = preg_replace('/[^0-9]/', '', $mysqli->real_escape_string($_GET['category']));
$list = $listing->get_items($category, $mysqli);
if($list == 0){
echo '<p><h2>Page not found</h2></p>';
die();
}
Problem 1:
When I loaded http://{ip}/sitename/item/category/44, the page cannot get the variable passes to get_item(), the page is shown Page not found? 44 is suppose return a value.
Problem 2:
My page doesn't loaded referrer files, all *.css *.js etc are just ignore?
Try this in your .htaccess file:
Options +FollowSymLinks
RewriteEngine on
RewriteRule item/category/(.*) item.php?category=$1
PROBLEM 2 - The problem has arised as you have not mentioned the base path.
You need to assign base path to load css and other references.
Try using this in <head> tag <base href="http://www.MYSITE.com/" />
This will load your css/js.
Problem 1: No rewriting has happened here – (you only think it has, beause your script item.php got called anyway, because you had item in the URL and MultiViews has done its work) – paths the RewriteRules match on in .htaccess never start with a /, so remove it.
Problem 2: Has been discussed many times before (and should also be obvious to anyone who knows the basics of how completion of relative URLs works) – see f.e. .htaccess URL Rewrite Problem (Scripts don't load)
RewriteEngine On
RewriteBase /sitename
RewriteRule ^item/([0-9]+)/?$ item.php?category=$1 [L,NC]

Using index.php to manage all url paths in my site

I must be missing something obvious, since this seems like it shouldn't be a difficult problem. I want all url paths on my site (http://example.com, http://example.com/admin, http://example.com/happy/happy/joy/joy, etc.) to be handled through my index.php. I've seen this done before, but offhand I do not know how to do it.
Just create an .htaccess file in your root directory with this :
# Turn on URL rewriting
RewriteEngine On
# Base directory
RewriteBase /
# Protect hidden files from being viewed
<Files .*>
Order Deny,Allow
Deny From All
</Files>
# Allow any files or directories that exist to be displayed directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Rewrite all other URLs to index.php/URL
RewriteRule .* index.php/$0 [PT]
Apache flags explained :
[L] - Alias for "Last", instructs the server that the rewrite rules have come to an end and it's time to perform an internal redirection without changing the browser's uri.
[PT] - Alias for "Pass Through", allows a Mod_Rewrite manipulated uri to be passed to the next type of handler, accordingly to the php.ini module's order of inclusion. Usefull depending on the subject.

Create a Catch-All Handler in PHP?

I want to have a PHP file catch and manage what's going to happen when users visit:
http://profiles.mywebsite.com/sometext
sometext is varying.
E.g. It can be someuser it can be john, etc. then I want a PHP file to handle requests from that structure.
My main goal is to have that certain PHP file to redirect my site users to their corresponding profiles but their profiles are different from that URL structure. I'm aiming for giving my users a sort of easy-to-remember profile URLs.
Thanks to those who'd answer!
Either in Apache configuration files [VirtualHost or Directory directives], or in .htaccess file put following line:
Options -MultiViews
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L,NC,QSA]
</IfModule>
It will silently redirect all incoming requests that do not correspond to valid filename or directory (RewriteCond's in the code above make sure of that), to index.php file. Additionally, as you see, MultiViews option also needs to be disabled for redirection to work - it generally conflicts with these two RewriteCond's I put there.
Inside index.php you can access the REQUEST_URI data via $_SERVER['REQUEST_URI'] variable. You shouldn't pass any URIs via GET, as it may pollute your Query-String data in an undesired way, since [QSA] parameter in our RewriteRule is active.
You should use a rewrite rule..
In apache (.htaccess), something like this:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /index.php?url=$1 [QSA,L]
</IfModule>
Then in your index.php you can read $_GET['url'] in your php code.
You can use a .htaccess file (http://httpd.apache.org/docs/1.3/howto/htaccess.html) to rewrite your url to something like profiles.websites.com/index.php?page=sometext . Then you can do what you want with sometext in index.php.
An obvious way to do this would be via the 404 errorDocument - saves all that messing about with mod_rewrite.
If you have not heard about MVC, its time you hear it, start with CodeIgniter, its simplest and is quite fast, use default controller and you can have URLs like
domain.com/usernam/profiledomain.com/usernam/profile/editdomain.com/usernam/inboxdomain.com/usernam/inbox/read/messageid Or use .htaccess wisely

Categories