11

How does URL rewriting affect the $_GET parameter for PHP?

Say I have a URL like http://example.com/index.php?p=contact and I use $_GET['p'] to tell index.php to serve the contact page. If I use a rewrite rule that converts the URL to http://example.com/contact, will $_GET['p'] still work as expected?

If it does, could you elaborate on why it works? If not, what strategies could be used to solve the problem so that the page will work both with and without the rewrite?

Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
VirtuosiMedia
  • 52,016
  • 21
  • 93
  • 140
  • Now seriously, am I the only one noticing here that he is trying to convert http://example.com/index.php?p=contact to http://example.com/contact, and not http://example.com/contact to http://example.com/index.php?p=contact like everyone here is doing?? – Filip Dupanović Feb 02 '09 at 23:43
  • I misunderstood how it worked, but the answers here helped correct my understanding. – VirtuosiMedia Feb 03 '09 at 00:52

7 Answers7

35

I'd amend Grant's answer to "Yes, that will work mostly as expected."

Specifically, mod_rewrite's behavior with regards to existing query strings can be surprising. As an example, let's take the following rule, which converts the URL you supplied:

RewriteRule /contact /index.php?p=contact

This will correctly rewrite /contact to /index.php?p=contact and the page name will be accessible through $_GET['p']. However, if you use this technique with a script that uses parameters other than the page name, it gets slightly trickier. This rule also translates /contact?person=Joe to /index.php?p=contact. The person=Joe parameter disappears entirely! There are two ways to deal with this.

The simplest way is to use the [QSA] ("query string append") flag on your rule, which will place the original query string after the parameters supplied in the rule, translating /contact?person=Joe to /index.php?p=contact&person=Joe:

RewriteRule /contact /index.php?p=contact [QSA]

However, this makes it possible for your p= parameter to get overwritten. Visiting /contact?p=about will get rewritten to /index.php?p=contact&p=about, so $_GET['p'] will return "about" in your script, not "contact". To resolve this, use the QUERY_STRING variable instead:

RewriteRule /contact /index.php?%{QUERY_STRING}&p=contact

This guarantees that $_GET['p'] will always return "contact" when using this rule, regardless of whether your visitors are messing with your URLs. :-)

Stuart M
  • 11,458
  • 6
  • 45
  • 59
Ben Blank
  • 54,908
  • 28
  • 127
  • 156
  • 1
    Well, this very same thing caught me off-guard over the weekend, so I thought I'd share. :-) – Ben Blank Feb 03 '09 at 01:00
  • 4
    I'd take the word "contact" out of the query string altogether: `RewriteRule ^(.+)$ index.php/$1` means that "contact" is accessible in `$_SERVER['PATH_INFO']` instead of in `$_GET['q']`. Other `$_GET` parameters are unaffected. – TRiG Jul 29 '10 at 18:34
4

Yes, that will work as expected.

Grant
  • 11,799
  • 13
  • 42
  • 47
  • So does the rewrite merely overwrite the URL to the location bar in the browser but still serve the unwritten URL to the script? – VirtuosiMedia Feb 02 '09 at 23:03
  • You type www.example.com/page/8 and rewrite sees the page/8 and changes it to index.php?page=8 before your php script sees it. – Grant Feb 02 '09 at 23:07
  • rewrite can either change the URL internally without telling the user, or immediately redirect him/her to the new url, depending on the parameters you use. It can even proxy in a new page from another server if you tell it to. – Grant Feb 02 '09 at 23:08
  • It completely depends on what your RewriteRule is – Joe Phillips Feb 02 '09 at 23:14
1

When rewriting an URL this is done by mod_rewrite -- the page retrieved in the end is still the "old" one, i.e. index.php?p=contact. In other words, the browser retrieves /contact. mod_rewrite then rewrites it to index.php?p=contact. The script, due to this, doesn't know that any rewriting happened -- it still gets called its "usual" way. Therefore such a rewrite will work. You might want to think of it as a rewriting proxy that requests a different page than the one the originating browser requested.

bluebrother
  • 8,636
  • 1
  • 20
  • 21
1

When the client requests http://example.com/contact, the server uses the rewrite rule to serve them http://example.com/index.php?p=contact instead. The client will not be able to see the rewritten URL and might not even be able to tell that it was rewritten. Requesting either URL as the client would give you the exact same page.

Paige Ruten
  • 172,675
  • 36
  • 177
  • 197
0

You rewrite URL from /contact to /index.php?p=contact, so yes, it'll work as expected.

Emil
  • 7,220
  • 17
  • 76
  • 135
