Custom URL in apache using get parameters - php

I'm trying to figure out how to rewrite urls using apache webserver and php.
The url below is the real nonrewritten url:
http://localhost:1337/rewritetest/index.php?id=12
And I want to reach it by
http://localhost:1337/rewritetest/index/12
My indexfile looks like this:
<?php
echo $_GET['id'];
?>
Is this possible? The "new" url doesn't include any parameter names so I guess I have to use an order of parameters instead but I dont know how to reach them in that case.
Below is as far I've come with my rewrite:
RewriteCond %{QUERY_STRING} id=([-a-zA-Z0-9_+]+)
RewriteRule ^/?index.php$ %1? [R=301,L]
RewriteRule ^/?([-a-zA-Z0-9_+]+)$ index.php?id=$1 [L]
Anyone have an idea of what I'm doing wrong?

it's located in the same folder as index.php
So, given the .htaccess file is located at /rewritetest/.htaccess (as opposed to the document root ie. /.htaccess) then...
RewriteRule ^/?([-a-zA-Z0-9_+]+)$ index.php?id=$1 [L]
If you request a URL of the form /rewritetest/index/12 then the above RewriteRule pattern won't actually match anything. It tries to match "index/12", but your pattern does not contain a slash so will fail. (Is the + inside the character class intentional?)
Try something like the following instead:
RewriteRule ^(index)/(\d+)$ $1.php?id=$2 [L]
This obviously specifically matches "index" in the URL. If you are always rewriting to index.php then you don't really need "index" in the URL - unless this means something different? This also assumes that the valuue of the id parameter consists only of digits.
To rewrite the more general .../<controller>/26 to .../<controller>.php?id=26 (as mentioned comments) then try something like:
RewriteRule ^([\w-]+)/(\d+)$ $1.php?id=$2 [L]
In per-directory .htaccess files the slash prefix is omitted on the URL-path that is matched by the RewriteRule pattern, so /? is not required. The above pattern also matches something for for the id, not anything. So, /index/ would not match.
If this is a new site then the "redirect" (from /index.php?id=12 back to /index/12) is not necessarily required. That's only really required if you are changing the URL structure on an existing site where old URLs already have inbound links and are indexed by search engines. In which case you could do something like the following before the internal rewrite:
RewriteBase /rewritetest/
RewriteRule %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^id=(\d+)
RewriteRule ^(index)\.php$ $1/%1 [R,L]
Or, for a more generic .../<controller>/26 to .../<controller>.php?id=26 (as above) then change the RewriteRule to:
RewriteRule ^([\w-]+)\.php$ $1/%1 [R,L]
The additional check against the REDIRECT_STATUS environment variable is to prevent a rewrite loop after having rewritten the URL to /index.php?id=12 earlier.

Related

Rewrite subdomain and URL-path to URL parameters but allow access to files

