Url rewrite not working; 404 error and no url change - apache

I'm working on a website where I want the url
www.example.com/directory1/states/california.php
to point to
www.example.com/directory1/california
And a similar url change for the city pages as well:
www.example.com/directory1/cities/miami.php
should point to
www.example.com/directory1/miami
I'm using the following rules in my access file to change the url:
RewriteRule ^directory1/(alabama|alaska|arizona|arkansas|california|colorado|connecticut|delaware|florida|georgia|guam|hawaii|idaho|illinois|indiana|iowa|kansas|kentucky|louisiana|maine|maryland|massachusetts|michigan|minnesota|mississippi|missouri|montana|nationwide|nebraska|nevada|new_hampshire|new_jersey|new_mexico|new_york|north_carolina|north_dakota|ohio|oklahoma|oregon|pennsylvania|rhode_island|south_carolina|south_dakota|tennesee|texas|us_virgin_islands|utah|vermont|virginia|washington|west_virigina|wisconsin|wyoming)$ /directory1/states/$1.php [L]
RewriteRule ^directory1/(.*)$ /directory1/cities/$1.php [L]
However, nothing changes in the url bar and I always get a 404 not found. When I tested it with the htaccess checker, the output url is always correct. What is wrong with my rules? Is there a way to test how/if mod_rewrite is even functioning?

Some server configs require you to turn the rewrite engine on in your .htaccess file. Right at the top:
RewriteEngine on
I've also known some server configs where the file path starts/ends with a / (due to other rewrite rules already being run on the request) so perhaps allow for that to be there in the rules:
RewriteRule ^/?directory1/(alabama|alaska|arizona|arkansas|california|colorado|connecticut|delaware|florida|georgia|guam|hawaii|idaho|illinois|indiana|iowa|kansas|kentucky|louisiana|maine|maryland|massachusetts|michigan|minnesota|mississippi|missouri|montana|nationwide|nebraska|nevada|new_hampshire|new_jersey|new_mexico|new_york|north_carolina|north_dakota|ohio|oklahoma|oregon|pennsylvania|rhode_island|south_carolina|south_dakota|tennesee|texas|us_virgin_islands|utah|vermont|virginia|washington|west_virigina|wisconsin|wyoming)/?$ /directory1/states/$1.php [L]
RewriteRule ^/?directory1/(.*)/?$ /directory1/cities/$1.php [L]

Related

.htaccess rewrite returning Error 404

RewriteEngine on
RewriteCond %{QUERY_STRING} (^|&)public_url=([^&]+)($|&)
RewriteRule ^process\.php$ /api/%2/? [L,R=301]
Where domain.tld/app/process.php?public_url=abcd1234 is the actual location of the script.
But I am trying to get .htaccess to make the URL like this: domain.tld/app/api/acbd1234.
Essentially hides the process.php script and the get query ?public_url.
However the script above is returning error 404 not found.
I think this is what you are actually looking for:
RewriteEngine on
RewriteCond %{QUERY_STRING} (?:^|&)public_url=([^&]+)(?:$|&)
RewriteRule ^/?app/process\.php$ /app/api/%1 [R=301,QSD]
RewriteRule ^/?app/api/([^/]+)/?$ /app/process.php?public_url=$1 [END]
If you receive an internal server error (http status 500) for that then check your http servers error log file. Chances are that you operate a very old version of the apache http server, you may have to replace the [END] flag with the [L] flag which probably will work just fine in this scenario.
And a general hint: you should always prefer to place such rules inside the http servers (virtual) host configuration instead of using dynamic configuration files (.htaccess style files). Those files are notoriously error prone, hard to debug and they really slow down the server. They are only supported as a last option for situations where you do not have control over the host configuration (read: really cheap hosting service providers) or if you have an application that relies on writing its own rewrite rules (which is an obvious security nightmare).
UPDATE:
Based on your many questions in the comments below (we see again how important it is to be precise in the question itself ;-) ) I add this variant implementing a different handling of path components:
RewriteEngine on
RewriteCond %{QUERY_STRING} (?:^|&)public_url=([^&]+)(?:$|&)
RewriteRule ^/?app/process\.php$ /api/%1 [R=301,QSD]
RewriteRule ^/?api/([^/]+)/?$ /app/process.php?public_url=$1 [END]
I am trying to get .htaccess to make the URL like this: example.com/app/api/acbd1234.
You don't do this in .htaccess. You change the URL in your application and then rewrite the new URL to the actual/old URL. (You only need to redirect this, if the old URLs have been indexed by search engines - but you need to watch for redirect loops.)
So, change the URL in your application to /app/api/acbd1234 and then rewrite this in .htaccess (which I assume in in your /app subdirectory). For example:
RewriteEngine On
# Rewrite new URL back to old
RewriteRule ^api/([^/]+)$ process.php?public_url=$1 [L]
You included a trailing slash in your earlier directive, but you omitted this in your example URL, so I've omitted it here also.
If you then need to also redirect the old URL for the sake of SEO, then you can implement a redirect before the internal rewrite:
RewriteEngine On
# Redirect old URL to new (if request by search engines or external links)
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} (?:^|&)public_url=([^&]+)(?:$|&)
RewriteRule ^process\.php$ /app/api/%1? [R=302,L]
# Rewrite new URL back to old
RewriteRule ^api/([^/]+)$ process.php?public_url=$1 [L]
The check against REDIRECT_STATUS is to avoid a rewrite loop. ?: inside the parenthesised subpattern avoids the group being captured as a backreference.
Change the 302 (temporary) to 301 (permanent) only when you are sure it's working OK, to avoid erroneous redirects being cached by the browser.