SilentGhost
  • 307,395
  • 66
  • 306
  • 293
0

Isn't it the case that modifying the headers after having rendered parts of the page can cause screw ups in php pages? How are you rewriting the URL? Maybe I misunderstand...

Brian Sweeney
  • 6,693
  • 14
  • 54
  • 69
  • i thought that with mod_rewrite or something similar – SilentGhost Feb 02 '09 at 23:06
  • The rewrite would be through an Apache rewrite rule. I don't know a whole lot about it, hence the question, but I don't think the headers are touched at all. – VirtuosiMedia Feb 02 '09 at 23:07
  • Would it be possible for you to use POST rather than GET and then create a subdir ROOT/contact in your webdir whose Apache INDEX points at the correct page? Sort of a weird solution but it may solve both stated problems... – Brian Sweeney Feb 02 '09 at 23:34
-2

In your case it wouldn't work. mod_rewrite, after it finds a match and rewrites http://example.com/index.php?p=contact to http://example.com/contact, does an internal redirect. Even after the redirect, the new, redirected URI, can still be matched against a condition and further redirected.

In any case, incoming URIs aren't kept in memory, so not even Apache can reconstruct what the original URI was. PHP, by the time it's executed, also doesn't know the original URI. Hence, you loose your $_GET vars, as variables sent via GET are contained in the URL, which was, by now, transformed, and PHP populates the associative $_GET array by parsing incoming requests.

Offering support for both would be painstaking. If you have http://domain.com/segment1/segment2/segment3 you have to associate the segments with something meaningful. You'd strip your domain and explode on '/', and in your case you could say that the first segment requests the page, and from http://example.com/contact/ you can extract page = 'contact'

Filip Dupanović
  • 32,650
  • 13
  • 84
  • 114
  • It depends on what the rewrite rule is. Depending on the rule it could either be susceptible to further rules, or stop right there. You can lose get vars, but that depends on the rewrite rules, which VirtuosiMedia hasn't started to construct yet, so it's a moot point anyway. – Grant Feb 02 '09 at 23:27
  • Well, in his case, going from http://example.com/index.php?p=contact to http://example.com/contact would strip you of any GET vars, so no $_GET['p'] wouldn't work. – Filip Dupanović Feb 02 '09 at 23:32
  • @kRON: it'll strip you of a script as well don't you think? – SilentGhost Feb 02 '09 at 23:34
  • If it's one depending on $_GET['p'], sure it'll strip you of a working script. – Filip Dupanović Feb 02 '09 at 23:38
  • I see where you're coming from. No, Virtuosi is going the other way. Users type /contact and he sees index.php?page=contact. He wrote it as if rewrite rule changes the URL FOR the user, not FROM the user. – Grant Feb 02 '09 at 23:38
  • In other words, he originally though that the url gets changed at his end and is sent to the user. Not the actual way (which is the user type a url that would not work, and rewrite changes it to make it work). – Grant Feb 02 '09 at 23:39
  • He hasn't thought anything, he stated he depends on $_GET['p'] to serve pages, and no you don't depend on $_GET when you strip it with a mod_rewrite rule. – Filip Dupanović Feb 02 '09 at 23:47
  • Where is he stripping it off? Show me in his example mod_rewrite rule where he's striping it off. – Grant Feb 02 '09 at 23:49
  • 'I have a URL like http://example.com/index.php?p=contact' ... 'If I use a rewrite rule that converts the URL to http://example.com/contact'. Where do YOU see a $_GET var from the incoming 'http://example.com/contact'?????? – Filip Dupanović Feb 02 '09 at 23:58
  • Holy crapmonkeys. No. He is converting it in the other direction. See my comment (#5 and #6) on this answer. He THOUGHT it worked the other way but he NOW understands that it doesn't work that way. EITHER WAY he WANTS his script to internally see example.com/index.php?stuff=stuff – Grant Feb 03 '09 at 00:01
  • So, basically, the question, as it's now written, has nothing to do with his actual inquiry? – Filip Dupanović Feb 03 '09 at 00:13
  • I wasn't clear exactly how everything works, so I asked a question for clarification. Though I perhaps didn't word my question correctly, I did the best I could with what I understood. – VirtuosiMedia Feb 03 '09 at 00:45
  • As Grant said, I want my script to see example.com/index.php?stuff=stuff regardless of whether the user sees that url or a clean one. I wanted to verify that I could still depend on the script seeing it. Where I think I got confused was which way the conversion went, which Grant helped with. – VirtuosiMedia Feb 03 '09 at 00:46