I'm struggling with my .htaccess file and setting it up the way I want it. The main function is a website that gets the language from the subdomain and the current page from the subfolders.
Requirements
I have three requirements that I need my .htaccess file to do;
Wildcard subdomain redirected to lang variable
Subfolder(s) redirected to page variable
Local files respected (this is where I'm stuck)
(Bonus) Split up the page variable into segments for each slash; page, sub1, sub2, etc
Examples
en.example.com/hello -> /index.php?lang=en&page=hello
es.example.com/hola -> /index.php?lang=es&page=hola
(Bonus) en.example.com/hello/there/sir -> index.php?lang=en&page=hello&sub1=there&sub2=sir
My current .htaccess
This is my current setup which actually kinda works, if I don't need any local files (lol). This means local images aren't found when my .htaccess below is active. I tried adding RewriteCond %{REQUEST_FILENAME} !-f to respect local files but that breaks the whole file it seems - and I don't know why.
RewriteCond %{REQUEST_URI} ^/$
RewriteCond %{HTTP_HOST} ((?!www).+)\.example\.com [NC]
RewriteRule ^$ /index.php?lang=%1 [L]
RewriteCond %{HTTP_HOST} ((?!www).+)\.example\.com [NC]
RewriteRule ^(.+)$ /index.php?lang=%1&page=$1 [L]
RewriteRule ^index\.php$ - [L]
RewriteRule ^(.*)$ /index.php?page=$1 [L,QSA]
If your URLs don't contain dots then exclude dots from your regex - this naturally excludes real files (that contain a dot before the file extension). This avoids the need for a filesystem check.
Your script should handle /index.php?lang=%1 and /index.php?lang=%1&page= exactly the same, so the first rule is superfluous.
RewriteRule ^index\.php$ - [L]
This rule should be first, not embedded in the middle.
Try the following instead:
RewriteRule ^index\.php$ - [L]
RewriteCond %{HTTP_HOST} ^((?!www).+)\.example\.com [NC]
RewriteRule ^([^.]*)$ /index.php?lang=%1&page=$1 [QSA,L]
RewriteRule ^([^.]*)$ /index.php?page=$1 [QSA,L]
Your last rule that rewrites everything else to index.php, less the lang URL param is questionable. Why not just include this in the preceding rule and validate the language in your script? Which you need to do anyway.
Assuming there is always a subdomain, then your rules could then be reduced to:
RewriteRule ^index\.php$ - [L]
RewriteCond %{HTTP_HOST} ^(.+)\.example\.com [NC]
RewriteRule ^([^.]*)$ /index.php?lang=%1&page=$1 [QSA,L]
Requests for the www language are then validated by your script and defaulted accordingly, as if the lang param was not passed at all (which you need to be doing anyway).
If your subdomain is entirely optional and you are accessing the domain apex then make it optional (with a non-capturing group) in the regex:
RewriteCond %{HTTP_HOST} ^(?:(.+)\.)?example\.com [NC]
:
The lang param would then be empty if the domain apex was requested.
(Bonus) en.domain.com/hello/there/sir -> index.php?lang=en&page=hello&sub1=there&sub2=sir
It would be preferable (more efficient, flexible, etc) to do this in your PHP script, not .htaccess.
But in .htaccess you could do something like this (instead of the existing rule):
:
RewriteRule ^([^/.]*)(?:/([^/.]+))?(?:/([^/.]+))?(?:/([^/.]+))?(?:/([^/.]+))?$ /index.php?lang=%1&page=$1&sub1=$2&sub2=$3&sub3=$4&sub4=$5 [QSA,L]
The URL params are empty when that path segment is not present.
It is assumed the URL-path does not end in a slash (the above will not match if it does, so a 404 will result). If a trailing slash needs to be permitted then this should be implemented as a canonical redirect to remove the trailing slash. Or reverse the logic to enforce a trailing slash.
This particular example allows up to 4 additional "sub" path segments, eg. hello/1/2/3/4. You can extend this method to allow up to 8 (since there is a limit of 9 backreferences in the Apache syntax) if required. Any more and you will need to use PHP. (You could potentially handle more using .htaccess, but it will get very messy as you will need to employ additional conditions to capture subsequent path segments.)
I tried adding RewriteCond %{REQUEST_FILENAME} !-f to respect local files but that breaks the whole file it seems
That should also be sufficient (if dots are permitted in your URLs). But I wonder where you were putting it? It should not "break" anything - it simply prevents the rule from being processed if the request does map to a file - the rule is "ignored".
This is of course assuming you are correctly linking to your resources/static assets using root-relative (starting with a slash) or absolute (starting with scheme + hostname) URLs. If you are using relative URLs then they will probably result in 404s. If this is the case then see my answer to the following question on the Webmasters stack:
https://webmasters.stackexchange.com/questions/86450/htaccess-rewrite-url-leads-to-missing-css

Set .htaccess rules to specify different folders

I want to set a rule in .htaccess if I enter in the url www.mydomain.com/compare.php set 'public_html' as root otherwise anything come in the url set root as 'public' folder.
RewriteRule ^(?!compare-source.php).*)$ public/$1 [L]
I want to achieve following result.
if url is www.mydomain.com/compare.php hit following file.
public_html/compare.php
if urls are www.mydomain.com/ OR www.mydomain.com/home etc hit following file.
public_html/public/index.php
I am weak in regex and in these apache rules always :-( can someone give me the solution with good description?
Your answers are welcome, please can you describe how this crazy things work in detail. Thanks.
Try:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/public
RewriteRule ^((?!compare\.php).*)$ /public/$1 [L]
The RewriteEngine directive enables or disables the runtime rewriting engine.
The RewriteCond directive defines a rule condition. The following Rule is only used if this condition is met; In our case, if REQUEST_URI (the path component of the requested URL) does not (because of !) begin (because of ^) with /public. We need this condition because we don't want to rewrite already rewritten URL - that would cause loop and Internal error 500.
Finally, the RewriteRule will match regex Pattern (^((?!compare\.php).*)$) against part of the URL after the hostname and port, and without the leading slash. If the pattern is matched, the Substitution (public/$1) will replace the original URL-path.
In plain language, if URL path does not begin with compare.php (because of ?!), pick everything (.*) between beginning (^) and end ($) and place it in variable $1. Then replace the original URL path with /public/$1.
#Anubhava's answer is also correct, he just placed both conditions in RewriteRule, and also it could be written even more readable as:
RewriteCond %{REQUEST_URI} !^/public
RewriteCond %{REQUEST_URI} !^/compare\.php
RewriteRule ^(.*)$ /public/$1 [L]
You can use this .htaccess in site root:
RewriteEngine On
# route /home/ or /home to /
RewriteRule ^home/?$ / [L,NC]
# if not compare-source.php or public/* then route to /public/*
RewriteRule ^(?!public/|compare-source\.php$).*)$ public/$1 [L,NC]