Why is this RewriteRule altering QUERY_STRING, but leaving REQUEST_URI untouched?

I have a copy of Concrete5, a PHP-based CMS, running on example.com.
Concrete5 comes with the following basic instructions for pretty URLs (redirecting all URLs to a central index.php)
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !^/c5.7
RewriteRule ^.*$ c5.7/$0 [L] # Concrete5 is running in the c5.7/ subdirectory
</IfModule>
Pretty straightforward.
Now I have a certain set of URLs that take the form
/product/{productname}
that I need to forward to the Concrete5 (virtual) URL
/products/details?name={productname}
That URL is set up and works as expected when I enter it manually in the browser.
So I added a line to the htaccess file and it now looks like this:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# New rule for products
RewriteCond %{REQUEST_URI} ^/product/
RewriteRule ^product/(.+)$ /products/details?name=$1 [QSA]
RewriteCond %{REQUEST_URI} !^/c5.7
RewriteRule ^.*$ c5.7/$0 [L]
</IfModule>
I can confirm the RewriteRule gets triggered when I choose a random, external URL as the redirection target.
But whenever it is an internal redirect like above, what happens is, I get a 404 inside Concrete5. When I inspect what was passed to it, I see:
REQUEST_URI: /product/my-random-product
QUERY_STRING: name=my-random-product
So it appears that the rule is triggered and does some rewriting, but REQUEST_URI remains unchanged!
Why?
Is it because PHP 7.1 is running via CGI?
I have tried a zillion variations and all the flags in the book, with little success.
The REQUEST_URI in PHP is not the same as the REQUEST_URI within mod_rewrite, so you can't do it like this. In PHP it always contains the original URL. So you can't change it like this if your CMS is working off that.
You should set up your CMS to use the URLs you want, rather than trying to augment your CMS's URL rewriting like this.
If you inspect REDIRECT_URL in PHP you will see the last rewritten URI.
REQUEST_URI in PHP will always be the original request URI.
Because this is already explained by LSerni and SuperDuperApps, I won't elaborate.
Instead, I'm offering a quick solution: modify the REQUEST_URI and add a name parameter in PHP instead of in .htaccess.
Add the following code to the start of your Concrete5 index.php to make sure that REQUEST_URI is modified
before any Concrete5 code runs:
if(preg_match('-^/product/([^?]*)-',$_SERVER['REQUEST_URI'],$matches)){
$_SERVER['REQUEST_URI'] = '/products/details';
$_GET['name'] = $matches[1];
}
Your setup works on a PHP 7.1 machine (without Concrete5). It does call a script I just put in, which is in /c5.7/products/details. So the Apache part is working.
Inside the script, I see that REQUEST_URI is the old value prior to the rewrite.
So its value is normal and it not being rewritten is a red herring - it isn't supposed to be rewritten. The 404 error must be due to something else.
Your Concrete5 routing should support the real URL, not just the virtual one, because C5's routing relies itself on REQUEST_URI. If this is so, you need to create a route for your short URLs
Route::register('/product/{productname}' ...)
and an appropriate controller to get the parameters and invoke the "old" controller.
One possibility using .htaccess could be this, but I'm not too sure it will work since REQUEST_URI is still left unchanged:
# New rule for products
RewriteCond %{REQUEST_URI} ^/product/
RewriteRule ^product/(.+)$ c5.7/products/details?name=$1 [L,QSA]
Otherwise you need to do an external redirect, which will disclose the URL in the browser:
RewriteRule product/(.*)$ http://.../products/details?name=$1 [QSA]
See also this other question.

htaccess mod_rewrite giving me 404

SO I have this in my .htaccess file
RewriteEngine On
RewriteBase /core/
RewriteCond %{QUERY_STRING} ^page=([0-9]+)$
RewriteRule ^page page/%1/? [L]
my url is
http://localhost/core/page.php?page=8
with the rules applied I'm getting..
Not Found
The requested URL /core/page/8/ was not found on this server.
This is running on wampserver 2.2
the file structure looks like
c:/wamp/www/core
the .htaccess is inside the /core/ directory.
What is it that I'm missing.. i've checked my apache.conf file and it looks fine.
I think you got it the wrong way around. When logically thinking of rewriting you don't rewrite original URL to new URL (for example page.php?page=8 to page/8/) you actually rewrite page/8/ to page.php?page=8. You tell the server how it should interpret the unfamiliar URL.
So if I understood correctly what you want to achieve is:
User visits localhost/core/page/8/
User is served (under the hood) localhost/core/page.php?page=8
I believe the following RewriteRule will do the trick (The query string condition is not necessary):
RewriteRule ^page/(\d+)/$ page.php?page=$1 [L]