Rewrite automatically removes backslash if there's more than one?

I have a very simple url rewriting rules:
RewriteEngine on
RewriteCond %{HTTP_HOST} !script.php
RewriteRule ^test/(.*) script.php?q=$1
The idea is to have this kind of urls: http://mywebsite.com/test/http://example.com
and then send http://example.com to the script.php as a query parameter. The problem is that I'm receiving http:/example.com instead of http://example.com. Also, http:////example.com would be sent as http:/example.com. What causes this behavior ?
Apache mod_rewrite engine converts multiple ///... into single / for pattern matching in RewriteRule directive. However if you match it using RewriteCond then you can match multiple /s.
You can use rule like this:
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^/+test/+(https?://.+)$ [NC]
RewriteRule ^ script.php?q=%1 [L,QSA]
The browser causes this behaviour. It contracts a sequence of / into 1 /, because it is still essentially a path. ///// does not change the directory we are in, so we could as well use /.
You have two options:
Change your links to use a query string instead. If you rewrite test/?q=something to script.php?q=something everything works as expected. You would do the following:
RewriteRule ^test/?$ script.php [L]
Since you don't alter the query string, the original query string is automatically copied to the new query string.
Don't make an assumption on how many slashes you will encounter. The url might not look correctly in the url bar of the browser, but if it is just a redirect, it will only be visible for a very short period of time.
RewriteRule ^test/(http|https):/+(.*)$ script.php?q=$1://$2

Apache mod_rewrite module issue

I have the following code in my .htaccess.
RewriteEngine On
RewriteRule ^/(\w+)/?$ /?user=$1
I'm trying to rewrite
http://domain.com/?user=username into http://domain.com/username. Unfortunately this code doesn't rewrite anything. Please help
Note:
I checked phpinfo() and mod_rewrite is loaded.
Update
I need to get username from url like http://facebook.com/username. But this code rewrites every folder in root folder, so my /css folder become http://domain.com/css/?u=common. How to allow this code works only for http://domain.com/index.php
The mistake you are doing is the use of / in the beginning of the line ^/(\w+)/?$
rewrite rules strips off the / from the beginning of the pattern to be matched in .htaccess and directory context.
Try doing this:
RewriteEngine On
RewriteRule ^(\w+)/?$ /?user=$1
From RewriteRule Directive docs :
What is matched?
In VirtualHost context, The Pattern will initially be matched against the part of the URL after the hostname and port, and before the query string (e.g. "/app1/index.html").
In Directory and htaccess context, the Pattern will initially be matched against the filesystem path, after removing the prefix that lead the server to the current RewriteRule (e.g. "app1/index.html" or "index.html" depending on where the directives are defined).
If you wish to match against the hostname, port, or query string, use a RewriteCond with the %{HTTP_HOST}, %{SERVER_PORT}, or %{QUERY_STRING} variables respectively.
Edit: Answer updated as per OP's request:
Add this :
RewriteEngine On
#do nothig if URL is trying to access the folder CSS.
RewriteRule *css/* - [L]
#checks where the URL is a valid file/folder.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(\w+)/?$ /?user=$1
I think that you are doing it the right way round, but explained it the wrong way round!
Is the problem that you don't need the initial / as the URL passed to test doesn't include it!?
I suspect it should be RewriteRule ^(\w+)/?$ /?u=$1
Also, be careful you don't end up with a loop!

mod rewrite problem?

I'm trying to rewrite this url:
http://www.example.com/user.php?user=username
into
http://example.com/username
I'm using this code in my .htaccess:
RewriteEngine On
RewriteRule ^([^/]*)$ /user.php?user=$1 [L]
but its giving me an internal error. Is there something wrong?
To match the query string part of a URL, you have to use RewriteCond, like this:
RewriteEngine On
RewriteCond %{QUERY_STRING} user=(.*)
RewriteRule ^user.php$ %1 [L]
So the RewriteCond rule matches the username in ?user=name and then the %1 uses that value in the resulting rewrite on the last line of my example.
On the slash issue, URLs like /name get automatically redirected to URLs like /name/ if the web server finds that /name is a directory. So if your intention is to map user.php/user=name to something like /name/index.html, you will cause that slash to get inserted. But if your intention is to map it to a file (or CGI script) at /name then it will work as expected.

Categories