How to prevent mod_rewrite from rewriting URLs more than once?

I want to use mod_rewrite to rewrite a few human-friendly URLs to arbitrary files in a folder called php (which is inside the web root, since mod_rewrite apparently won't let you rewrite to files outside the web root).
/ --> /php/home.php
/about --> /php/about_page.php
/contact --> /php/contact.php
Here are my rewrite rules:
Options +FollowSymlinks
RewriteEngine On
RewriteRule ^$ php/home.php [L]
RewriteRule ^about$ php/about_page.php [L]
RewriteRule ^contact$ php/contact.php [L]
However, I also want to prevent users from accessing files in this php directory directly. If a user enters any URL beginning with /php, I want them to get a 404 page.
I tried adding this extra rule at the end:
RewriteRule ^php php/404.php [L]
...(where 404.php is a file that outputs 404 headers and a "Not found" message.)
But when I access / or /about or /contact, I always get redirected to the 404. It seems the final RewriteRule is applied even to the internally rewritten URLs (as they now all start with /php).
I thought the [L] flag (on the first three RewriteRules) was supposed to prevent further rules from being applied? Am I doing something wrong? (Or is there a smarter way to do what I'm trying to do?)
[L] flag should be used only in the last rule,
L - Last Rule - Stops the rewriting process here and don’t apply any more rewriting rules & because of that you are facing issues.
I had similar problem. I have a content management system written in PHP and based on Model-View-Control paradigm. The most base part is the mod_rewrite. I've successfully prevent access to PHP files globally. The trick has name THE_REQUEST.
What's the problem?
Rewriting modul rewrites the URI. If the URI matches a rule, it is rewritten and other rules are applied on the new, rewritted URI. But! If the matched rule ends with [L], the engine doesn't terminate in fact, but starts again. Then the new URI doesn't more match the rule ending with [L], continues and matches the last one. Result? The programmer stars saying bad words at the unexpected 404 error page. However computer does, what you say and doesn't do, what you want. I had this in my .htaccess file:
RewriteEngine On
RewriteBase /
RewriteRule ^plugins/.* pluginLoader.php [L]
RewriteCond %{REQUEST_URI} \.php$
RewriteRule .* index.php [L]
That's wrong. Even the URIs beginning with plugins/ are rewritten to index.php.
Solution
You need to apply the rule if and only if the original - not rewritten - URI matches the rule. Regrettably the mod_rewrite does not provide any variable containing the original URI, but it provides some THE_REQUEST variable, which contains the first line of HTTP request header. This variable is invariant. It doesn't change while rewrite engine is working.
...
RewriteCond %{THE_REQUEST} \s.*\.php\s
RewriteRule \.php$ index.php [L]
The regular expression is different. It is not applied on the URI only, but on entire first line of the header, that means on something like GET /script.php HTTP/1.1. But the critical rule is this time applied only if the user is explicitly requesting some PHP-script directly. The rewritten URI is not used.

Redirecting URLs (with specific GET parameters)

I have this old survey link that is has been superseded by another link, so basically I want anyone trying to access the URL:
http://mywebsite.com/survey/view_survey.php?surveyID=1
To be redirected to:
http://mywebsite.com/survey/view_survey.php?surveyID=2
Can I do this in the Apache configuration or htaccess file?
I tried the following rule in the Redirect section of my httpd.conf file:
Redirect 301 /survey/view_survey.php?surveyID=1 http://mywebsite.com/survey/view_survey.php?surveyID=2
But it doesn't work. I am suspecting that the GET parameters are not used when processing the rule.
Is my only option to hack my code to redirect on a specific surveyID?
Following the suggestion of using the Rewrite rules, I tried the following in my .htaccess file:
RewriteRule ^survey/view_survey\.php\?surveyID=1525$ /survey/view_survey.php?sur
veyID=1607
But that doesn't work. I do have the rewrite engine up and running, because I have another rewrite rule currently running.
Try this in a .htaccess file:
RewriteEngine on
RewriteCond %{QUERY_STRING} (^|.*&)surveyID=1525(&.*|$)
RewriteRule ^survey/view_survey\.php$ /survey/view_survey.php?%1surveyID=1607%2 [L,R=301]
RewriteEngine On
RewriteCond %{QUERY_STRING} ^surveyID=1525$
RewriteRule ^/survey/view_survey\.php /survey/view_survey.php?surveyID=1607 [R=301]
Check out the QSA portion of the mod_rewrite.
It does GET string manipulation.
There might be a possible duplicate of this question and it is solved if this solution doesnt work for you:
Apache Redirect 301 fails when using GET parameters, such as ?blah=